diff options
author | David Ahern <dsahern@gmail.com> | 2012-02-08 11:32:52 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2012-02-13 19:54:11 -0500 |
commit | b52956c961be3a04182ae7b776623531601e0fb7 (patch) | |
tree | 2f7ebf4a910dc8cd9014ac9df59f7e2441a5b034 /tools/perf/util | |
parent | eca1c3e3f937307331fd1fd5ee5205e57f2131ca (diff) |
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>
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/evlist.c | 10 | ||||
-rw-r--r-- | tools/perf/util/evlist.h | 4 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 2 | ||||
-rw-r--r-- | tools/perf/util/python-ext-sources | 2 | ||||
-rw-r--r-- | tools/perf/util/thread_map.c | 128 | ||||
-rw-r--r-- | tools/perf/util/thread_map.h | 4 | ||||
-rw-r--r-- | tools/perf/util/top.c | 10 | ||||
-rw-r--r-- | tools/perf/util/top.h | 2 | ||||
-rw-r--r-- | tools/perf/util/usage.c | 6 | ||||
-rw-r--r-- | tools/perf/util/util.h | 2 |
10 files changed, 152 insertions, 18 deletions
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index a57a8cfc5d9..5c61dc57d7c 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -593,15 +593,15 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
593 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 593 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
594 | } | 594 | } |
595 | 595 | ||
596 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 596 | int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, |
597 | pid_t target_tid, uid_t uid, const char *cpu_list) | 597 | const char *target_tid, uid_t uid, const char *cpu_list) |
598 | { | 598 | { |
599 | evlist->threads = thread_map__new(target_pid, target_tid, uid); | 599 | evlist->threads = thread_map__new_str(target_pid, target_tid, uid); |
600 | 600 | ||
601 | if (evlist->threads == NULL) | 601 | if (evlist->threads == NULL) |
602 | return -1; | 602 | return -1; |
603 | 603 | ||
604 | if (uid != UINT_MAX || (cpu_list == NULL && target_tid != -1)) | 604 | if (uid != UINT_MAX || (cpu_list == NULL && target_tid)) |
605 | evlist->cpus = cpu_map__dummy_new(); | 605 | evlist->cpus = cpu_map__dummy_new(); |
606 | else | 606 | else |
607 | evlist->cpus = cpu_map__new(cpu_list); | 607 | evlist->cpus = cpu_map__new(cpu_list); |
@@ -820,7 +820,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
820 | exit(-1); | 820 | exit(-1); |
821 | } | 821 | } |
822 | 822 | ||
823 | if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1) | 823 | if (!opts->system_wide && !opts->target_tid && !opts->target_pid) |
824 | evlist->threads->map[0] = evlist->workload.pid; | 824 | evlist->threads->map[0] = evlist->workload.pid; |
825 | 825 | ||
826 | close(child_ready_pipe[1]); | 826 | close(child_ready_pipe[1]); |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 1b4282be8fe..21f1c9e57f1 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
106 | evlist->threads = threads; | 106 | evlist->threads = threads; |
107 | } | 107 | } |
108 | 108 | ||
109 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 109 | int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, |
110 | pid_t tid, uid_t uid, const char *cpu_list); | 110 | const char *tid, uid_t uid, const char *cpu_list); |
111 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 111 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
112 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 112 | int perf_evlist__set_filters(struct perf_evlist *evlist); |
113 | 113 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9a11f9edac1..f910f50136d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -130,7 +130,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
130 | attr->mmap = track; | 130 | attr->mmap = track; |
131 | attr->comm = track; | 131 | attr->comm = track; |
132 | 132 | ||
133 | if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) { | 133 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide) { |
134 | attr->disabled = 1; | 134 | attr->disabled = 1; |
135 | attr->enable_on_exec = 1; | 135 | attr->enable_on_exec = 1; |
136 | } | 136 | } |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index ff606f482a7..2884e67ee62 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -15,3 +15,5 @@ util/util.c | |||
15 | util/xyarray.c | 15 | util/xyarray.c |
16 | util/cgroup.c | 16 | util/cgroup.c |
17 | util/debugfs.c | 17 | util/debugfs.c |
18 | util/strlist.c | ||
19 | ../../lib/rbtree.c | ||
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 3d4b6c5931b..e15983cf077 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
@@ -6,6 +6,8 @@ | |||
6 | #include <sys/types.h> | 6 | #include <sys/types.h> |
7 | #include <sys/stat.h> | 7 | #include <sys/stat.h> |
8 | #include <unistd.h> | 8 | #include <unistd.h> |
9 | #include "strlist.h" | ||
10 | #include <string.h> | ||
9 | #include "thread_map.h" | 11 | #include "thread_map.h" |
10 | 12 | ||
11 | /* Skip "." and ".." directories */ | 13 | /* Skip "." and ".." directories */ |
@@ -152,6 +154,132 @@ struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) | |||
152 | return thread_map__new_by_tid(tid); | 154 | return thread_map__new_by_tid(tid); |
153 | } | 155 | } |
154 | 156 | ||
157 | static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) | ||
158 | { | ||
159 | struct thread_map *threads = NULL, *nt; | ||
160 | char name[256]; | ||
161 | int items, total_tasks = 0; | ||
162 | struct dirent **namelist = NULL; | ||
163 | int i, j = 0; | ||
164 | pid_t pid, prev_pid = INT_MAX; | ||
165 | char *end_ptr; | ||
166 | struct str_node *pos; | ||
167 | struct strlist *slist = strlist__new(false, pid_str); | ||
168 | |||
169 | if (!slist) | ||
170 | return NULL; | ||
171 | |||
172 | strlist__for_each(pos, slist) { | ||
173 | pid = strtol(pos->s, &end_ptr, 10); | ||
174 | |||
175 | if (pid == INT_MIN || pid == INT_MAX || | ||
176 | (*end_ptr != '\0' && *end_ptr != ',')) | ||
177 | goto out_free_threads; | ||
178 | |||
179 | if (pid == prev_pid) | ||
180 | continue; | ||
181 | |||
182 | sprintf(name, "/proc/%d/task", pid); | ||
183 | items = scandir(name, &namelist, filter, NULL); | ||
184 | if (items <= 0) | ||
185 | goto out_free_threads; | ||
186 | |||
187 | total_tasks += items; | ||
188 | nt = realloc(threads, (sizeof(*threads) + | ||
189 | sizeof(pid_t) * total_tasks)); | ||
190 | if (nt == NULL) | ||
191 | goto out_free_threads; | ||
192 | |||
193 | threads = nt; | ||
194 | |||
195 | if (threads) { | ||
196 | for (i = 0; i < items; i++) | ||
197 | threads->map[j++] = atoi(namelist[i]->d_name); | ||
198 | threads->nr = total_tasks; | ||
199 | } | ||
200 | |||
201 | for (i = 0; i < items; i++) | ||
202 | free(namelist[i]); | ||
203 | free(namelist); | ||
204 | |||
205 | if (!threads) | ||
206 | break; | ||
207 | } | ||
208 | |||
209 | out: | ||
210 | strlist__delete(slist); | ||
211 | return threads; | ||
212 | |||
213 | out_free_threads: | ||
214 | free(threads); | ||
215 | threads = NULL; | ||
216 | goto out; | ||
217 | } | ||
218 | |||
219 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | ||
220 | { | ||
221 | struct thread_map *threads = NULL, *nt; | ||
222 | int ntasks = 0; | ||
223 | pid_t tid, prev_tid = INT_MAX; | ||
224 | char *end_ptr; | ||
225 | struct str_node *pos; | ||
226 | struct strlist *slist; | ||
227 | |||
228 | /* perf-stat expects threads to be generated even if tid not given */ | ||
229 | if (!tid_str) { | ||
230 | threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
231 | if (threads != NULL) { | ||
232 | threads->map[1] = -1; | ||
233 | threads->nr = 1; | ||
234 | } | ||
235 | return threads; | ||
236 | } | ||
237 | |||
238 | slist = strlist__new(false, tid_str); | ||
239 | if (!slist) | ||
240 | return NULL; | ||
241 | |||
242 | strlist__for_each(pos, slist) { | ||
243 | tid = strtol(pos->s, &end_ptr, 10); | ||
244 | |||
245 | if (tid == INT_MIN || tid == INT_MAX || | ||
246 | (*end_ptr != '\0' && *end_ptr != ',')) | ||
247 | goto out_free_threads; | ||
248 | |||
249 | if (tid == prev_tid) | ||
250 | continue; | ||
251 | |||
252 | ntasks++; | ||
253 | nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks); | ||
254 | |||
255 | if (nt == NULL) | ||
256 | goto out_free_threads; | ||
257 | |||
258 | threads = nt; | ||
259 | threads->map[ntasks - 1] = tid; | ||
260 | threads->nr = ntasks; | ||
261 | } | ||
262 | out: | ||
263 | return threads; | ||
264 | |||
265 | out_free_threads: | ||
266 | free(threads); | ||
267 | threads = NULL; | ||
268 | goto out; | ||
269 | } | ||
270 | |||
271 | struct thread_map *thread_map__new_str(const char *pid, const char *tid, | ||
272 | uid_t uid) | ||
273 | { | ||
274 | if (pid) | ||
275 | return thread_map__new_by_pid_str(pid); | ||
276 | |||
277 | if (!tid && uid != UINT_MAX) | ||
278 | return thread_map__new_by_uid(uid); | ||
279 | |||
280 | return thread_map__new_by_tid_str(tid); | ||
281 | } | ||
282 | |||
155 | void thread_map__delete(struct thread_map *threads) | 283 | void thread_map__delete(struct thread_map *threads) |
156 | { | 284 | { |
157 | free(threads); | 285 | free(threads); |
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index c75ddbaba00..7da80f14418 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -13,6 +13,10 @@ struct thread_map *thread_map__new_by_pid(pid_t pid); | |||
13 | struct thread_map *thread_map__new_by_tid(pid_t tid); | 13 | struct thread_map *thread_map__new_by_tid(pid_t tid); |
14 | struct thread_map *thread_map__new_by_uid(uid_t uid); | 14 | struct thread_map *thread_map__new_by_uid(uid_t uid); |
15 | struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); | 15 | struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); |
16 | |||
17 | struct thread_map *thread_map__new_str(const char *pid, | ||
18 | const char *tid, uid_t uid); | ||
19 | |||
16 | void thread_map__delete(struct thread_map *threads); | 20 | void thread_map__delete(struct thread_map *threads); |
17 | 21 | ||
18 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); | 22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index e4370ca2719..09fe579ccaf 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -69,11 +69,11 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
69 | 69 | ||
70 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 70 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
71 | 71 | ||
72 | if (top->target_pid != -1) | 72 | if (top->target_pid) |
73 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d", | 73 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", |
74 | top->target_pid); | 74 | top->target_pid); |
75 | else if (top->target_tid != -1) | 75 | else if (top->target_tid) |
76 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", | 76 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", |
77 | top->target_tid); | 77 | top->target_tid); |
78 | else if (top->uid_str != NULL) | 78 | else if (top->uid_str != NULL) |
79 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", | 79 | 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) | |||
85 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | 85 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", |
86 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); | 86 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); |
87 | else { | 87 | else { |
88 | if (top->target_tid != -1) | 88 | if (top->target_tid) |
89 | ret += SNPRINTF(bf + ret, size - ret, ")"); | 89 | ret += SNPRINTF(bf + ret, size - ret, ")"); |
90 | else | 90 | else |
91 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | 91 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index def3e53e0fe..49eb8481f19 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -23,7 +23,7 @@ struct perf_top { | |||
23 | u64 guest_us_samples, guest_kernel_samples; | 23 | u64 guest_us_samples, guest_kernel_samples; |
24 | int print_entries, count_filter, delay_secs; | 24 | int print_entries, count_filter, delay_secs; |
25 | int freq; | 25 | int freq; |
26 | pid_t target_pid, target_tid; | 26 | const char *target_pid, *target_tid; |
27 | uid_t uid; | 27 | uid_t uid; |
28 | bool hide_kernel_symbols, hide_user_symbols, zero; | 28 | bool hide_kernel_symbols, hide_user_symbols, zero; |
29 | bool system_wide; | 29 | bool system_wide; |
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index d0c013934f3..52bb07c6442 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c | |||
@@ -83,7 +83,7 @@ void warning(const char *warn, ...) | |||
83 | va_end(params); | 83 | va_end(params); |
84 | } | 84 | } |
85 | 85 | ||
86 | uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid) | 86 | uid_t parse_target_uid(const char *str, const char *tid, const char *pid) |
87 | { | 87 | { |
88 | struct passwd pwd, *result; | 88 | struct passwd pwd, *result; |
89 | char buf[1024]; | 89 | char buf[1024]; |
@@ -91,8 +91,8 @@ uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid) | |||
91 | if (str == NULL) | 91 | if (str == NULL) |
92 | return UINT_MAX; | 92 | return UINT_MAX; |
93 | 93 | ||
94 | /* CPU and PID are mutually exclusive */ | 94 | /* UID and PID are mutually exclusive */ |
95 | if (tid > 0 || pid > 0) { | 95 | if (tid || pid) { |
96 | ui__warning("PID/TID switch overriding UID\n"); | 96 | ui__warning("PID/TID switch overriding UID\n"); |
97 | sleep(1); | 97 | sleep(1); |
98 | return UINT_MAX; | 98 | return UINT_MAX; |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 232d17ef3e6..7917b09430b 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -245,7 +245,7 @@ struct perf_event_attr; | |||
245 | 245 | ||
246 | void event_attr_init(struct perf_event_attr *attr); | 246 | void event_attr_init(struct perf_event_attr *attr); |
247 | 247 | ||
248 | uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid); | 248 | uid_t parse_target_uid(const char *str, const char *tid, const char *pid); |
249 | 249 | ||
250 | #define _STR(x) #x | 250 | #define _STR(x) #x |
251 | #define STR(x) _STR(x) | 251 | #define STR(x) _STR(x) |