perf tools: Allow multiple threads or processes in record, stat, top
Allow a user to collect events for multiple threads or processes using a comma separated list. e.g., collect data on a VM and its vhost thread: perf top -p 21483,21485 perf stat -p 21483,21485 -ddd perf record -p 21483,21485 or monitoring vcpu threads perf top -t 21488,21489 perf stat -t 21488,21489 -ddd perf record -t 21488,21489 Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/1328718772-16688-1-git-send-email-dsahern@gmail.com Signed-off-by: David Ahern <dsahern@gmail.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
eca1c3e3f9
commit
b52956c961
@ -52,11 +52,11 @@ OPTIONS
|
|||||||
|
|
||||||
-p::
|
-p::
|
||||||
--pid=::
|
--pid=::
|
||||||
Record events on existing process ID.
|
Record events on existing process ID (comma separated list).
|
||||||
|
|
||||||
-t::
|
-t::
|
||||||
--tid=::
|
--tid=::
|
||||||
Record events on existing thread ID.
|
Record events on existing thread ID (comma separated list).
|
||||||
|
|
||||||
-u::
|
-u::
|
||||||
--uid=::
|
--uid=::
|
||||||
|
@ -35,11 +35,11 @@ OPTIONS
|
|||||||
child tasks do not inherit counters
|
child tasks do not inherit counters
|
||||||
-p::
|
-p::
|
||||||
--pid=<pid>::
|
--pid=<pid>::
|
||||||
stat events on existing process id
|
stat events on existing process id (comma separated list)
|
||||||
|
|
||||||
-t::
|
-t::
|
||||||
--tid=<tid>::
|
--tid=<tid>::
|
||||||
stat events on existing thread id
|
stat events on existing thread id (comma separated list)
|
||||||
|
|
||||||
|
|
||||||
-a::
|
-a::
|
||||||
|
@ -72,11 +72,11 @@ Default is to monitor all CPUS.
|
|||||||
|
|
||||||
-p <pid>::
|
-p <pid>::
|
||||||
--pid=<pid>::
|
--pid=<pid>::
|
||||||
Profile events on existing Process ID.
|
Profile events on existing Process ID (comma separated list).
|
||||||
|
|
||||||
-t <tid>::
|
-t <tid>::
|
||||||
--tid=<tid>::
|
--tid=<tid>::
|
||||||
Profile events on existing thread ID.
|
Profile events on existing thread ID (comma separated list).
|
||||||
|
|
||||||
-u::
|
-u::
|
||||||
--uid=::
|
--uid=::
|
||||||
|
@ -645,8 +645,6 @@ static const char * const record_usage[] = {
|
|||||||
*/
|
*/
|
||||||
static struct perf_record record = {
|
static struct perf_record record = {
|
||||||
.opts = {
|
.opts = {
|
||||||
.target_pid = -1,
|
|
||||||
.target_tid = -1,
|
|
||||||
.mmap_pages = UINT_MAX,
|
.mmap_pages = UINT_MAX,
|
||||||
.user_freq = UINT_MAX,
|
.user_freq = UINT_MAX,
|
||||||
.user_interval = ULLONG_MAX,
|
.user_interval = ULLONG_MAX,
|
||||||
@ -670,9 +668,9 @@ const struct option record_options[] = {
|
|||||||
parse_events_option),
|
parse_events_option),
|
||||||
OPT_CALLBACK(0, "filter", &record.evlist, "filter",
|
OPT_CALLBACK(0, "filter", &record.evlist, "filter",
|
||||||
"event filter", parse_filter),
|
"event filter", parse_filter),
|
||||||
OPT_INTEGER('p', "pid", &record.opts.target_pid,
|
OPT_STRING('p', "pid", &record.opts.target_pid, "pid",
|
||||||
"record events on existing process id"),
|
"record events on existing process id"),
|
||||||
OPT_INTEGER('t', "tid", &record.opts.target_tid,
|
OPT_STRING('t', "tid", &record.opts.target_tid, "tid",
|
||||||
"record events on existing thread id"),
|
"record events on existing thread id"),
|
||||||
OPT_INTEGER('r', "realtime", &record.realtime_prio,
|
OPT_INTEGER('r', "realtime", &record.realtime_prio,
|
||||||
"collect data with this RT SCHED_FIFO priority"),
|
"collect data with this RT SCHED_FIFO priority"),
|
||||||
@ -739,7 +737,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
|||||||
|
|
||||||
argc = parse_options(argc, argv, record_options, record_usage,
|
argc = parse_options(argc, argv, record_options, record_usage,
|
||||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||||
if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 &&
|
if (!argc && !rec->opts.target_pid && !rec->opts.target_tid &&
|
||||||
!rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str)
|
!rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str)
|
||||||
usage_with_options(record_usage, record_options);
|
usage_with_options(record_usage, record_options);
|
||||||
|
|
||||||
@ -785,7 +783,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
|||||||
if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1)
|
if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1)
|
||||||
goto out_free_fd;
|
goto out_free_fd;
|
||||||
|
|
||||||
if (rec->opts.target_pid != -1)
|
if (rec->opts.target_pid)
|
||||||
rec->opts.target_tid = rec->opts.target_pid;
|
rec->opts.target_tid = rec->opts.target_pid;
|
||||||
|
|
||||||
if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
|
if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
|
||||||
|
@ -182,8 +182,8 @@ static int run_count = 1;
|
|||||||
static bool no_inherit = false;
|
static bool no_inherit = false;
|
||||||
static bool scale = true;
|
static bool scale = true;
|
||||||
static bool no_aggr = false;
|
static bool no_aggr = false;
|
||||||
static pid_t target_pid = -1;
|
static const char *target_pid;
|
||||||
static pid_t target_tid = -1;
|
static const char *target_tid;
|
||||||
static pid_t child_pid = -1;
|
static pid_t child_pid = -1;
|
||||||
static bool null_run = false;
|
static bool null_run = false;
|
||||||
static int detailed_run = 0;
|
static int detailed_run = 0;
|
||||||
@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel,
|
|||||||
if (system_wide)
|
if (system_wide)
|
||||||
return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
|
return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
|
||||||
group, group_fd);
|
group, group_fd);
|
||||||
if (target_pid == -1 && target_tid == -1) {
|
if (!target_pid && !target_tid) {
|
||||||
attr->disabled = 1;
|
attr->disabled = 1;
|
||||||
attr->enable_on_exec = 1;
|
attr->enable_on_exec = 1;
|
||||||
}
|
}
|
||||||
@ -446,7 +446,7 @@ static int run_perf_stat(int argc __used, const char **argv)
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target_tid == -1 && target_pid == -1 && !system_wide)
|
if (!target_tid && !target_pid && !system_wide)
|
||||||
evsel_list->threads->map[0] = child_pid;
|
evsel_list->threads->map[0] = child_pid;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -968,14 +968,14 @@ static void print_stat(int argc, const char **argv)
|
|||||||
if (!csv_output) {
|
if (!csv_output) {
|
||||||
fprintf(output, "\n");
|
fprintf(output, "\n");
|
||||||
fprintf(output, " Performance counter stats for ");
|
fprintf(output, " Performance counter stats for ");
|
||||||
if(target_pid == -1 && target_tid == -1) {
|
if (!target_pid && !target_tid) {
|
||||||
fprintf(output, "\'%s", argv[0]);
|
fprintf(output, "\'%s", argv[0]);
|
||||||
for (i = 1; i < argc; i++)
|
for (i = 1; i < argc; i++)
|
||||||
fprintf(output, " %s", argv[i]);
|
fprintf(output, " %s", argv[i]);
|
||||||
} else if (target_pid != -1)
|
} else if (target_pid)
|
||||||
fprintf(output, "process id \'%d", target_pid);
|
fprintf(output, "process id \'%s", target_pid);
|
||||||
else
|
else
|
||||||
fprintf(output, "thread id \'%d", target_tid);
|
fprintf(output, "thread id \'%s", target_tid);
|
||||||
|
|
||||||
fprintf(output, "\'");
|
fprintf(output, "\'");
|
||||||
if (run_count > 1)
|
if (run_count > 1)
|
||||||
@ -1049,10 +1049,10 @@ static const struct option options[] = {
|
|||||||
"event filter", parse_filter),
|
"event filter", parse_filter),
|
||||||
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
|
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
|
||||||
"child tasks do not inherit counters"),
|
"child tasks do not inherit counters"),
|
||||||
OPT_INTEGER('p', "pid", &target_pid,
|
OPT_STRING('p', "pid", &target_pid, "pid",
|
||||||
"stat events on existing process id"),
|
"stat events on existing process id"),
|
||||||
OPT_INTEGER('t', "tid", &target_tid,
|
OPT_STRING('t', "tid", &target_tid, "tid",
|
||||||
"stat events on existing thread id"),
|
"stat events on existing thread id"),
|
||||||
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
||||||
"system-wide collection from all CPUs"),
|
"system-wide collection from all CPUs"),
|
||||||
OPT_BOOLEAN('g', "group", &group,
|
OPT_BOOLEAN('g', "group", &group,
|
||||||
@ -1190,7 +1190,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
|
|||||||
} else if (big_num_opt == 0) /* User passed --no-big-num */
|
} else if (big_num_opt == 0) /* User passed --no-big-num */
|
||||||
big_num = false;
|
big_num = false;
|
||||||
|
|
||||||
if (!argc && target_pid == -1 && target_tid == -1)
|
if (!argc && !target_pid && !target_tid)
|
||||||
usage_with_options(stat_usage, options);
|
usage_with_options(stat_usage, options);
|
||||||
if (run_count <= 0)
|
if (run_count <= 0)
|
||||||
usage_with_options(stat_usage, options);
|
usage_with_options(stat_usage, options);
|
||||||
@ -1206,10 +1206,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
|
|||||||
if (add_default_attributes())
|
if (add_default_attributes())
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (target_pid != -1)
|
if (target_pid)
|
||||||
target_tid = target_pid;
|
target_tid = target_pid;
|
||||||
|
|
||||||
evsel_list->threads = thread_map__new(target_pid, target_tid, UINT_MAX);
|
evsel_list->threads = thread_map__new_str(target_pid,
|
||||||
|
target_tid, UINT_MAX);
|
||||||
if (evsel_list->threads == NULL) {
|
if (evsel_list->threads == NULL) {
|
||||||
pr_err("Problems finding threads of monitor\n");
|
pr_err("Problems finding threads of monitor\n");
|
||||||
usage_with_options(stat_usage, options);
|
usage_with_options(stat_usage, options);
|
||||||
|
@ -1010,8 +1010,6 @@ realloc:
|
|||||||
static int test__PERF_RECORD(void)
|
static int test__PERF_RECORD(void)
|
||||||
{
|
{
|
||||||
struct perf_record_opts opts = {
|
struct perf_record_opts opts = {
|
||||||
.target_pid = -1,
|
|
||||||
.target_tid = -1,
|
|
||||||
.no_delay = true,
|
.no_delay = true,
|
||||||
.freq = 10,
|
.freq = 10,
|
||||||
.mmap_pages = 256,
|
.mmap_pages = 256,
|
||||||
|
@ -965,7 +965,7 @@ static int __cmd_top(struct perf_top *top)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out_delete;
|
goto out_delete;
|
||||||
|
|
||||||
if (top->target_tid != -1 || top->uid != UINT_MAX)
|
if (top->target_tid || top->uid != UINT_MAX)
|
||||||
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
|
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
|
||||||
perf_event__process,
|
perf_event__process,
|
||||||
&top->session->host_machine);
|
&top->session->host_machine);
|
||||||
@ -1103,8 +1103,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
|||||||
struct perf_top top = {
|
struct perf_top top = {
|
||||||
.count_filter = 5,
|
.count_filter = 5,
|
||||||
.delay_secs = 2,
|
.delay_secs = 2,
|
||||||
.target_pid = -1,
|
|
||||||
.target_tid = -1,
|
|
||||||
.uid = UINT_MAX,
|
.uid = UINT_MAX,
|
||||||
.freq = 1000, /* 1 KHz */
|
.freq = 1000, /* 1 KHz */
|
||||||
.sample_id_all_avail = true,
|
.sample_id_all_avail = true,
|
||||||
@ -1118,9 +1116,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
|||||||
parse_events_option),
|
parse_events_option),
|
||||||
OPT_INTEGER('c', "count", &top.default_interval,
|
OPT_INTEGER('c', "count", &top.default_interval,
|
||||||
"event period to sample"),
|
"event period to sample"),
|
||||||
OPT_INTEGER('p', "pid", &top.target_pid,
|
OPT_STRING('p', "pid", &top.target_pid, "pid",
|
||||||
"profile events on existing process id"),
|
"profile events on existing process id"),
|
||||||
OPT_INTEGER('t', "tid", &top.target_tid,
|
OPT_STRING('t', "tid", &top.target_tid, "tid",
|
||||||
"profile events on existing thread id"),
|
"profile events on existing thread id"),
|
||||||
OPT_BOOLEAN('a', "all-cpus", &top.system_wide,
|
OPT_BOOLEAN('a', "all-cpus", &top.system_wide,
|
||||||
"system-wide collection from all CPUs"),
|
"system-wide collection from all CPUs"),
|
||||||
@ -1210,13 +1208,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
|
|||||||
goto out_delete_evlist;
|
goto out_delete_evlist;
|
||||||
|
|
||||||
/* CPU and PID are mutually exclusive */
|
/* CPU and PID are mutually exclusive */
|
||||||
if (top.target_tid > 0 && top.cpu_list) {
|
if (top.target_tid && top.cpu_list) {
|
||||||
printf("WARNING: PID switch overriding CPU\n");
|
printf("WARNING: PID switch overriding CPU\n");
|
||||||
sleep(1);
|
sleep(1);
|
||||||
top.cpu_list = NULL;
|
top.cpu_list = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top.target_pid != -1)
|
if (top.target_pid)
|
||||||
top.target_tid = top.target_pid;
|
top.target_tid = top.target_pid;
|
||||||
|
|
||||||
if (perf_evlist__create_maps(top.evlist, top.target_pid,
|
if (perf_evlist__create_maps(top.evlist, top.target_pid,
|
||||||
|
@ -186,8 +186,8 @@ extern const char perf_version_string[];
|
|||||||
void pthread__unblock_sigwinch(void);
|
void pthread__unblock_sigwinch(void);
|
||||||
|
|
||||||
struct perf_record_opts {
|
struct perf_record_opts {
|
||||||
pid_t target_pid;
|
const char *target_pid;
|
||||||
pid_t target_tid;
|
const char *target_tid;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
bool call_graph;
|
bool call_graph;
|
||||||
bool group;
|
bool group;
|
||||||
|
@ -593,15 +593,15 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
|||||||
return perf_evlist__mmap_per_cpu(evlist, prot, mask);
|
return perf_evlist__mmap_per_cpu(evlist, prot, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
|
int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
|
||||||
pid_t target_tid, uid_t uid, const char *cpu_list)
|
const char *target_tid, uid_t uid, const char *cpu_list)
|
||||||
{
|
{
|
||||||
evlist->threads = thread_map__new(target_pid, target_tid, uid);
|
evlist->threads = thread_map__new_str(target_pid, target_tid, uid);
|
||||||
|
|
||||||
if (evlist->threads == NULL)
|
if (evlist->threads == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (uid != UINT_MAX || (cpu_list == NULL && target_tid != -1))
|
if (uid != UINT_MAX || (cpu_list == NULL && target_tid))
|
||||||
evlist->cpus = cpu_map__dummy_new();
|
evlist->cpus = cpu_map__dummy_new();
|
||||||
else
|
else
|
||||||
evlist->cpus = cpu_map__new(cpu_list);
|
evlist->cpus = cpu_map__new(cpu_list);
|
||||||
@ -820,7 +820,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1)
|
if (!opts->system_wide && !opts->target_tid && !opts->target_pid)
|
||||||
evlist->threads->map[0] = evlist->workload.pid;
|
evlist->threads->map[0] = evlist->workload.pid;
|
||||||
|
|
||||||
close(child_ready_pipe[1]);
|
close(child_ready_pipe[1]);
|
||||||
|
@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
|
|||||||
evlist->threads = threads;
|
evlist->threads = threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
|
int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
|
||||||
pid_t tid, uid_t uid, const char *cpu_list);
|
const char *tid, uid_t uid, const char *cpu_list);
|
||||||
void perf_evlist__delete_maps(struct perf_evlist *evlist);
|
void perf_evlist__delete_maps(struct perf_evlist *evlist);
|
||||||
int perf_evlist__set_filters(struct perf_evlist *evlist);
|
int perf_evlist__set_filters(struct perf_evlist *evlist);
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
|
|||||||
attr->mmap = track;
|
attr->mmap = track;
|
||||||
attr->comm = track;
|
attr->comm = track;
|
||||||
|
|
||||||
if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) {
|
if (!opts->target_pid && !opts->target_tid && !opts->system_wide) {
|
||||||
attr->disabled = 1;
|
attr->disabled = 1;
|
||||||
attr->enable_on_exec = 1;
|
attr->enable_on_exec = 1;
|
||||||
}
|
}
|
||||||
|
@ -15,3 +15,5 @@ util/util.c
|
|||||||
util/xyarray.c
|
util/xyarray.c
|
||||||
util/cgroup.c
|
util/cgroup.c
|
||||||
util/debugfs.c
|
util/debugfs.c
|
||||||
|
util/strlist.c
|
||||||
|
../../lib/rbtree.c
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include "strlist.h"
|
||||||
|
#include <string.h>
|
||||||
#include "thread_map.h"
|
#include "thread_map.h"
|
||||||
|
|
||||||
/* Skip "." and ".." directories */
|
/* Skip "." and ".." directories */
|
||||||
@ -152,6 +154,132 @@ struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
|
|||||||
return thread_map__new_by_tid(tid);
|
return thread_map__new_by_tid(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
|
||||||
|
{
|
||||||
|
struct thread_map *threads = NULL, *nt;
|
||||||
|
char name[256];
|
||||||
|
int items, total_tasks = 0;
|
||||||
|
struct dirent **namelist = NULL;
|
||||||
|
int i, j = 0;
|
||||||
|
pid_t pid, prev_pid = INT_MAX;
|
||||||
|
char *end_ptr;
|
||||||
|
struct str_node *pos;
|
||||||
|
struct strlist *slist = strlist__new(false, pid_str);
|
||||||
|
|
||||||
|
if (!slist)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
strlist__for_each(pos, slist) {
|
||||||
|
pid = strtol(pos->s, &end_ptr, 10);
|
||||||
|
|
||||||
|
if (pid == INT_MIN || pid == INT_MAX ||
|
||||||
|
(*end_ptr != '\0' && *end_ptr != ','))
|
||||||
|
goto out_free_threads;
|
||||||
|
|
||||||
|
if (pid == prev_pid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sprintf(name, "/proc/%d/task", pid);
|
||||||
|
items = scandir(name, &namelist, filter, NULL);
|
||||||
|
if (items <= 0)
|
||||||
|
goto out_free_threads;
|
||||||
|
|
||||||
|
total_tasks += items;
|
||||||
|
nt = realloc(threads, (sizeof(*threads) +
|
||||||
|
sizeof(pid_t) * total_tasks));
|
||||||
|
if (nt == NULL)
|
||||||
|
goto out_free_threads;
|
||||||
|
|
||||||
|
threads = nt;
|
||||||
|
|
||||||
|
if (threads) {
|
||||||
|
for (i = 0; i < items; i++)
|
||||||
|
threads->map[j++] = atoi(namelist[i]->d_name);
|
||||||
|
threads->nr = total_tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < items; i++)
|
||||||
|
free(namelist[i]);
|
||||||
|
free(namelist);
|
||||||
|
|
||||||
|
if (!threads)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
strlist__delete(slist);
|
||||||
|
return threads;
|
||||||
|
|
||||||
|
out_free_threads:
|
||||||
|
free(threads);
|
||||||
|
threads = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
|
||||||
|
{
|
||||||
|
struct thread_map *threads = NULL, *nt;
|
||||||
|
int ntasks = 0;
|
||||||
|
pid_t tid, prev_tid = INT_MAX;
|
||||||
|
char *end_ptr;
|
||||||
|
struct str_node *pos;
|
||||||
|
struct strlist *slist;
|
||||||
|
|
||||||
|
/* perf-stat expects threads to be generated even if tid not given */
|
||||||
|
if (!tid_str) {
|
||||||
|
threads = malloc(sizeof(*threads) + sizeof(pid_t));
|
||||||
|
if (threads != NULL) {
|
||||||
|
threads->map[1] = -1;
|
||||||
|
threads->nr = 1;
|
||||||
|
}
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
slist = strlist__new(false, tid_str);
|
||||||
|
if (!slist)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
strlist__for_each(pos, slist) {
|
||||||
|
tid = strtol(pos->s, &end_ptr, 10);
|
||||||
|
|
||||||
|
if (tid == INT_MIN || tid == INT_MAX ||
|
||||||
|
(*end_ptr != '\0' && *end_ptr != ','))
|
||||||
|
goto out_free_threads;
|
||||||
|
|
||||||
|
if (tid == prev_tid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ntasks++;
|
||||||
|
nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
|
||||||
|
|
||||||
|
if (nt == NULL)
|
||||||
|
goto out_free_threads;
|
||||||
|
|
||||||
|
threads = nt;
|
||||||
|
threads->map[ntasks - 1] = tid;
|
||||||
|
threads->nr = ntasks;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return threads;
|
||||||
|
|
||||||
|
out_free_threads:
|
||||||
|
free(threads);
|
||||||
|
threads = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread_map *thread_map__new_str(const char *pid, const char *tid,
|
||||||
|
uid_t uid)
|
||||||
|
{
|
||||||
|
if (pid)
|
||||||
|
return thread_map__new_by_pid_str(pid);
|
||||||
|
|
||||||
|
if (!tid && uid != UINT_MAX)
|
||||||
|
return thread_map__new_by_uid(uid);
|
||||||
|
|
||||||
|
return thread_map__new_by_tid_str(tid);
|
||||||
|
}
|
||||||
|
|
||||||
void thread_map__delete(struct thread_map *threads)
|
void thread_map__delete(struct thread_map *threads)
|
||||||
{
|
{
|
||||||
free(threads);
|
free(threads);
|
||||||
|
@ -13,6 +13,10 @@ struct thread_map *thread_map__new_by_pid(pid_t pid);
|
|||||||
struct thread_map *thread_map__new_by_tid(pid_t tid);
|
struct thread_map *thread_map__new_by_tid(pid_t tid);
|
||||||
struct thread_map *thread_map__new_by_uid(uid_t uid);
|
struct thread_map *thread_map__new_by_uid(uid_t uid);
|
||||||
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
|
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
|
||||||
|
|
||||||
|
struct thread_map *thread_map__new_str(const char *pid,
|
||||||
|
const char *tid, uid_t uid);
|
||||||
|
|
||||||
void thread_map__delete(struct thread_map *threads);
|
void thread_map__delete(struct thread_map *threads);
|
||||||
|
|
||||||
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
|
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
|
||||||
|
@ -69,11 +69,11 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
|
|||||||
|
|
||||||
ret += SNPRINTF(bf + ret, size - ret, "], ");
|
ret += SNPRINTF(bf + ret, size - ret, "], ");
|
||||||
|
|
||||||
if (top->target_pid != -1)
|
if (top->target_pid)
|
||||||
ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d",
|
ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s",
|
||||||
top->target_pid);
|
top->target_pid);
|
||||||
else if (top->target_tid != -1)
|
else if (top->target_tid)
|
||||||
ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d",
|
ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s",
|
||||||
top->target_tid);
|
top->target_tid);
|
||||||
else if (top->uid_str != NULL)
|
else if (top->uid_str != NULL)
|
||||||
ret += SNPRINTF(bf + ret, size - ret, " (uid: %s",
|
ret += SNPRINTF(bf + ret, size - ret, " (uid: %s",
|
||||||
@ -85,7 +85,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
|
|||||||
ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)",
|
ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)",
|
||||||
top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list);
|
top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list);
|
||||||
else {
|
else {
|
||||||
if (top->target_tid != -1)
|
if (top->target_tid)
|
||||||
ret += SNPRINTF(bf + ret, size - ret, ")");
|
ret += SNPRINTF(bf + ret, size - ret, ")");
|
||||||
else
|
else
|
||||||
ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)",
|
ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)",
|
||||||
|
@ -23,7 +23,7 @@ struct perf_top {
|
|||||||
u64 guest_us_samples, guest_kernel_samples;
|
u64 guest_us_samples, guest_kernel_samples;
|
||||||
int print_entries, count_filter, delay_secs;
|
int print_entries, count_filter, delay_secs;
|
||||||
int freq;
|
int freq;
|
||||||
pid_t target_pid, target_tid;
|
const char *target_pid, *target_tid;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
bool hide_kernel_symbols, hide_user_symbols, zero;
|
bool hide_kernel_symbols, hide_user_symbols, zero;
|
||||||
bool system_wide;
|
bool system_wide;
|
||||||
|
@ -83,7 +83,7 @@ void warning(const char *warn, ...)
|
|||||||
va_end(params);
|
va_end(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid)
|
uid_t parse_target_uid(const char *str, const char *tid, const char *pid)
|
||||||
{
|
{
|
||||||
struct passwd pwd, *result;
|
struct passwd pwd, *result;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
@ -91,8 +91,8 @@ uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid)
|
|||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return UINT_MAX;
|
return UINT_MAX;
|
||||||
|
|
||||||
/* CPU and PID are mutually exclusive */
|
/* UID and PID are mutually exclusive */
|
||||||
if (tid > 0 || pid > 0) {
|
if (tid || pid) {
|
||||||
ui__warning("PID/TID switch overriding UID\n");
|
ui__warning("PID/TID switch overriding UID\n");
|
||||||
sleep(1);
|
sleep(1);
|
||||||
return UINT_MAX;
|
return UINT_MAX;
|
||||||
|
@ -245,7 +245,7 @@ struct perf_event_attr;
|
|||||||
|
|
||||||
void event_attr_init(struct perf_event_attr *attr);
|
void event_attr_init(struct perf_event_attr *attr);
|
||||||
|
|
||||||
uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid);
|
uid_t parse_target_uid(const char *str, const char *tid, const char *pid);
|
||||||
|
|
||||||
#define _STR(x) #x
|
#define _STR(x) #x
|
||||||
#define STR(x) _STR(x)
|
#define STR(x) _STR(x)
|
||||||
|
Loading…
Reference in New Issue
Block a user