diff options
author | Ingo Molnar <mingo@kernel.org> | 2014-03-18 04:23:09 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-03-18 04:23:09 -0400 |
commit | 0afd2d51029961281572d02545c7bde1b3f4292c (patch) | |
tree | 73f8b07ee0b43ebd93fb0556b0af0f217f897d5c /tools/perf/util | |
parent | 81827ed8d85e892311965dc9ec4120b2b2e745bd (diff) | |
parent | d75e6097ef1f7669deb500fbbdf53cfe524f1b53 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo:
User visible:
* Add several futex 'perf bench' microbenchmarks (Davidlohr Bueso)
* Speed up thread map generation (Don Zickus)
* Fix synthesizing mmaps for threads (Don Zickus)
* Fix invalid output on event group stdio report (Namhyung Kim)
* Introduce 'perf kvm --list-cmds' command line option for use by
scripts (Ramkumar Ramachandra)
Documentation:
* Clarify load-latency information in the 'perf mem' docs (Andi Kleen)
* Clarify x86 register naming in 'perf probe' docs (Andi Kleen)
Refactorings:
* hists browser refactorings to reuse code accross UIs (Namhyung Kim)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf/util')
-rw-r--r-- | tools/perf/util/event.c | 156 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 20 | ||||
-rw-r--r-- | tools/perf/util/machine.c | 46 | ||||
-rw-r--r-- | tools/perf/util/machine.h | 3 | ||||
-rw-r--r-- | tools/perf/util/parse-options.c | 37 | ||||
-rw-r--r-- | tools/perf/util/parse-options.h | 8 | ||||
-rw-r--r-- | tools/perf/util/session.c | 2 | ||||
-rw-r--r-- | tools/perf/util/thread.c | 21 | ||||
-rw-r--r-- | tools/perf/util/thread.h | 5 |
9 files changed, 204 insertions, 94 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index b0f3ca850e9e..3e580be0f6fb 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -94,14 +94,10 @@ static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len) | |||
94 | 94 | ||
95 | static pid_t perf_event__synthesize_comm(struct perf_tool *tool, | 95 | static pid_t perf_event__synthesize_comm(struct perf_tool *tool, |
96 | union perf_event *event, pid_t pid, | 96 | union perf_event *event, pid_t pid, |
97 | int full, | ||
98 | perf_event__handler_t process, | 97 | perf_event__handler_t process, |
99 | struct machine *machine) | 98 | struct machine *machine) |
100 | { | 99 | { |
101 | char filename[PATH_MAX]; | ||
102 | size_t size; | 100 | size_t size; |
103 | DIR *tasks; | ||
104 | struct dirent dirent, *next; | ||
105 | pid_t tgid; | 101 | pid_t tgid; |
106 | 102 | ||
107 | memset(&event->comm, 0, sizeof(event->comm)); | 103 | memset(&event->comm, 0, sizeof(event->comm)); |
@@ -124,55 +120,35 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, | |||
124 | event->comm.header.size = (sizeof(event->comm) - | 120 | event->comm.header.size = (sizeof(event->comm) - |
125 | (sizeof(event->comm.comm) - size) + | 121 | (sizeof(event->comm.comm) - size) + |
126 | machine->id_hdr_size); | 122 | machine->id_hdr_size); |
127 | if (!full) { | 123 | event->comm.tid = pid; |
128 | event->comm.tid = pid; | ||
129 | |||
130 | if (process(tool, event, &synth_sample, machine) != 0) | ||
131 | return -1; | ||
132 | 124 | ||
133 | goto out; | 125 | if (process(tool, event, &synth_sample, machine) != 0) |
134 | } | 126 | return -1; |
135 | |||
136 | if (machine__is_default_guest(machine)) | ||
137 | return 0; | ||
138 | |||
139 | snprintf(filename, sizeof(filename), "%s/proc/%d/task", | ||
140 | machine->root_dir, pid); | ||
141 | |||
142 | tasks = opendir(filename); | ||
143 | if (tasks == NULL) { | ||
144 | pr_debug("couldn't open %s\n", filename); | ||
145 | return 0; | ||
146 | } | ||
147 | 127 | ||
148 | while (!readdir_r(tasks, &dirent, &next) && next) { | 128 | out: |
149 | char *end; | 129 | return tgid; |
150 | pid = strtol(dirent.d_name, &end, 10); | 130 | } |
151 | if (*end) | ||
152 | continue; | ||
153 | 131 | ||
154 | /* already have tgid; jut want to update the comm */ | 132 | static int perf_event__synthesize_fork(struct perf_tool *tool, |
155 | (void) perf_event__get_comm_tgid(pid, event->comm.comm, | 133 | union perf_event *event, pid_t pid, |
156 | sizeof(event->comm.comm)); | 134 | pid_t tgid, perf_event__handler_t process, |
135 | struct machine *machine) | ||
136 | { | ||
137 | memset(&event->fork, 0, sizeof(event->fork) + machine->id_hdr_size); | ||
157 | 138 | ||
158 | size = strlen(event->comm.comm) + 1; | 139 | /* this is really a clone event but we use fork to synthesize it */ |
159 | size = PERF_ALIGN(size, sizeof(u64)); | 140 | event->fork.ppid = tgid; |
160 | memset(event->comm.comm + size, 0, machine->id_hdr_size); | 141 | event->fork.ptid = tgid; |
161 | event->comm.header.size = (sizeof(event->comm) - | 142 | event->fork.pid = tgid; |
162 | (sizeof(event->comm.comm) - size) + | 143 | event->fork.tid = pid; |
163 | machine->id_hdr_size); | 144 | event->fork.header.type = PERF_RECORD_FORK; |
164 | 145 | ||
165 | event->comm.tid = pid; | 146 | event->fork.header.size = (sizeof(event->fork) + machine->id_hdr_size); |
166 | 147 | ||
167 | if (process(tool, event, &synth_sample, machine) != 0) { | 148 | if (process(tool, event, &synth_sample, machine) != 0) |
168 | tgid = -1; | 149 | return -1; |
169 | break; | ||
170 | } | ||
171 | } | ||
172 | 150 | ||
173 | closedir(tasks); | 151 | return 0; |
174 | out: | ||
175 | return tgid; | ||
176 | } | 152 | } |
177 | 153 | ||
178 | int perf_event__synthesize_mmap_events(struct perf_tool *tool, | 154 | int perf_event__synthesize_mmap_events(struct perf_tool *tool, |
@@ -324,17 +300,71 @@ int perf_event__synthesize_modules(struct perf_tool *tool, | |||
324 | 300 | ||
325 | static int __event__synthesize_thread(union perf_event *comm_event, | 301 | static int __event__synthesize_thread(union perf_event *comm_event, |
326 | union perf_event *mmap_event, | 302 | union perf_event *mmap_event, |
303 | union perf_event *fork_event, | ||
327 | pid_t pid, int full, | 304 | pid_t pid, int full, |
328 | perf_event__handler_t process, | 305 | perf_event__handler_t process, |
329 | struct perf_tool *tool, | 306 | struct perf_tool *tool, |
330 | struct machine *machine, bool mmap_data) | 307 | struct machine *machine, bool mmap_data) |
331 | { | 308 | { |
332 | pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, | 309 | char filename[PATH_MAX]; |
310 | DIR *tasks; | ||
311 | struct dirent dirent, *next; | ||
312 | pid_t tgid; | ||
313 | |||
314 | /* special case: only send one comm event using passed in pid */ | ||
315 | if (!full) { | ||
316 | tgid = perf_event__synthesize_comm(tool, comm_event, pid, | ||
317 | process, machine); | ||
318 | |||
319 | if (tgid == -1) | ||
320 | return -1; | ||
321 | |||
322 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, | ||
323 | process, machine, mmap_data); | ||
324 | } | ||
325 | |||
326 | if (machine__is_default_guest(machine)) | ||
327 | return 0; | ||
328 | |||
329 | snprintf(filename, sizeof(filename), "%s/proc/%d/task", | ||
330 | machine->root_dir, pid); | ||
331 | |||
332 | tasks = opendir(filename); | ||
333 | if (tasks == NULL) { | ||
334 | pr_debug("couldn't open %s\n", filename); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
339 | char *end; | ||
340 | int rc = 0; | ||
341 | pid_t _pid; | ||
342 | |||
343 | _pid = strtol(dirent.d_name, &end, 10); | ||
344 | if (*end) | ||
345 | continue; | ||
346 | |||
347 | tgid = perf_event__synthesize_comm(tool, comm_event, _pid, | ||
348 | process, machine); | ||
349 | if (tgid == -1) | ||
350 | return -1; | ||
351 | |||
352 | if (_pid == pid) { | ||
353 | /* process the parent's maps too */ | ||
354 | rc = perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, | ||
355 | process, machine, mmap_data); | ||
356 | } else { | ||
357 | /* only fork the tid's map, to save time */ | ||
358 | rc = perf_event__synthesize_fork(tool, fork_event, _pid, tgid, | ||
333 | process, machine); | 359 | process, machine); |
334 | if (tgid == -1) | 360 | } |
335 | return -1; | 361 | |
336 | return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, | 362 | if (rc) |
337 | process, machine, mmap_data); | 363 | return rc; |
364 | } | ||
365 | |||
366 | closedir(tasks); | ||
367 | return 0; | ||
338 | } | 368 | } |
339 | 369 | ||
340 | int perf_event__synthesize_thread_map(struct perf_tool *tool, | 370 | int perf_event__synthesize_thread_map(struct perf_tool *tool, |
@@ -343,7 +373,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
343 | struct machine *machine, | 373 | struct machine *machine, |
344 | bool mmap_data) | 374 | bool mmap_data) |
345 | { | 375 | { |
346 | union perf_event *comm_event, *mmap_event; | 376 | union perf_event *comm_event, *mmap_event, *fork_event; |
347 | int err = -1, thread, j; | 377 | int err = -1, thread, j; |
348 | 378 | ||
349 | comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); | 379 | comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); |
@@ -354,9 +384,14 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
354 | if (mmap_event == NULL) | 384 | if (mmap_event == NULL) |
355 | goto out_free_comm; | 385 | goto out_free_comm; |
356 | 386 | ||
387 | fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); | ||
388 | if (fork_event == NULL) | ||
389 | goto out_free_mmap; | ||
390 | |||
357 | err = 0; | 391 | err = 0; |
358 | for (thread = 0; thread < threads->nr; ++thread) { | 392 | for (thread = 0; thread < threads->nr; ++thread) { |
359 | if (__event__synthesize_thread(comm_event, mmap_event, | 393 | if (__event__synthesize_thread(comm_event, mmap_event, |
394 | fork_event, | ||
360 | threads->map[thread], 0, | 395 | threads->map[thread], 0, |
361 | process, tool, machine, | 396 | process, tool, machine, |
362 | mmap_data)) { | 397 | mmap_data)) { |
@@ -382,6 +417,7 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
382 | /* if not, generate events for it */ | 417 | /* if not, generate events for it */ |
383 | if (need_leader && | 418 | if (need_leader && |
384 | __event__synthesize_thread(comm_event, mmap_event, | 419 | __event__synthesize_thread(comm_event, mmap_event, |
420 | fork_event, | ||
385 | comm_event->comm.pid, 0, | 421 | comm_event->comm.pid, 0, |
386 | process, tool, machine, | 422 | process, tool, machine, |
387 | mmap_data)) { | 423 | mmap_data)) { |
@@ -390,6 +426,8 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, | |||
390 | } | 426 | } |
391 | } | 427 | } |
392 | } | 428 | } |
429 | free(fork_event); | ||
430 | out_free_mmap: | ||
393 | free(mmap_event); | 431 | free(mmap_event); |
394 | out_free_comm: | 432 | out_free_comm: |
395 | free(comm_event); | 433 | free(comm_event); |
@@ -404,7 +442,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
404 | DIR *proc; | 442 | DIR *proc; |
405 | char proc_path[PATH_MAX]; | 443 | char proc_path[PATH_MAX]; |
406 | struct dirent dirent, *next; | 444 | struct dirent dirent, *next; |
407 | union perf_event *comm_event, *mmap_event; | 445 | union perf_event *comm_event, *mmap_event, *fork_event; |
408 | int err = -1; | 446 | int err = -1; |
409 | 447 | ||
410 | comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); | 448 | comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); |
@@ -415,6 +453,10 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
415 | if (mmap_event == NULL) | 453 | if (mmap_event == NULL) |
416 | goto out_free_comm; | 454 | goto out_free_comm; |
417 | 455 | ||
456 | fork_event = malloc(sizeof(fork_event->fork) + machine->id_hdr_size); | ||
457 | if (fork_event == NULL) | ||
458 | goto out_free_mmap; | ||
459 | |||
418 | if (machine__is_default_guest(machine)) | 460 | if (machine__is_default_guest(machine)) |
419 | return 0; | 461 | return 0; |
420 | 462 | ||
@@ -422,7 +464,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
422 | proc = opendir(proc_path); | 464 | proc = opendir(proc_path); |
423 | 465 | ||
424 | if (proc == NULL) | 466 | if (proc == NULL) |
425 | goto out_free_mmap; | 467 | goto out_free_fork; |
426 | 468 | ||
427 | while (!readdir_r(proc, &dirent, &next) && next) { | 469 | while (!readdir_r(proc, &dirent, &next) && next) { |
428 | char *end; | 470 | char *end; |
@@ -434,12 +476,14 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
434 | * We may race with exiting thread, so don't stop just because | 476 | * We may race with exiting thread, so don't stop just because |
435 | * one thread couldn't be synthesized. | 477 | * one thread couldn't be synthesized. |
436 | */ | 478 | */ |
437 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, | 479 | __event__synthesize_thread(comm_event, mmap_event, fork_event, pid, |
438 | process, tool, machine, mmap_data); | 480 | 1, process, tool, machine, mmap_data); |
439 | } | 481 | } |
440 | 482 | ||
441 | err = 0; | 483 | err = 0; |
442 | closedir(proc); | 484 | closedir(proc); |
485 | out_free_fork: | ||
486 | free(fork_event); | ||
443 | out_free_mmap: | 487 | out_free_mmap: |
444 | free(mmap_event); | 488 | free(mmap_event); |
445 | out_free_comm: | 489 | out_free_comm: |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a59743fa3ef7..0c76bf972736 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -132,8 +132,10 @@ struct perf_hpp { | |||
132 | }; | 132 | }; |
133 | 133 | ||
134 | struct perf_hpp_fmt { | 134 | struct perf_hpp_fmt { |
135 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); | 135 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
136 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); | 136 | struct perf_evsel *evsel); |
137 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
138 | struct perf_evsel *evsel); | ||
137 | int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 139 | int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
138 | struct hist_entry *he); | 140 | struct hist_entry *he); |
139 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 141 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
@@ -166,6 +168,20 @@ void perf_hpp__init(void); | |||
166 | void perf_hpp__column_register(struct perf_hpp_fmt *format); | 168 | void perf_hpp__column_register(struct perf_hpp_fmt *format); |
167 | void perf_hpp__column_enable(unsigned col); | 169 | void perf_hpp__column_enable(unsigned col); |
168 | 170 | ||
171 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); | ||
172 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); | ||
173 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); | ||
174 | |||
175 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | ||
176 | hpp_field_fn get_field, hpp_callback_fn callback, | ||
177 | const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); | ||
178 | |||
179 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | ||
180 | { | ||
181 | hpp->buf += inc; | ||
182 | hpp->size -= inc; | ||
183 | } | ||
184 | |||
169 | static inline size_t perf_hpp__use_color(void) | 185 | static inline size_t perf_hpp__use_color(void) |
170 | { | 186 | { |
171 | return !symbol_conf.field_sep; | 187 | return !symbol_conf.field_sep; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 813e94e7cf29..5cecd98c1bc0 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -327,9 +327,10 @@ struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, | |||
327 | return __machine__findnew_thread(machine, pid, tid, true); | 327 | return __machine__findnew_thread(machine, pid, tid, true); |
328 | } | 328 | } |
329 | 329 | ||
330 | struct thread *machine__find_thread(struct machine *machine, pid_t tid) | 330 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, |
331 | pid_t tid) | ||
331 | { | 332 | { |
332 | return __machine__findnew_thread(machine, 0, tid, false); | 333 | return __machine__findnew_thread(machine, pid, tid, false); |
333 | } | 334 | } |
334 | 335 | ||
335 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 336 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
@@ -1114,7 +1115,9 @@ static void machine__remove_thread(struct machine *machine, struct thread *th) | |||
1114 | int machine__process_fork_event(struct machine *machine, union perf_event *event, | 1115 | int machine__process_fork_event(struct machine *machine, union perf_event *event, |
1115 | struct perf_sample *sample) | 1116 | struct perf_sample *sample) |
1116 | { | 1117 | { |
1117 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1118 | struct thread *thread = machine__find_thread(machine, |
1119 | event->fork.pid, | ||
1120 | event->fork.tid); | ||
1118 | struct thread *parent = machine__findnew_thread(machine, | 1121 | struct thread *parent = machine__findnew_thread(machine, |
1119 | event->fork.ppid, | 1122 | event->fork.ppid, |
1120 | event->fork.ptid); | 1123 | event->fork.ptid); |
@@ -1140,7 +1143,9 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1140 | int machine__process_exit_event(struct machine *machine, union perf_event *event, | 1143 | int machine__process_exit_event(struct machine *machine, union perf_event *event, |
1141 | struct perf_sample *sample __maybe_unused) | 1144 | struct perf_sample *sample __maybe_unused) |
1142 | { | 1145 | { |
1143 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1146 | struct thread *thread = machine__find_thread(machine, |
1147 | event->fork.pid, | ||
1148 | event->fork.tid); | ||
1144 | 1149 | ||
1145 | if (dump_trace) | 1150 | if (dump_trace) |
1146 | perf_event__fprintf_task(event, stdout); | 1151 | perf_event__fprintf_task(event, stdout); |
@@ -1184,39 +1189,22 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex) | |||
1184 | return 0; | 1189 | return 0; |
1185 | } | 1190 | } |
1186 | 1191 | ||
1187 | static const u8 cpumodes[] = { | ||
1188 | PERF_RECORD_MISC_USER, | ||
1189 | PERF_RECORD_MISC_KERNEL, | ||
1190 | PERF_RECORD_MISC_GUEST_USER, | ||
1191 | PERF_RECORD_MISC_GUEST_KERNEL | ||
1192 | }; | ||
1193 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
1194 | |||
1195 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | 1192 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, |
1196 | struct addr_map_symbol *ams, | 1193 | struct addr_map_symbol *ams, |
1197 | u64 ip) | 1194 | u64 ip) |
1198 | { | 1195 | { |
1199 | struct addr_location al; | 1196 | struct addr_location al; |
1200 | size_t i; | ||
1201 | u8 m; | ||
1202 | 1197 | ||
1203 | memset(&al, 0, sizeof(al)); | 1198 | memset(&al, 0, sizeof(al)); |
1199 | /* | ||
1200 | * We cannot use the header.misc hint to determine whether a | ||
1201 | * branch stack address is user, kernel, guest, hypervisor. | ||
1202 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
1203 | * Thus, we have to try consecutively until we find a match | ||
1204 | * or else, the symbol is unknown | ||
1205 | */ | ||
1206 | thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); | ||
1204 | 1207 | ||
1205 | for (i = 0; i < NCPUMODES; i++) { | ||
1206 | m = cpumodes[i]; | ||
1207 | /* | ||
1208 | * We cannot use the header.misc hint to determine whether a | ||
1209 | * branch stack address is user, kernel, guest, hypervisor. | ||
1210 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
1211 | * Thus, we have to try consecutively until we find a match | ||
1212 | * or else, the symbol is unknown | ||
1213 | */ | ||
1214 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, | ||
1215 | ip, &al); | ||
1216 | if (al.map) | ||
1217 | goto found; | ||
1218 | } | ||
1219 | found: | ||
1220 | ams->addr = ip; | 1208 | ams->addr = ip; |
1221 | ams->al_addr = al.addr; | 1209 | ams->al_addr = al.addr; |
1222 | ams->sym = al.sym; | 1210 | ams->sym = al.sym; |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 2e6c248c870f..c8c74a119398 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -41,7 +41,8 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) | |||
41 | return machine->vmlinux_maps[type]; | 41 | return machine->vmlinux_maps[type]; |
42 | } | 42 | } |
43 | 43 | ||
44 | struct thread *machine__find_thread(struct machine *machine, pid_t tid); | 44 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, |
45 | pid_t tid); | ||
45 | 46 | ||
46 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 47 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
47 | struct perf_sample *sample); | 48 | struct perf_sample *sample); |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index d22e3f8017dc..bf48092983c6 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -407,7 +407,9 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, | |||
407 | if (internal_help && !strcmp(arg + 2, "help")) | 407 | if (internal_help && !strcmp(arg + 2, "help")) |
408 | return usage_with_options_internal(usagestr, options, 0); | 408 | return usage_with_options_internal(usagestr, options, 0); |
409 | if (!strcmp(arg + 2, "list-opts")) | 409 | if (!strcmp(arg + 2, "list-opts")) |
410 | return PARSE_OPT_LIST; | 410 | return PARSE_OPT_LIST_OPTS; |
411 | if (!strcmp(arg + 2, "list-cmds")) | ||
412 | return PARSE_OPT_LIST_SUBCMDS; | ||
411 | switch (parse_long_opt(ctx, arg + 2, options)) { | 413 | switch (parse_long_opt(ctx, arg + 2, options)) { |
412 | case -1: | 414 | case -1: |
413 | return parse_options_usage(usagestr, options, arg + 2, 0); | 415 | return parse_options_usage(usagestr, options, arg + 2, 0); |
@@ -433,25 +435,45 @@ int parse_options_end(struct parse_opt_ctx_t *ctx) | |||
433 | return ctx->cpidx + ctx->argc; | 435 | return ctx->cpidx + ctx->argc; |
434 | } | 436 | } |
435 | 437 | ||
436 | int parse_options(int argc, const char **argv, const struct option *options, | 438 | int parse_options_subcommand(int argc, const char **argv, const struct option *options, |
437 | const char * const usagestr[], int flags) | 439 | const char *const subcommands[], const char *usagestr[], int flags) |
438 | { | 440 | { |
439 | struct parse_opt_ctx_t ctx; | 441 | struct parse_opt_ctx_t ctx; |
440 | 442 | ||
441 | perf_header__set_cmdline(argc, argv); | 443 | perf_header__set_cmdline(argc, argv); |
442 | 444 | ||
445 | /* build usage string if it's not provided */ | ||
446 | if (subcommands && !usagestr[0]) { | ||
447 | struct strbuf buf = STRBUF_INIT; | ||
448 | |||
449 | strbuf_addf(&buf, "perf %s [<options>] {", argv[0]); | ||
450 | for (int i = 0; subcommands[i]; i++) { | ||
451 | if (i) | ||
452 | strbuf_addstr(&buf, "|"); | ||
453 | strbuf_addstr(&buf, subcommands[i]); | ||
454 | } | ||
455 | strbuf_addstr(&buf, "}"); | ||
456 | |||
457 | usagestr[0] = strdup(buf.buf); | ||
458 | strbuf_release(&buf); | ||
459 | } | ||
460 | |||
443 | parse_options_start(&ctx, argc, argv, flags); | 461 | parse_options_start(&ctx, argc, argv, flags); |
444 | switch (parse_options_step(&ctx, options, usagestr)) { | 462 | switch (parse_options_step(&ctx, options, usagestr)) { |
445 | case PARSE_OPT_HELP: | 463 | case PARSE_OPT_HELP: |
446 | exit(129); | 464 | exit(129); |
447 | case PARSE_OPT_DONE: | 465 | case PARSE_OPT_DONE: |
448 | break; | 466 | break; |
449 | case PARSE_OPT_LIST: | 467 | case PARSE_OPT_LIST_OPTS: |
450 | while (options->type != OPTION_END) { | 468 | while (options->type != OPTION_END) { |
451 | printf("--%s ", options->long_name); | 469 | printf("--%s ", options->long_name); |
452 | options++; | 470 | options++; |
453 | } | 471 | } |
454 | exit(130); | 472 | exit(130); |
473 | case PARSE_OPT_LIST_SUBCMDS: | ||
474 | for (int i = 0; subcommands[i]; i++) | ||
475 | printf("%s ", subcommands[i]); | ||
476 | exit(130); | ||
455 | default: /* PARSE_OPT_UNKNOWN */ | 477 | default: /* PARSE_OPT_UNKNOWN */ |
456 | if (ctx.argv[0][1] == '-') { | 478 | if (ctx.argv[0][1] == '-') { |
457 | error("unknown option `%s'", ctx.argv[0] + 2); | 479 | error("unknown option `%s'", ctx.argv[0] + 2); |
@@ -464,6 +486,13 @@ int parse_options(int argc, const char **argv, const struct option *options, | |||
464 | return parse_options_end(&ctx); | 486 | return parse_options_end(&ctx); |
465 | } | 487 | } |
466 | 488 | ||
489 | int parse_options(int argc, const char **argv, const struct option *options, | ||
490 | const char * const usagestr[], int flags) | ||
491 | { | ||
492 | return parse_options_subcommand(argc, argv, options, NULL, | ||
493 | (const char **) usagestr, flags); | ||
494 | } | ||
495 | |||
467 | #define USAGE_OPTS_WIDTH 24 | 496 | #define USAGE_OPTS_WIDTH 24 |
468 | #define USAGE_GAP 2 | 497 | #define USAGE_GAP 2 |
469 | 498 | ||
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index cbf0149cf221..d8dac8ac5f37 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -140,6 +140,11 @@ extern int parse_options(int argc, const char **argv, | |||
140 | const struct option *options, | 140 | const struct option *options, |
141 | const char * const usagestr[], int flags); | 141 | const char * const usagestr[], int flags); |
142 | 142 | ||
143 | extern int parse_options_subcommand(int argc, const char **argv, | ||
144 | const struct option *options, | ||
145 | const char *const subcommands[], | ||
146 | const char *usagestr[], int flags); | ||
147 | |||
143 | extern NORETURN void usage_with_options(const char * const *usagestr, | 148 | extern NORETURN void usage_with_options(const char * const *usagestr, |
144 | const struct option *options); | 149 | const struct option *options); |
145 | 150 | ||
@@ -148,7 +153,8 @@ extern NORETURN void usage_with_options(const char * const *usagestr, | |||
148 | enum { | 153 | enum { |
149 | PARSE_OPT_HELP = -1, | 154 | PARSE_OPT_HELP = -1, |
150 | PARSE_OPT_DONE, | 155 | PARSE_OPT_DONE, |
151 | PARSE_OPT_LIST, | 156 | PARSE_OPT_LIST_OPTS, |
157 | PARSE_OPT_LIST_SUBCMDS, | ||
152 | PARSE_OPT_UNKNOWN, | 158 | PARSE_OPT_UNKNOWN, |
153 | }; | 159 | }; |
154 | 160 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1d555d652f58..55960f22233c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -794,7 +794,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
794 | if (!dump_trace) | 794 | if (!dump_trace) |
795 | return; | 795 | return; |
796 | 796 | ||
797 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", | 797 | printf("(IP, 0x%x): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", |
798 | event->header.misc, sample->pid, sample->tid, sample->ip, | 798 | event->header.misc, sample->pid, sample->tid, sample->ip, |
799 | sample->period, sample->addr); | 799 | sample->period, sample->addr); |
800 | 800 | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 0358882c8910..3ce0498bdae6 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -142,3 +142,24 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | |||
142 | 142 | ||
143 | return 0; | 143 | return 0; |
144 | } | 144 | } |
145 | |||
146 | void thread__find_cpumode_addr_location(struct thread *thread, | ||
147 | struct machine *machine, | ||
148 | enum map_type type, u64 addr, | ||
149 | struct addr_location *al) | ||
150 | { | ||
151 | size_t i; | ||
152 | const u8 const cpumodes[] = { | ||
153 | PERF_RECORD_MISC_USER, | ||
154 | PERF_RECORD_MISC_KERNEL, | ||
155 | PERF_RECORD_MISC_GUEST_USER, | ||
156 | PERF_RECORD_MISC_GUEST_KERNEL | ||
157 | }; | ||
158 | |||
159 | for (i = 0; i < ARRAY_SIZE(cpumodes); i++) { | ||
160 | thread__find_addr_location(thread, machine, cpumodes[i], type, | ||
161 | addr, al); | ||
162 | if (al->map) | ||
163 | break; | ||
164 | } | ||
165 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 5b856bf942e1..9a070743270c 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -58,6 +58,11 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine, | |||
58 | u8 cpumode, enum map_type type, u64 addr, | 58 | u8 cpumode, enum map_type type, u64 addr, |
59 | struct addr_location *al); | 59 | struct addr_location *al); |
60 | 60 | ||
61 | void thread__find_cpumode_addr_location(struct thread *thread, | ||
62 | struct machine *machine, | ||
63 | enum map_type type, u64 addr, | ||
64 | struct addr_location *al); | ||
65 | |||
61 | static inline void *thread__priv(struct thread *thread) | 66 | static inline void *thread__priv(struct thread *thread) |
62 | { | 67 | { |
63 | return thread->priv; | 68 | return thread->priv; |