diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-03 16:18:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-03 16:18:00 -0400 |
commit | 3d521f9151dacab566904d1f57dcb3e7080cdd8f (patch) | |
tree | 160d15ff955541c6ca27a69c8291a0269f105bb3 | |
parent | 776edb59317ada867dfcddde40b55648beeb0078 (diff) | |
parent | e450f90e8c7d0bf70519223c1b848446ae63f313 (diff) |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next
Pull perf updates from Ingo Molnar:
"The tooling changes maintained by Jiri Olsa until Arnaldo is on
vacation:
User visible changes:
- Add -F option for specifying output fields (Namhyung Kim)
- Propagate exit status of a command line workload for record command
(Namhyung Kim)
- Use tid for finding thread (Namhyung Kim)
- Clarify the output of perf sched map plus small sched command
fixes (Dongsheng Yang)
- Wire up perf_regs and unwind support for ARM64 (Jean Pihet)
- Factor hists statistics counts processing which in turn also fixes
several bugs in TUI report command (Namhyung Kim)
- Add --percentage option to control absolute/relative percentage
output (Namhyung Kim)
- Add --list-cmds to 'kmem', 'mem', 'lock' and 'sched', for use by
completion scripts (Ramkumar Ramachandra)
Development/infrastructure changes and fixes:
- Android related fixes for pager and map dso resolving (Michael
Lentine)
- Add libdw DWARF post unwind support for ARM (Jean Pihet)
- Consolidate types.h for ARM and ARM64 (Jean Pihet)
- Fix possible null pointer dereference in session.c (Masanari Iida)
- Cleanup, remove unused variables in map_switch_event() (Dongsheng
Yang)
- Remove nr_state_machine_bugs in perf latency (Dongsheng Yang)
- Remove usage of trace_sched_wakeup(.success) (Peter Zijlstra)
- Cleanups for perf.h header (Jiri Olsa)
- Consolidate types.h and export.h within tools (Borislav Petkov)
- Move u64_swap union to its single user's header, evsel.h (Borislav
Petkov)
- Fix for s390 to properly parse tracepoints plus test code
(Alexander Yarygin)
- Handle EINTR error for readn/writen (Namhyung Kim)
- Add a test case for hists filtering (Namhyung Kim)
- Share map_groups among threads of the same group (Arnaldo Carvalho
de Melo, Jiri Olsa)
- Making some code (cpu node map and report parse callchain callback)
global to be usable by upcomming changes (Don Zickus)
- Fix pmu object compilation error (Jiri Olsa)
Kernel side changes:
- intrusive uprobes fixes from Oleg Nesterov. Since the interface is
admin-only, and the bug only affects user-space ("any probed
jmp/call can kill the application"), we queued these fixes via the
development tree, as a special exception.
- more fuzzer motivated race fixes and related refactoring and
robustization.
- allow PMU drivers to be built as modules. (No actual module yet,
because the x86 Intel uncore module wasn't ready in time for this)"
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (114 commits)
perf tools: Add automatic remapping of Android libraries
perf tools: Add cat as fallback pager
perf tests: Add a testcase for histogram output sorting
perf tests: Factor out print_hists_*()
perf tools: Introduce reset_output_field()
perf tools: Get rid of obsolete hist_entry__sort_list
perf hists: Reset width of output fields with header length
perf tools: Skip elided sort entries
perf top: Add --fields option to specify output fields
perf report/tui: Fix a bug when --fields/sort is given
perf tools: Add ->sort() member to struct sort_entry
perf report: Add -F option to specify output fields
perf tools: Call perf_hpp__init() before setting up GUI browsers
perf tools: Consolidate management of default sort orders
perf tools: Allow hpp fields to be sort keys
perf ui: Get rid of callback from __hpp__fmt()
perf tools: Consolidate output field handling to hpp format routines
perf tools: Use hpp formats to sort final output
perf tools: Support event grouping in hpp ->sort()
perf tools: Use hpp formats to sort hist entries
...
123 files changed, 4591 insertions, 1718 deletions
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 3087ea9c5f2e..93bee7b93854 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h | |||
@@ -33,15 +33,27 @@ typedef u8 uprobe_opcode_t; | |||
33 | #define UPROBE_SWBP_INSN 0xcc | 33 | #define UPROBE_SWBP_INSN 0xcc |
34 | #define UPROBE_SWBP_INSN_SIZE 1 | 34 | #define UPROBE_SWBP_INSN_SIZE 1 |
35 | 35 | ||
36 | struct uprobe_xol_ops; | ||
37 | |||
36 | struct arch_uprobe { | 38 | struct arch_uprobe { |
37 | u16 fixups; | ||
38 | union { | 39 | union { |
39 | u8 insn[MAX_UINSN_BYTES]; | 40 | u8 insn[MAX_UINSN_BYTES]; |
40 | u8 ixol[MAX_UINSN_BYTES]; | 41 | u8 ixol[MAX_UINSN_BYTES]; |
41 | }; | 42 | }; |
43 | |||
44 | u16 fixups; | ||
45 | const struct uprobe_xol_ops *ops; | ||
46 | |||
47 | union { | ||
42 | #ifdef CONFIG_X86_64 | 48 | #ifdef CONFIG_X86_64 |
43 | unsigned long rip_rela_target_address; | 49 | unsigned long rip_rela_target_address; |
44 | #endif | 50 | #endif |
51 | struct { | ||
52 | s32 offs; | ||
53 | u8 ilen; | ||
54 | u8 opc1; | ||
55 | } branch; | ||
56 | }; | ||
45 | }; | 57 | }; |
46 | 58 | ||
47 | struct arch_uprobe_task { | 59 | struct arch_uprobe_task { |
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index ae407f7226c8..89f3b7c1af20 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -721,6 +721,7 @@ int perf_assign_events(struct perf_event **events, int n, | |||
721 | 721 | ||
722 | return sched.state.unassigned; | 722 | return sched.state.unassigned; |
723 | } | 723 | } |
724 | EXPORT_SYMBOL_GPL(perf_assign_events); | ||
724 | 725 | ||
725 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) | 726 | int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) |
726 | { | 727 | { |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index ae96cfa5eddd..980970cb744d 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c | |||
@@ -108,15 +108,31 @@ static u64 precise_store_data(u64 status) | |||
108 | return val; | 108 | return val; |
109 | } | 109 | } |
110 | 110 | ||
111 | static u64 precise_store_data_hsw(u64 status) | 111 | static u64 precise_store_data_hsw(struct perf_event *event, u64 status) |
112 | { | 112 | { |
113 | union perf_mem_data_src dse; | 113 | union perf_mem_data_src dse; |
114 | u64 cfg = event->hw.config & INTEL_ARCH_EVENT_MASK; | ||
114 | 115 | ||
115 | dse.val = 0; | 116 | dse.val = 0; |
116 | dse.mem_op = PERF_MEM_OP_STORE; | 117 | dse.mem_op = PERF_MEM_OP_STORE; |
117 | dse.mem_lvl = PERF_MEM_LVL_NA; | 118 | dse.mem_lvl = PERF_MEM_LVL_NA; |
119 | |||
120 | /* | ||
121 | * L1 info only valid for following events: | ||
122 | * | ||
123 | * MEM_UOPS_RETIRED.STLB_MISS_STORES | ||
124 | * MEM_UOPS_RETIRED.LOCK_STORES | ||
125 | * MEM_UOPS_RETIRED.SPLIT_STORES | ||
126 | * MEM_UOPS_RETIRED.ALL_STORES | ||
127 | */ | ||
128 | if (cfg != 0x12d0 && cfg != 0x22d0 && cfg != 0x42d0 && cfg != 0x82d0) | ||
129 | return dse.mem_lvl; | ||
130 | |||
118 | if (status & 1) | 131 | if (status & 1) |
119 | dse.mem_lvl = PERF_MEM_LVL_L1; | 132 | dse.mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_HIT; |
133 | else | ||
134 | dse.mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_MISS; | ||
135 | |||
120 | /* Nothing else supported. Sorry. */ | 136 | /* Nothing else supported. Sorry. */ |
121 | return dse.val; | 137 | return dse.val; |
122 | } | 138 | } |
@@ -887,7 +903,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event, | |||
887 | data.data_src.val = load_latency_data(pebs->dse); | 903 | data.data_src.val = load_latency_data(pebs->dse); |
888 | else if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW) | 904 | else if (event->hw.flags & PERF_X86_EVENT_PEBS_ST_HSW) |
889 | data.data_src.val = | 905 | data.data_src.val = |
890 | precise_store_data_hsw(pebs->dse); | 906 | precise_store_data_hsw(event, pebs->dse); |
891 | else | 907 | else |
892 | data.data_src.val = precise_store_data(pebs->dse); | 908 | data.data_src.val = precise_store_data(pebs->dse); |
893 | } | 909 | } |
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 2ed845928b5f..ace22916ade3 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c | |||
@@ -53,7 +53,7 @@ | |||
53 | #define OPCODE1(insn) ((insn)->opcode.bytes[0]) | 53 | #define OPCODE1(insn) ((insn)->opcode.bytes[0]) |
54 | #define OPCODE2(insn) ((insn)->opcode.bytes[1]) | 54 | #define OPCODE2(insn) ((insn)->opcode.bytes[1]) |
55 | #define OPCODE3(insn) ((insn)->opcode.bytes[2]) | 55 | #define OPCODE3(insn) ((insn)->opcode.bytes[2]) |
56 | #define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) | 56 | #define MODRM_REG(insn) X86_MODRM_REG((insn)->modrm.value) |
57 | 57 | ||
58 | #define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ | 58 | #define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ |
59 | (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ | 59 | (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ |
@@ -229,63 +229,6 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn) | |||
229 | return -ENOTSUPP; | 229 | return -ENOTSUPP; |
230 | } | 230 | } |
231 | 231 | ||
232 | /* | ||
233 | * Figure out which fixups arch_uprobe_post_xol() will need to perform, and | ||
234 | * annotate arch_uprobe->fixups accordingly. To start with, | ||
235 | * arch_uprobe->fixups is either zero or it reflects rip-related fixups. | ||
236 | */ | ||
237 | static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) | ||
238 | { | ||
239 | bool fix_ip = true, fix_call = false; /* defaults */ | ||
240 | int reg; | ||
241 | |||
242 | insn_get_opcode(insn); /* should be a nop */ | ||
243 | |||
244 | switch (OPCODE1(insn)) { | ||
245 | case 0x9d: | ||
246 | /* popf */ | ||
247 | auprobe->fixups |= UPROBE_FIX_SETF; | ||
248 | break; | ||
249 | case 0xc3: /* ret/lret */ | ||
250 | case 0xcb: | ||
251 | case 0xc2: | ||
252 | case 0xca: | ||
253 | /* ip is correct */ | ||
254 | fix_ip = false; | ||
255 | break; | ||
256 | case 0xe8: /* call relative - Fix return addr */ | ||
257 | fix_call = true; | ||
258 | break; | ||
259 | case 0x9a: /* call absolute - Fix return addr, not ip */ | ||
260 | fix_call = true; | ||
261 | fix_ip = false; | ||
262 | break; | ||
263 | case 0xff: | ||
264 | insn_get_modrm(insn); | ||
265 | reg = MODRM_REG(insn); | ||
266 | if (reg == 2 || reg == 3) { | ||
267 | /* call or lcall, indirect */ | ||
268 | /* Fix return addr; ip is correct. */ | ||
269 | fix_call = true; | ||
270 | fix_ip = false; | ||
271 | } else if (reg == 4 || reg == 5) { | ||
272 | /* jmp or ljmp, indirect */ | ||
273 | /* ip is correct. */ | ||
274 | fix_ip = false; | ||
275 | } | ||
276 | break; | ||
277 | case 0xea: /* jmp absolute -- ip is correct */ | ||
278 | fix_ip = false; | ||
279 | break; | ||
280 | default: | ||
281 | break; | ||
282 | } | ||
283 | if (fix_ip) | ||
284 | auprobe->fixups |= UPROBE_FIX_IP; | ||
285 | if (fix_call) | ||
286 | auprobe->fixups |= UPROBE_FIX_CALL; | ||
287 | } | ||
288 | |||
289 | #ifdef CONFIG_X86_64 | 232 | #ifdef CONFIG_X86_64 |
290 | /* | 233 | /* |
291 | * If arch_uprobe->insn doesn't use rip-relative addressing, return | 234 | * If arch_uprobe->insn doesn't use rip-relative addressing, return |
@@ -310,15 +253,11 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) | |||
310 | * - The displacement is always 4 bytes. | 253 | * - The displacement is always 4 bytes. |
311 | */ | 254 | */ |
312 | static void | 255 | static void |
313 | handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) | 256 | handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn) |
314 | { | 257 | { |
315 | u8 *cursor; | 258 | u8 *cursor; |
316 | u8 reg; | 259 | u8 reg; |
317 | 260 | ||
318 | if (mm->context.ia32_compat) | ||
319 | return; | ||
320 | |||
321 | auprobe->rip_rela_target_address = 0x0; | ||
322 | if (!insn_rip_relative(insn)) | 261 | if (!insn_rip_relative(insn)) |
323 | return; | 262 | return; |
324 | 263 | ||
@@ -372,7 +311,48 @@ handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct ins | |||
372 | cursor++; | 311 | cursor++; |
373 | memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes); | 312 | memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes); |
374 | } | 313 | } |
375 | return; | 314 | } |
315 | |||
316 | /* | ||
317 | * If we're emulating a rip-relative instruction, save the contents | ||
318 | * of the scratch register and store the target address in that register. | ||
319 | */ | ||
320 | static void | ||
321 | pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, | ||
322 | struct arch_uprobe_task *autask) | ||
323 | { | ||
324 | if (auprobe->fixups & UPROBE_FIX_RIP_AX) { | ||
325 | autask->saved_scratch_register = regs->ax; | ||
326 | regs->ax = current->utask->vaddr; | ||
327 | regs->ax += auprobe->rip_rela_target_address; | ||
328 | } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) { | ||
329 | autask->saved_scratch_register = regs->cx; | ||
330 | regs->cx = current->utask->vaddr; | ||
331 | regs->cx += auprobe->rip_rela_target_address; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | static void | ||
336 | handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) | ||
337 | { | ||
338 | if (auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) { | ||
339 | struct arch_uprobe_task *autask; | ||
340 | |||
341 | autask = ¤t->utask->autask; | ||
342 | if (auprobe->fixups & UPROBE_FIX_RIP_AX) | ||
343 | regs->ax = autask->saved_scratch_register; | ||
344 | else | ||
345 | regs->cx = autask->saved_scratch_register; | ||
346 | |||
347 | /* | ||
348 | * The original instruction includes a displacement, and so | ||
349 | * is 4 bytes longer than what we've just single-stepped. | ||
350 | * Caller may need to apply other fixups to handle stuff | ||
351 | * like "jmpq *...(%rip)" and "callq *...(%rip)". | ||
352 | */ | ||
353 | if (correction) | ||
354 | *correction += 4; | ||
355 | } | ||
376 | } | 356 | } |
377 | 357 | ||
378 | static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) | 358 | static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) |
@@ -401,9 +381,19 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, | |||
401 | return validate_insn_64bits(auprobe, insn); | 381 | return validate_insn_64bits(auprobe, insn); |
402 | } | 382 | } |
403 | #else /* 32-bit: */ | 383 | #else /* 32-bit: */ |
404 | static void handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) | 384 | /* |
385 | * No RIP-relative addressing on 32-bit | ||
386 | */ | ||
387 | static void handle_riprel_insn(struct arch_uprobe *auprobe, struct insn *insn) | ||
388 | { | ||
389 | } | ||
390 | static void pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, | ||
391 | struct arch_uprobe_task *autask) | ||
392 | { | ||
393 | } | ||
394 | static void handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, | ||
395 | long *correction) | ||
405 | { | 396 | { |
406 | /* No RIP-relative addressing on 32-bit */ | ||
407 | } | 397 | } |
408 | 398 | ||
409 | static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) | 399 | static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) |
@@ -412,141 +402,311 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, | |||
412 | } | 402 | } |
413 | #endif /* CONFIG_X86_64 */ | 403 | #endif /* CONFIG_X86_64 */ |
414 | 404 | ||
415 | /** | 405 | struct uprobe_xol_ops { |
416 | * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. | 406 | bool (*emulate)(struct arch_uprobe *, struct pt_regs *); |
417 | * @mm: the probed address space. | 407 | int (*pre_xol)(struct arch_uprobe *, struct pt_regs *); |
418 | * @arch_uprobe: the probepoint information. | 408 | int (*post_xol)(struct arch_uprobe *, struct pt_regs *); |
419 | * @addr: virtual address at which to install the probepoint | 409 | }; |
420 | * Return 0 on success or a -ve number on error. | 410 | |
411 | static inline int sizeof_long(void) | ||
412 | { | ||
413 | return is_ia32_task() ? 4 : 8; | ||
414 | } | ||
415 | |||
416 | static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
417 | { | ||
418 | pre_xol_rip_insn(auprobe, regs, ¤t->utask->autask); | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * Adjust the return address pushed by a call insn executed out of line. | ||
421 | */ | 424 | */ |
422 | int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr) | 425 | static int adjust_ret_addr(unsigned long sp, long correction) |
423 | { | 426 | { |
424 | int ret; | 427 | int rasize = sizeof_long(); |
425 | struct insn insn; | 428 | long ra; |
426 | 429 | ||
427 | auprobe->fixups = 0; | 430 | if (copy_from_user(&ra, (void __user *)sp, rasize)) |
428 | ret = validate_insn_bits(auprobe, mm, &insn); | 431 | return -EFAULT; |
429 | if (ret != 0) | ||
430 | return ret; | ||
431 | 432 | ||
432 | handle_riprel_insn(auprobe, mm, &insn); | 433 | ra += correction; |
433 | prepare_fixups(auprobe, &insn); | 434 | if (copy_to_user((void __user *)sp, &ra, rasize)) |
435 | return -EFAULT; | ||
434 | 436 | ||
435 | return 0; | 437 | return 0; |
436 | } | 438 | } |
437 | 439 | ||
438 | #ifdef CONFIG_X86_64 | 440 | static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs) |
439 | /* | ||
440 | * If we're emulating a rip-relative instruction, save the contents | ||
441 | * of the scratch register and store the target address in that register. | ||
442 | */ | ||
443 | static void | ||
444 | pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, | ||
445 | struct arch_uprobe_task *autask) | ||
446 | { | 441 | { |
447 | if (auprobe->fixups & UPROBE_FIX_RIP_AX) { | 442 | struct uprobe_task *utask = current->utask; |
448 | autask->saved_scratch_register = regs->ax; | 443 | long correction = (long)(utask->vaddr - utask->xol_vaddr); |
449 | regs->ax = current->utask->vaddr; | 444 | |
450 | regs->ax += auprobe->rip_rela_target_address; | 445 | handle_riprel_post_xol(auprobe, regs, &correction); |
451 | } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) { | 446 | if (auprobe->fixups & UPROBE_FIX_IP) |
452 | autask->saved_scratch_register = regs->cx; | 447 | regs->ip += correction; |
453 | regs->cx = current->utask->vaddr; | 448 | |
454 | regs->cx += auprobe->rip_rela_target_address; | 449 | if (auprobe->fixups & UPROBE_FIX_CALL) { |
450 | if (adjust_ret_addr(regs->sp, correction)) { | ||
451 | regs->sp += sizeof_long(); | ||
452 | return -ERESTART; | ||
453 | } | ||
455 | } | 454 | } |
455 | |||
456 | return 0; | ||
456 | } | 457 | } |
457 | #else | 458 | |
458 | static void | 459 | static struct uprobe_xol_ops default_xol_ops = { |
459 | pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, | 460 | .pre_xol = default_pre_xol_op, |
460 | struct arch_uprobe_task *autask) | 461 | .post_xol = default_post_xol_op, |
462 | }; | ||
463 | |||
464 | static bool branch_is_call(struct arch_uprobe *auprobe) | ||
461 | { | 465 | { |
462 | /* No RIP-relative addressing on 32-bit */ | 466 | return auprobe->branch.opc1 == 0xe8; |
463 | } | 467 | } |
464 | #endif | ||
465 | 468 | ||
466 | /* | 469 | #define CASE_COND \ |
467 | * arch_uprobe_pre_xol - prepare to execute out of line. | 470 | COND(70, 71, XF(OF)) \ |
468 | * @auprobe: the probepoint information. | 471 | COND(72, 73, XF(CF)) \ |
469 | * @regs: reflects the saved user state of current task. | 472 | COND(74, 75, XF(ZF)) \ |
470 | */ | 473 | COND(78, 79, XF(SF)) \ |
471 | int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | 474 | COND(7a, 7b, XF(PF)) \ |
472 | { | 475 | COND(76, 77, XF(CF) || XF(ZF)) \ |
473 | struct arch_uprobe_task *autask; | 476 | COND(7c, 7d, XF(SF) != XF(OF)) \ |
477 | COND(7e, 7f, XF(ZF) || XF(SF) != XF(OF)) | ||
474 | 478 | ||
475 | autask = ¤t->utask->autask; | 479 | #define COND(op_y, op_n, expr) \ |
476 | autask->saved_trap_nr = current->thread.trap_nr; | 480 | case 0x ## op_y: DO((expr) != 0) \ |
477 | current->thread.trap_nr = UPROBE_TRAP_NR; | 481 | case 0x ## op_n: DO((expr) == 0) |
478 | regs->ip = current->utask->xol_vaddr; | ||
479 | pre_xol_rip_insn(auprobe, regs, autask); | ||
480 | 482 | ||
481 | autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF); | 483 | #define XF(xf) (!!(flags & X86_EFLAGS_ ## xf)) |
482 | regs->flags |= X86_EFLAGS_TF; | ||
483 | if (test_tsk_thread_flag(current, TIF_BLOCKSTEP)) | ||
484 | set_task_blockstep(current, false); | ||
485 | 484 | ||
486 | return 0; | 485 | static bool is_cond_jmp_opcode(u8 opcode) |
486 | { | ||
487 | switch (opcode) { | ||
488 | #define DO(expr) \ | ||
489 | return true; | ||
490 | CASE_COND | ||
491 | #undef DO | ||
492 | |||
493 | default: | ||
494 | return false; | ||
495 | } | ||
487 | } | 496 | } |
488 | 497 | ||
489 | /* | 498 | static bool check_jmp_cond(struct arch_uprobe *auprobe, struct pt_regs *regs) |
490 | * This function is called by arch_uprobe_post_xol() to adjust the return | ||
491 | * address pushed by a call instruction executed out of line. | ||
492 | */ | ||
493 | static int adjust_ret_addr(unsigned long sp, long correction) | ||
494 | { | 499 | { |
495 | int rasize, ncopied; | 500 | unsigned long flags = regs->flags; |
496 | long ra = 0; | ||
497 | 501 | ||
498 | if (is_ia32_task()) | 502 | switch (auprobe->branch.opc1) { |
499 | rasize = 4; | 503 | #define DO(expr) \ |
500 | else | 504 | return expr; |
501 | rasize = 8; | 505 | CASE_COND |
506 | #undef DO | ||
502 | 507 | ||
503 | ncopied = copy_from_user(&ra, (void __user *)sp, rasize); | 508 | default: /* not a conditional jmp */ |
504 | if (unlikely(ncopied)) | 509 | return true; |
505 | return -EFAULT; | 510 | } |
511 | } | ||
506 | 512 | ||
507 | ra += correction; | 513 | #undef XF |
508 | ncopied = copy_to_user((void __user *)sp, &ra, rasize); | 514 | #undef COND |
509 | if (unlikely(ncopied)) | 515 | #undef CASE_COND |
510 | return -EFAULT; | ||
511 | 516 | ||
512 | return 0; | 517 | static bool branch_emulate_op(struct arch_uprobe *auprobe, struct pt_regs *regs) |
518 | { | ||
519 | unsigned long new_ip = regs->ip += auprobe->branch.ilen; | ||
520 | unsigned long offs = (long)auprobe->branch.offs; | ||
521 | |||
522 | if (branch_is_call(auprobe)) { | ||
523 | unsigned long new_sp = regs->sp - sizeof_long(); | ||
524 | /* | ||
525 | * If it fails we execute this (mangled, see the comment in | ||
526 | * branch_clear_offset) insn out-of-line. In the likely case | ||
527 | * this should trigger the trap, and the probed application | ||
528 | * should die or restart the same insn after it handles the | ||
529 | * signal, arch_uprobe_post_xol() won't be even called. | ||
530 | * | ||
531 | * But there is corner case, see the comment in ->post_xol(). | ||
532 | */ | ||
533 | if (copy_to_user((void __user *)new_sp, &new_ip, sizeof_long())) | ||
534 | return false; | ||
535 | regs->sp = new_sp; | ||
536 | } else if (!check_jmp_cond(auprobe, regs)) { | ||
537 | offs = 0; | ||
538 | } | ||
539 | |||
540 | regs->ip = new_ip + offs; | ||
541 | return true; | ||
513 | } | 542 | } |
514 | 543 | ||
515 | #ifdef CONFIG_X86_64 | 544 | static int branch_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs) |
516 | static bool is_riprel_insn(struct arch_uprobe *auprobe) | ||
517 | { | 545 | { |
518 | return ((auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) != 0); | 546 | BUG_ON(!branch_is_call(auprobe)); |
547 | /* | ||
548 | * We can only get here if branch_emulate_op() failed to push the ret | ||
549 | * address _and_ another thread expanded our stack before the (mangled) | ||
550 | * "call" insn was executed out-of-line. Just restore ->sp and restart. | ||
551 | * We could also restore ->ip and try to call branch_emulate_op() again. | ||
552 | */ | ||
553 | regs->sp += sizeof_long(); | ||
554 | return -ERESTART; | ||
519 | } | 555 | } |
520 | 556 | ||
521 | static void | 557 | static void branch_clear_offset(struct arch_uprobe *auprobe, struct insn *insn) |
522 | handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) | ||
523 | { | 558 | { |
524 | if (is_riprel_insn(auprobe)) { | 559 | /* |
525 | struct arch_uprobe_task *autask; | 560 | * Turn this insn into "call 1f; 1:", this is what we will execute |
561 | * out-of-line if ->emulate() fails. We only need this to generate | ||
562 | * a trap, so that the probed task receives the correct signal with | ||
563 | * the properly filled siginfo. | ||
564 | * | ||
565 | * But see the comment in ->post_xol(), in the unlikely case it can | ||
566 | * succeed. So we need to ensure that the new ->ip can not fall into | ||
567 | * the non-canonical area and trigger #GP. | ||
568 | * | ||
569 | * We could turn it into (say) "pushf", but then we would need to | ||
570 | * divorce ->insn[] and ->ixol[]. We need to preserve the 1st byte | ||
571 | * of ->insn[] for set_orig_insn(). | ||
572 | */ | ||
573 | memset(auprobe->insn + insn_offset_immediate(insn), | ||
574 | 0, insn->immediate.nbytes); | ||
575 | } | ||
526 | 576 | ||
527 | autask = ¤t->utask->autask; | 577 | static struct uprobe_xol_ops branch_xol_ops = { |
528 | if (auprobe->fixups & UPROBE_FIX_RIP_AX) | 578 | .emulate = branch_emulate_op, |
529 | regs->ax = autask->saved_scratch_register; | 579 | .post_xol = branch_post_xol_op, |
530 | else | 580 | }; |
531 | regs->cx = autask->saved_scratch_register; | 581 | |
582 | /* Returns -ENOSYS if branch_xol_ops doesn't handle this insn */ | ||
583 | static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn) | ||
584 | { | ||
585 | u8 opc1 = OPCODE1(insn); | ||
586 | |||
587 | /* has the side-effect of processing the entire instruction */ | ||
588 | insn_get_length(insn); | ||
589 | if (WARN_ON_ONCE(!insn_complete(insn))) | ||
590 | return -ENOEXEC; | ||
591 | |||
592 | switch (opc1) { | ||
593 | case 0xeb: /* jmp 8 */ | ||
594 | case 0xe9: /* jmp 32 */ | ||
595 | case 0x90: /* prefix* + nop; same as jmp with .offs = 0 */ | ||
596 | break; | ||
597 | |||
598 | case 0xe8: /* call relative */ | ||
599 | branch_clear_offset(auprobe, insn); | ||
600 | break; | ||
532 | 601 | ||
602 | case 0x0f: | ||
603 | if (insn->opcode.nbytes != 2) | ||
604 | return -ENOSYS; | ||
533 | /* | 605 | /* |
534 | * The original instruction includes a displacement, and so | 606 | * If it is a "near" conditional jmp, OPCODE2() - 0x10 matches |
535 | * is 4 bytes longer than what we've just single-stepped. | 607 | * OPCODE1() of the "short" jmp which checks the same condition. |
536 | * Fall through to handle stuff like "jmpq *...(%rip)" and | ||
537 | * "callq *...(%rip)". | ||
538 | */ | 608 | */ |
539 | if (correction) | 609 | opc1 = OPCODE2(insn) - 0x10; |
540 | *correction += 4; | 610 | default: |
611 | if (!is_cond_jmp_opcode(opc1)) | ||
612 | return -ENOSYS; | ||
541 | } | 613 | } |
614 | |||
615 | auprobe->branch.opc1 = opc1; | ||
616 | auprobe->branch.ilen = insn->length; | ||
617 | auprobe->branch.offs = insn->immediate.value; | ||
618 | |||
619 | auprobe->ops = &branch_xol_ops; | ||
620 | return 0; | ||
542 | } | 621 | } |
543 | #else | 622 | |
544 | static void | 623 | /** |
545 | handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) | 624 | * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. |
625 | * @mm: the probed address space. | ||
626 | * @arch_uprobe: the probepoint information. | ||
627 | * @addr: virtual address at which to install the probepoint | ||
628 | * Return 0 on success or a -ve number on error. | ||
629 | */ | ||
630 | int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr) | ||
631 | { | ||
632 | struct insn insn; | ||
633 | bool fix_ip = true, fix_call = false; | ||
634 | int ret; | ||
635 | |||
636 | ret = validate_insn_bits(auprobe, mm, &insn); | ||
637 | if (ret) | ||
638 | return ret; | ||
639 | |||
640 | ret = branch_setup_xol_ops(auprobe, &insn); | ||
641 | if (ret != -ENOSYS) | ||
642 | return ret; | ||
643 | |||
644 | /* | ||
645 | * Figure out which fixups arch_uprobe_post_xol() will need to perform, | ||
646 | * and annotate arch_uprobe->fixups accordingly. To start with, ->fixups | ||
647 | * is either zero or it reflects rip-related fixups. | ||
648 | */ | ||
649 | switch (OPCODE1(&insn)) { | ||
650 | case 0x9d: /* popf */ | ||
651 | auprobe->fixups |= UPROBE_FIX_SETF; | ||
652 | break; | ||
653 | case 0xc3: /* ret or lret -- ip is correct */ | ||
654 | case 0xcb: | ||
655 | case 0xc2: | ||
656 | case 0xca: | ||
657 | fix_ip = false; | ||
658 | break; | ||
659 | case 0x9a: /* call absolute - Fix return addr, not ip */ | ||
660 | fix_call = true; | ||
661 | fix_ip = false; | ||
662 | break; | ||
663 | case 0xea: /* jmp absolute -- ip is correct */ | ||
664 | fix_ip = false; | ||
665 | break; | ||
666 | case 0xff: | ||
667 | insn_get_modrm(&insn); | ||
668 | switch (MODRM_REG(&insn)) { | ||
669 | case 2: case 3: /* call or lcall, indirect */ | ||
670 | fix_call = true; | ||
671 | case 4: case 5: /* jmp or ljmp, indirect */ | ||
672 | fix_ip = false; | ||
673 | } | ||
674 | /* fall through */ | ||
675 | default: | ||
676 | handle_riprel_insn(auprobe, &insn); | ||
677 | } | ||
678 | |||
679 | if (fix_ip) | ||
680 | auprobe->fixups |= UPROBE_FIX_IP; | ||
681 | if (fix_call) | ||
682 | auprobe->fixups |= UPROBE_FIX_CALL; | ||
683 | |||
684 | auprobe->ops = &default_xol_ops; | ||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | /* | ||
689 | * arch_uprobe_pre_xol - prepare to execute out of line. | ||
690 | * @auprobe: the probepoint information. | ||
691 | * @regs: reflects the saved user state of current task. | ||
692 | */ | ||
693 | int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
546 | { | 694 | { |
547 | /* No RIP-relative addressing on 32-bit */ | 695 | struct uprobe_task *utask = current->utask; |
696 | |||
697 | regs->ip = utask->xol_vaddr; | ||
698 | utask->autask.saved_trap_nr = current->thread.trap_nr; | ||
699 | current->thread.trap_nr = UPROBE_TRAP_NR; | ||
700 | |||
701 | utask->autask.saved_tf = !!(regs->flags & X86_EFLAGS_TF); | ||
702 | regs->flags |= X86_EFLAGS_TF; | ||
703 | if (test_tsk_thread_flag(current, TIF_BLOCKSTEP)) | ||
704 | set_task_blockstep(current, false); | ||
705 | |||
706 | if (auprobe->ops->pre_xol) | ||
707 | return auprobe->ops->pre_xol(auprobe, regs); | ||
708 | return 0; | ||
548 | } | 709 | } |
549 | #endif | ||
550 | 710 | ||
551 | /* | 711 | /* |
552 | * If xol insn itself traps and generates a signal(Say, | 712 | * If xol insn itself traps and generates a signal(Say, |
@@ -592,22 +752,25 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t) | |||
592 | */ | 752 | */ |
593 | int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | 753 | int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) |
594 | { | 754 | { |
595 | struct uprobe_task *utask; | 755 | struct uprobe_task *utask = current->utask; |
596 | long correction; | ||
597 | int result = 0; | ||
598 | 756 | ||
599 | WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); | 757 | WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); |
600 | 758 | ||
601 | utask = current->utask; | 759 | if (auprobe->ops->post_xol) { |
602 | current->thread.trap_nr = utask->autask.saved_trap_nr; | 760 | int err = auprobe->ops->post_xol(auprobe, regs); |
603 | correction = (long)(utask->vaddr - utask->xol_vaddr); | 761 | if (err) { |
604 | handle_riprel_post_xol(auprobe, regs, &correction); | 762 | arch_uprobe_abort_xol(auprobe, regs); |
605 | if (auprobe->fixups & UPROBE_FIX_IP) | 763 | /* |
606 | regs->ip += correction; | 764 | * Restart the probed insn. ->post_xol() must ensure |
607 | 765 | * this is really possible if it returns -ERESTART. | |
608 | if (auprobe->fixups & UPROBE_FIX_CALL) | 766 | */ |
609 | result = adjust_ret_addr(regs->sp, correction); | 767 | if (err == -ERESTART) |
768 | return 0; | ||
769 | return err; | ||
770 | } | ||
771 | } | ||
610 | 772 | ||
773 | current->thread.trap_nr = utask->autask.saved_trap_nr; | ||
611 | /* | 774 | /* |
612 | * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP | 775 | * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP |
613 | * so we can get an extra SIGTRAP if we do not clear TF. We need | 776 | * so we can get an extra SIGTRAP if we do not clear TF. We need |
@@ -618,7 +781,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
618 | else if (!(auprobe->fixups & UPROBE_FIX_SETF)) | 781 | else if (!(auprobe->fixups & UPROBE_FIX_SETF)) |
619 | regs->flags &= ~X86_EFLAGS_TF; | 782 | regs->flags &= ~X86_EFLAGS_TF; |
620 | 783 | ||
621 | return result; | 784 | return 0; |
622 | } | 785 | } |
623 | 786 | ||
624 | /* callback routine for handling exceptions. */ | 787 | /* callback routine for handling exceptions. */ |
@@ -652,8 +815,9 @@ int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, | |||
652 | 815 | ||
653 | /* | 816 | /* |
654 | * This function gets called when XOL instruction either gets trapped or | 817 | * This function gets called when XOL instruction either gets trapped or |
655 | * the thread has a fatal signal, so reset the instruction pointer to its | 818 | * the thread has a fatal signal, or if arch_uprobe_post_xol() failed. |
656 | * probed address. | 819 | * Reset the instruction pointer to its probed address for the potential |
820 | * restart or for post mortem analysis. | ||
657 | */ | 821 | */ |
658 | void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | 822 | void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) |
659 | { | 823 | { |
@@ -668,25 +832,10 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
668 | regs->flags &= ~X86_EFLAGS_TF; | 832 | regs->flags &= ~X86_EFLAGS_TF; |
669 | } | 833 | } |
670 | 834 | ||
671 | /* | ||
672 | * Skip these instructions as per the currently known x86 ISA. | ||
673 | * rep=0x66*; nop=0x90 | ||
674 | */ | ||
675 | static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | 835 | static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) |
676 | { | 836 | { |
677 | int i; | 837 | if (auprobe->ops->emulate) |
678 | 838 | return auprobe->ops->emulate(auprobe, regs); | |
679 | for (i = 0; i < MAX_UINSN_BYTES; i++) { | ||
680 | if (auprobe->insn[i] == 0x66) | ||
681 | continue; | ||
682 | |||
683 | if (auprobe->insn[i] == 0x90) { | ||
684 | regs->ip += i + 1; | ||
685 | return true; | ||
686 | } | ||
687 | |||
688 | break; | ||
689 | } | ||
690 | return false; | 839 | return false; |
691 | } | 840 | } |
692 | 841 | ||
@@ -701,23 +850,21 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
701 | unsigned long | 850 | unsigned long |
702 | arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs) | 851 | arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs) |
703 | { | 852 | { |
704 | int rasize, ncopied; | 853 | int rasize = sizeof_long(), nleft; |
705 | unsigned long orig_ret_vaddr = 0; /* clear high bits for 32-bit apps */ | 854 | unsigned long orig_ret_vaddr = 0; /* clear high bits for 32-bit apps */ |
706 | 855 | ||
707 | rasize = is_ia32_task() ? 4 : 8; | 856 | if (copy_from_user(&orig_ret_vaddr, (void __user *)regs->sp, rasize)) |
708 | ncopied = copy_from_user(&orig_ret_vaddr, (void __user *)regs->sp, rasize); | ||
709 | if (unlikely(ncopied)) | ||
710 | return -1; | 857 | return -1; |
711 | 858 | ||
712 | /* check whether address has been already hijacked */ | 859 | /* check whether address has been already hijacked */ |
713 | if (orig_ret_vaddr == trampoline_vaddr) | 860 | if (orig_ret_vaddr == trampoline_vaddr) |
714 | return orig_ret_vaddr; | 861 | return orig_ret_vaddr; |
715 | 862 | ||
716 | ncopied = copy_to_user((void __user *)regs->sp, &trampoline_vaddr, rasize); | 863 | nleft = copy_to_user((void __user *)regs->sp, &trampoline_vaddr, rasize); |
717 | if (likely(!ncopied)) | 864 | if (likely(!nleft)) |
718 | return orig_ret_vaddr; | 865 | return orig_ret_vaddr; |
719 | 866 | ||
720 | if (ncopied != rasize) { | 867 | if (nleft != rasize) { |
721 | pr_err("uprobe: return address clobbered: pid=%d, %%sp=%#lx, " | 868 | pr_err("uprobe: return address clobbered: pid=%d, %%sp=%#lx, " |
722 | "%%ip=%#lx\n", current->pid, regs->sp, regs->ip); | 869 | "%%ip=%#lx\n", current->pid, regs->sp, regs->ip); |
723 | 870 | ||
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3ef6ea12806a..a9209118d80f 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -172,6 +172,7 @@ struct perf_event; | |||
172 | struct pmu { | 172 | struct pmu { |
173 | struct list_head entry; | 173 | struct list_head entry; |
174 | 174 | ||
175 | struct module *module; | ||
175 | struct device *dev; | 176 | struct device *dev; |
176 | const struct attribute_group **attr_groups; | 177 | const struct attribute_group **attr_groups; |
177 | const char *name; | 178 | const char *name; |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 853bc1ccb395..e3fc8f09d110 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
@@ -722,10 +722,10 @@ enum perf_callchain_context { | |||
722 | PERF_CONTEXT_MAX = (__u64)-4095, | 722 | PERF_CONTEXT_MAX = (__u64)-4095, |
723 | }; | 723 | }; |
724 | 724 | ||
725 | #define PERF_FLAG_FD_NO_GROUP (1U << 0) | 725 | #define PERF_FLAG_FD_NO_GROUP (1UL << 0) |
726 | #define PERF_FLAG_FD_OUTPUT (1U << 1) | 726 | #define PERF_FLAG_FD_OUTPUT (1UL << 1) |
727 | #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ | 727 | #define PERF_FLAG_PID_CGROUP (1UL << 2) /* pid=cgroup id, per-cpu mode only */ |
728 | #define PERF_FLAG_FD_CLOEXEC (1U << 3) /* O_CLOEXEC */ | 728 | #define PERF_FLAG_FD_CLOEXEC (1UL << 3) /* O_CLOEXEC */ |
729 | 729 | ||
730 | union perf_mem_data_src { | 730 | union perf_mem_data_src { |
731 | __u64 val; | 731 | __u64 val; |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 440eefc67397..689237a0c5e8 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/hw_breakpoint.h> | 39 | #include <linux/hw_breakpoint.h> |
40 | #include <linux/mm_types.h> | 40 | #include <linux/mm_types.h> |
41 | #include <linux/cgroup.h> | 41 | #include <linux/cgroup.h> |
42 | #include <linux/module.h> | ||
42 | 43 | ||
43 | #include "internal.h" | 44 | #include "internal.h" |
44 | 45 | ||
@@ -1677,6 +1678,8 @@ event_sched_in(struct perf_event *event, | |||
1677 | u64 tstamp = perf_event_time(event); | 1678 | u64 tstamp = perf_event_time(event); |
1678 | int ret = 0; | 1679 | int ret = 0; |
1679 | 1680 | ||
1681 | lockdep_assert_held(&ctx->lock); | ||
1682 | |||
1680 | if (event->state <= PERF_EVENT_STATE_OFF) | 1683 | if (event->state <= PERF_EVENT_STATE_OFF) |
1681 | return 0; | 1684 | return 0; |
1682 | 1685 | ||
@@ -3244,9 +3247,13 @@ static void __free_event(struct perf_event *event) | |||
3244 | if (event->ctx) | 3247 | if (event->ctx) |
3245 | put_ctx(event->ctx); | 3248 | put_ctx(event->ctx); |
3246 | 3249 | ||
3250 | if (event->pmu) | ||
3251 | module_put(event->pmu->module); | ||
3252 | |||
3247 | call_rcu(&event->rcu_head, free_event_rcu); | 3253 | call_rcu(&event->rcu_head, free_event_rcu); |
3248 | } | 3254 | } |
3249 | static void free_event(struct perf_event *event) | 3255 | |
3256 | static void _free_event(struct perf_event *event) | ||
3250 | { | 3257 | { |
3251 | irq_work_sync(&event->pending); | 3258 | irq_work_sync(&event->pending); |
3252 | 3259 | ||
@@ -3267,42 +3274,31 @@ static void free_event(struct perf_event *event) | |||
3267 | if (is_cgroup_event(event)) | 3274 | if (is_cgroup_event(event)) |
3268 | perf_detach_cgroup(event); | 3275 | perf_detach_cgroup(event); |
3269 | 3276 | ||
3270 | |||
3271 | __free_event(event); | 3277 | __free_event(event); |
3272 | } | 3278 | } |
3273 | 3279 | ||
3274 | int perf_event_release_kernel(struct perf_event *event) | 3280 | /* |
3281 | * Used to free events which have a known refcount of 1, such as in error paths | ||
3282 | * where the event isn't exposed yet and inherited events. | ||
3283 | */ | ||
3284 | static void free_event(struct perf_event *event) | ||
3275 | { | 3285 | { |
3276 | struct perf_event_context *ctx = event->ctx; | 3286 | if (WARN(atomic_long_cmpxchg(&event->refcount, 1, 0) != 1, |
3277 | 3287 | "unexpected event refcount: %ld; ptr=%p\n", | |
3278 | WARN_ON_ONCE(ctx->parent_ctx); | 3288 | atomic_long_read(&event->refcount), event)) { |
3279 | /* | 3289 | /* leak to avoid use-after-free */ |
3280 | * There are two ways this annotation is useful: | 3290 | return; |
3281 | * | 3291 | } |
3282 | * 1) there is a lock recursion from perf_event_exit_task | ||
3283 | * see the comment there. | ||
3284 | * | ||
3285 | * 2) there is a lock-inversion with mmap_sem through | ||
3286 | * perf_event_read_group(), which takes faults while | ||
3287 | * holding ctx->mutex, however this is called after | ||
3288 | * the last filedesc died, so there is no possibility | ||
3289 | * to trigger the AB-BA case. | ||
3290 | */ | ||
3291 | mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); | ||
3292 | perf_remove_from_context(event, true); | ||
3293 | mutex_unlock(&ctx->mutex); | ||
3294 | |||
3295 | free_event(event); | ||
3296 | 3292 | ||
3297 | return 0; | 3293 | _free_event(event); |
3298 | } | 3294 | } |
3299 | EXPORT_SYMBOL_GPL(perf_event_release_kernel); | ||
3300 | 3295 | ||
3301 | /* | 3296 | /* |
3302 | * Called when the last reference to the file is gone. | 3297 | * Called when the last reference to the file is gone. |
3303 | */ | 3298 | */ |
3304 | static void put_event(struct perf_event *event) | 3299 | static void put_event(struct perf_event *event) |
3305 | { | 3300 | { |
3301 | struct perf_event_context *ctx = event->ctx; | ||
3306 | struct task_struct *owner; | 3302 | struct task_struct *owner; |
3307 | 3303 | ||
3308 | if (!atomic_long_dec_and_test(&event->refcount)) | 3304 | if (!atomic_long_dec_and_test(&event->refcount)) |
@@ -3341,9 +3337,33 @@ static void put_event(struct perf_event *event) | |||
3341 | put_task_struct(owner); | 3337 | put_task_struct(owner); |
3342 | } | 3338 | } |
3343 | 3339 | ||
3344 | perf_event_release_kernel(event); | 3340 | WARN_ON_ONCE(ctx->parent_ctx); |
3341 | /* | ||
3342 | * There are two ways this annotation is useful: | ||
3343 | * | ||
3344 | * 1) there is a lock recursion from perf_event_exit_task | ||
3345 | * see the comment there. | ||
3346 | * | ||
3347 | * 2) there is a lock-inversion with mmap_sem through | ||
3348 | * perf_event_read_group(), which takes faults while | ||
3349 | * holding ctx->mutex, however this is called after | ||
3350 | * the last filedesc died, so there is no possibility | ||
3351 | * to trigger the AB-BA case. | ||
3352 | */ | ||
3353 | mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); | ||
3354 | perf_remove_from_context(event, true); | ||
3355 | mutex_unlock(&ctx->mutex); | ||
3356 | |||
3357 | _free_event(event); | ||
3345 | } | 3358 | } |
3346 | 3359 | ||
3360 | int perf_event_release_kernel(struct perf_event *event) | ||
3361 | { | ||
3362 | put_event(event); | ||
3363 | return 0; | ||
3364 | } | ||
3365 | EXPORT_SYMBOL_GPL(perf_event_release_kernel); | ||
3366 | |||
3347 | static int perf_release(struct inode *inode, struct file *file) | 3367 | static int perf_release(struct inode *inode, struct file *file) |
3348 | { | 3368 | { |
3349 | put_event(file->private_data); | 3369 | put_event(file->private_data); |
@@ -6578,6 +6598,7 @@ free_pdc: | |||
6578 | free_percpu(pmu->pmu_disable_count); | 6598 | free_percpu(pmu->pmu_disable_count); |
6579 | goto unlock; | 6599 | goto unlock; |
6580 | } | 6600 | } |
6601 | EXPORT_SYMBOL_GPL(perf_pmu_register); | ||
6581 | 6602 | ||
6582 | void perf_pmu_unregister(struct pmu *pmu) | 6603 | void perf_pmu_unregister(struct pmu *pmu) |
6583 | { | 6604 | { |
@@ -6599,6 +6620,7 @@ void perf_pmu_unregister(struct pmu *pmu) | |||
6599 | put_device(pmu->dev); | 6620 | put_device(pmu->dev); |
6600 | free_pmu_context(pmu); | 6621 | free_pmu_context(pmu); |
6601 | } | 6622 | } |
6623 | EXPORT_SYMBOL_GPL(perf_pmu_unregister); | ||
6602 | 6624 | ||
6603 | struct pmu *perf_init_event(struct perf_event *event) | 6625 | struct pmu *perf_init_event(struct perf_event *event) |
6604 | { | 6626 | { |
@@ -6612,6 +6634,10 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
6612 | pmu = idr_find(&pmu_idr, event->attr.type); | 6634 | pmu = idr_find(&pmu_idr, event->attr.type); |
6613 | rcu_read_unlock(); | 6635 | rcu_read_unlock(); |
6614 | if (pmu) { | 6636 | if (pmu) { |
6637 | if (!try_module_get(pmu->module)) { | ||
6638 | pmu = ERR_PTR(-ENODEV); | ||
6639 | goto unlock; | ||
6640 | } | ||
6615 | event->pmu = pmu; | 6641 | event->pmu = pmu; |
6616 | ret = pmu->event_init(event); | 6642 | ret = pmu->event_init(event); |
6617 | if (ret) | 6643 | if (ret) |
@@ -6620,6 +6646,10 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
6620 | } | 6646 | } |
6621 | 6647 | ||
6622 | list_for_each_entry_rcu(pmu, &pmus, entry) { | 6648 | list_for_each_entry_rcu(pmu, &pmus, entry) { |
6649 | if (!try_module_get(pmu->module)) { | ||
6650 | pmu = ERR_PTR(-ENODEV); | ||
6651 | goto unlock; | ||
6652 | } | ||
6623 | event->pmu = pmu; | 6653 | event->pmu = pmu; |
6624 | ret = pmu->event_init(event); | 6654 | ret = pmu->event_init(event); |
6625 | if (!ret) | 6655 | if (!ret) |
@@ -6798,6 +6828,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, | |||
6798 | err_pmu: | 6828 | err_pmu: |
6799 | if (event->destroy) | 6829 | if (event->destroy) |
6800 | event->destroy(event); | 6830 | event->destroy(event); |
6831 | module_put(pmu->module); | ||
6801 | err_ns: | 6832 | err_ns: |
6802 | if (event->ns) | 6833 | if (event->ns) |
6803 | put_pid_ns(event->ns); | 6834 | put_pid_ns(event->ns); |
@@ -7067,20 +7098,26 @@ SYSCALL_DEFINE5(perf_event_open, | |||
7067 | } | 7098 | } |
7068 | } | 7099 | } |
7069 | 7100 | ||
7101 | if (task && group_leader && | ||
7102 | group_leader->attr.inherit != attr.inherit) { | ||
7103 | err = -EINVAL; | ||
7104 | goto err_task; | ||
7105 | } | ||
7106 | |||
7070 | get_online_cpus(); | 7107 | get_online_cpus(); |
7071 | 7108 | ||
7072 | event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, | 7109 | event = perf_event_alloc(&attr, cpu, task, group_leader, NULL, |
7073 | NULL, NULL); | 7110 | NULL, NULL); |
7074 | if (IS_ERR(event)) { | 7111 | if (IS_ERR(event)) { |
7075 | err = PTR_ERR(event); | 7112 | err = PTR_ERR(event); |
7076 | goto err_task; | 7113 | goto err_cpus; |
7077 | } | 7114 | } |
7078 | 7115 | ||
7079 | if (flags & PERF_FLAG_PID_CGROUP) { | 7116 | if (flags & PERF_FLAG_PID_CGROUP) { |
7080 | err = perf_cgroup_connect(pid, event, &attr, group_leader); | 7117 | err = perf_cgroup_connect(pid, event, &attr, group_leader); |
7081 | if (err) { | 7118 | if (err) { |
7082 | __free_event(event); | 7119 | __free_event(event); |
7083 | goto err_task; | 7120 | goto err_cpus; |
7084 | } | 7121 | } |
7085 | } | 7122 | } |
7086 | 7123 | ||
@@ -7242,8 +7279,9 @@ err_context: | |||
7242 | put_ctx(ctx); | 7279 | put_ctx(ctx); |
7243 | err_alloc: | 7280 | err_alloc: |
7244 | free_event(event); | 7281 | free_event(event); |
7245 | err_task: | 7282 | err_cpus: |
7246 | put_online_cpus(); | 7283 | put_online_cpus(); |
7284 | err_task: | ||
7247 | if (task) | 7285 | if (task) |
7248 | put_task_struct(task); | 7286 | put_task_struct(task); |
7249 | err_group_fd: | 7287 | err_group_fd: |
@@ -7379,7 +7417,7 @@ __perf_event_exit_task(struct perf_event *child_event, | |||
7379 | struct perf_event_context *child_ctx, | 7417 | struct perf_event_context *child_ctx, |
7380 | struct task_struct *child) | 7418 | struct task_struct *child) |
7381 | { | 7419 | { |
7382 | perf_remove_from_context(child_event, !!child_event->parent); | 7420 | perf_remove_from_context(child_event, true); |
7383 | 7421 | ||
7384 | /* | 7422 | /* |
7385 | * It can happen that the parent exits first, and has events | 7423 | * It can happen that the parent exits first, and has events |
@@ -7394,7 +7432,7 @@ __perf_event_exit_task(struct perf_event *child_event, | |||
7394 | 7432 | ||
7395 | static void perf_event_exit_task_context(struct task_struct *child, int ctxn) | 7433 | static void perf_event_exit_task_context(struct task_struct *child, int ctxn) |
7396 | { | 7434 | { |
7397 | struct perf_event *child_event, *tmp; | 7435 | struct perf_event *child_event; |
7398 | struct perf_event_context *child_ctx; | 7436 | struct perf_event_context *child_ctx; |
7399 | unsigned long flags; | 7437 | unsigned long flags; |
7400 | 7438 | ||
@@ -7448,24 +7486,9 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) | |||
7448 | */ | 7486 | */ |
7449 | mutex_lock(&child_ctx->mutex); | 7487 | mutex_lock(&child_ctx->mutex); |
7450 | 7488 | ||
7451 | again: | 7489 | list_for_each_entry_rcu(child_event, &child_ctx->event_list, event_entry) |
7452 | list_for_each_entry_safe(child_event, tmp, &child_ctx->pinned_groups, | ||
7453 | group_entry) | ||
7454 | __perf_event_exit_task(child_event, child_ctx, child); | ||
7455 | |||
7456 | list_for_each_entry_safe(child_event, tmp, &child_ctx->flexible_groups, | ||
7457 | group_entry) | ||
7458 | __perf_event_exit_task(child_event, child_ctx, child); | 7490 | __perf_event_exit_task(child_event, child_ctx, child); |
7459 | 7491 | ||
7460 | /* | ||
7461 | * If the last event was a group event, it will have appended all | ||
7462 | * its siblings to the list, but we obtained 'tmp' before that which | ||
7463 | * will still point to the list head terminating the iteration. | ||
7464 | */ | ||
7465 | if (!list_empty(&child_ctx->pinned_groups) || | ||
7466 | !list_empty(&child_ctx->flexible_groups)) | ||
7467 | goto again; | ||
7468 | |||
7469 | mutex_unlock(&child_ctx->mutex); | 7492 | mutex_unlock(&child_ctx->mutex); |
7470 | 7493 | ||
7471 | put_ctx(child_ctx); | 7494 | put_ctx(child_ctx); |
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 04709b66369d..d1edc5e6fd03 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -60,8 +60,6 @@ static struct percpu_rw_semaphore dup_mmap_sem; | |||
60 | 60 | ||
61 | /* Have a copy of original instruction */ | 61 | /* Have a copy of original instruction */ |
62 | #define UPROBE_COPY_INSN 0 | 62 | #define UPROBE_COPY_INSN 0 |
63 | /* Can skip singlestep */ | ||
64 | #define UPROBE_SKIP_SSTEP 1 | ||
65 | 63 | ||
66 | struct uprobe { | 64 | struct uprobe { |
67 | struct rb_node rb_node; /* node in the rb tree */ | 65 | struct rb_node rb_node; /* node in the rb tree */ |
@@ -491,12 +489,9 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) | |||
491 | uprobe->offset = offset; | 489 | uprobe->offset = offset; |
492 | init_rwsem(&uprobe->register_rwsem); | 490 | init_rwsem(&uprobe->register_rwsem); |
493 | init_rwsem(&uprobe->consumer_rwsem); | 491 | init_rwsem(&uprobe->consumer_rwsem); |
494 | /* For now assume that the instruction need not be single-stepped */ | ||
495 | __set_bit(UPROBE_SKIP_SSTEP, &uprobe->flags); | ||
496 | 492 | ||
497 | /* add to uprobes_tree, sorted on inode:offset */ | 493 | /* add to uprobes_tree, sorted on inode:offset */ |
498 | cur_uprobe = insert_uprobe(uprobe); | 494 | cur_uprobe = insert_uprobe(uprobe); |
499 | |||
500 | /* a uprobe exists for this inode:offset combination */ | 495 | /* a uprobe exists for this inode:offset combination */ |
501 | if (cur_uprobe) { | 496 | if (cur_uprobe) { |
502 | kfree(uprobe); | 497 | kfree(uprobe); |
@@ -1628,20 +1623,6 @@ bool uprobe_deny_signal(void) | |||
1628 | return true; | 1623 | return true; |
1629 | } | 1624 | } |
1630 | 1625 | ||
1631 | /* | ||
1632 | * Avoid singlestepping the original instruction if the original instruction | ||
1633 | * is a NOP or can be emulated. | ||
1634 | */ | ||
1635 | static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs) | ||
1636 | { | ||
1637 | if (test_bit(UPROBE_SKIP_SSTEP, &uprobe->flags)) { | ||
1638 | if (arch_uprobe_skip_sstep(&uprobe->arch, regs)) | ||
1639 | return true; | ||
1640 | clear_bit(UPROBE_SKIP_SSTEP, &uprobe->flags); | ||
1641 | } | ||
1642 | return false; | ||
1643 | } | ||
1644 | |||
1645 | static void mmf_recalc_uprobes(struct mm_struct *mm) | 1626 | static void mmf_recalc_uprobes(struct mm_struct *mm) |
1646 | { | 1627 | { |
1647 | struct vm_area_struct *vma; | 1628 | struct vm_area_struct *vma; |
@@ -1868,13 +1849,13 @@ static void handle_swbp(struct pt_regs *regs) | |||
1868 | 1849 | ||
1869 | handler_chain(uprobe, regs); | 1850 | handler_chain(uprobe, regs); |
1870 | 1851 | ||
1871 | if (can_skip_sstep(uprobe, regs)) | 1852 | if (arch_uprobe_skip_sstep(&uprobe->arch, regs)) |
1872 | goto out; | 1853 | goto out; |
1873 | 1854 | ||
1874 | if (!pre_ssout(uprobe, regs, bp_vaddr)) | 1855 | if (!pre_ssout(uprobe, regs, bp_vaddr)) |
1875 | return; | 1856 | return; |
1876 | 1857 | ||
1877 | /* can_skip_sstep() succeeded, or restart if can't singlestep */ | 1858 | /* arch_uprobe_skip_sstep() succeeded, or restart if can't singlestep */ |
1878 | out: | 1859 | out: |
1879 | put_uprobe(uprobe); | 1860 | put_uprobe(uprobe); |
1880 | } | 1861 | } |
@@ -1886,10 +1867,11 @@ out: | |||
1886 | static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) | 1867 | static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) |
1887 | { | 1868 | { |
1888 | struct uprobe *uprobe; | 1869 | struct uprobe *uprobe; |
1870 | int err = 0; | ||
1889 | 1871 | ||
1890 | uprobe = utask->active_uprobe; | 1872 | uprobe = utask->active_uprobe; |
1891 | if (utask->state == UTASK_SSTEP_ACK) | 1873 | if (utask->state == UTASK_SSTEP_ACK) |
1892 | arch_uprobe_post_xol(&uprobe->arch, regs); | 1874 | err = arch_uprobe_post_xol(&uprobe->arch, regs); |
1893 | else if (utask->state == UTASK_SSTEP_TRAPPED) | 1875 | else if (utask->state == UTASK_SSTEP_TRAPPED) |
1894 | arch_uprobe_abort_xol(&uprobe->arch, regs); | 1876 | arch_uprobe_abort_xol(&uprobe->arch, regs); |
1895 | else | 1877 | else |
@@ -1903,6 +1885,11 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) | |||
1903 | spin_lock_irq(¤t->sighand->siglock); | 1885 | spin_lock_irq(¤t->sighand->siglock); |
1904 | recalc_sigpending(); /* see uprobe_deny_signal() */ | 1886 | recalc_sigpending(); /* see uprobe_deny_signal() */ |
1905 | spin_unlock_irq(¤t->sighand->siglock); | 1887 | spin_unlock_irq(¤t->sighand->siglock); |
1888 | |||
1889 | if (unlikely(err)) { | ||
1890 | uprobe_warn(current, "execute the probed insn, sending SIGILL."); | ||
1891 | force_sig_info(SIGILL, SEND_SIG_FORCED, current); | ||
1892 | } | ||
1906 | } | 1893 | } |
1907 | 1894 | ||
1908 | /* | 1895 | /* |
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index e0501fe7140d..3ab28993f6e0 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -1039,6 +1039,7 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | |||
1039 | 1039 | ||
1040 | return ret; | 1040 | return ret; |
1041 | } | 1041 | } |
1042 | EXPORT_SYMBOL_GPL(__hrtimer_start_range_ns); | ||
1042 | 1043 | ||
1043 | /** | 1044 | /** |
1044 | * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU | 1045 | * hrtimer_start_range_ns - (re)start an hrtimer on the current CPU |
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h index fbc6665c6d53..88461f09cc86 100644 --- a/tools/include/linux/compiler.h +++ b/tools/include/linux/compiler.h | |||
@@ -35,4 +35,6 @@ | |||
35 | # define unlikely(x) __builtin_expect(!!(x), 0) | 35 | # define unlikely(x) __builtin_expect(!!(x), 0) |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) | ||
39 | |||
38 | #endif /* _TOOLS_LINUX_COMPILER_H */ | 40 | #endif /* _TOOLS_LINUX_COMPILER_H */ |
diff --git a/tools/virtio/linux/export.h b/tools/include/linux/export.h index 7311d326894a..d07e586b9ba0 100644 --- a/tools/virtio/linux/export.h +++ b/tools/include/linux/export.h | |||
@@ -1,5 +1,10 @@ | |||
1 | #ifndef _TOOLS_LINUX_EXPORT_H_ | ||
2 | #define _TOOLS_LINUX_EXPORT_H_ | ||
3 | |||
1 | #define EXPORT_SYMBOL(sym) | 4 | #define EXPORT_SYMBOL(sym) |
2 | #define EXPORT_SYMBOL_GPL(sym) | 5 | #define EXPORT_SYMBOL_GPL(sym) |
3 | #define EXPORT_SYMBOL_GPL_FUTURE(sym) | 6 | #define EXPORT_SYMBOL_GPL_FUTURE(sym) |
4 | #define EXPORT_UNUSED_SYMBOL(sym) | 7 | #define EXPORT_UNUSED_SYMBOL(sym) |
5 | #define EXPORT_UNUSED_SYMBOL_GPL(sym) | 8 | #define EXPORT_UNUSED_SYMBOL_GPL(sym) |
9 | |||
10 | #endif | ||
diff --git a/tools/lib/lockdep/uinclude/linux/types.h b/tools/include/linux/types.h index 929938f426de..b5cf25e05df2 100644 --- a/tools/lib/lockdep/uinclude/linux/types.h +++ b/tools/include/linux/types.h | |||
@@ -1,8 +1,9 @@ | |||
1 | #ifndef _LIBLOCKDEP_LINUX_TYPES_H_ | 1 | #ifndef _TOOLS_LINUX_TYPES_H_ |
2 | #define _LIBLOCKDEP_LINUX_TYPES_H_ | 2 | #define _TOOLS_LINUX_TYPES_H_ |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stddef.h> | 5 | #include <stddef.h> |
6 | #include <stdint.h> | ||
6 | 7 | ||
7 | #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ | 8 | #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ |
8 | #include <asm/types.h> | 9 | #include <asm/types.h> |
@@ -10,10 +11,22 @@ | |||
10 | struct page; | 11 | struct page; |
11 | struct kmem_cache; | 12 | struct kmem_cache; |
12 | 13 | ||
13 | typedef unsigned gfp_t; | 14 | typedef enum { |
15 | GFP_KERNEL, | ||
16 | GFP_ATOMIC, | ||
17 | __GFP_HIGHMEM, | ||
18 | __GFP_HIGH | ||
19 | } gfp_t; | ||
14 | 20 | ||
15 | typedef __u64 u64; | 21 | /* |
16 | typedef __s64 s64; | 22 | * We define u64 as uint64_t for every architecture |
23 | * so that we can print it with "%"PRIx64 without getting warnings. | ||
24 | * | ||
25 | * typedef __u64 u64; | ||
26 | * typedef __s64 s64; | ||
27 | */ | ||
28 | typedef uint64_t u64; | ||
29 | typedef int64_t s64; | ||
17 | 30 | ||
18 | typedef __u32 u32; | 31 | typedef __u32 u32; |
19 | typedef __s32 s32; | 32 | typedef __s32 s32; |
@@ -35,6 +48,10 @@ typedef __s8 s8; | |||
35 | #define __bitwise | 48 | #define __bitwise |
36 | #endif | 49 | #endif |
37 | 50 | ||
51 | #define __force | ||
52 | #define __user | ||
53 | #define __must_check | ||
54 | #define __cold | ||
38 | 55 | ||
39 | typedef __u16 __bitwise __le16; | 56 | typedef __u16 __bitwise __le16; |
40 | typedef __u16 __bitwise __be16; | 57 | typedef __u16 __bitwise __be16; |
@@ -55,4 +72,4 @@ struct hlist_node { | |||
55 | struct hlist_node *next, **pprev; | 72 | struct hlist_node *next, **pprev; |
56 | }; | 73 | }; |
57 | 74 | ||
58 | #endif | 75 | #endif /* _TOOLS_LINUX_TYPES_H_ */ |
diff --git a/tools/lib/lockdep/Makefile b/tools/lib/lockdep/Makefile index bba2f5253b6e..52f9279c6c13 100644 --- a/tools/lib/lockdep/Makefile +++ b/tools/lib/lockdep/Makefile | |||
@@ -104,7 +104,7 @@ N = | |||
104 | 104 | ||
105 | export Q VERBOSE | 105 | export Q VERBOSE |
106 | 106 | ||
107 | INCLUDES = -I. -I/usr/local/include -I./uinclude -I./include $(CONFIG_INCLUDES) | 107 | INCLUDES = -I. -I/usr/local/include -I./uinclude -I./include -I../../include $(CONFIG_INCLUDES) |
108 | 108 | ||
109 | # Set compile option CFLAGS if not set elsewhere | 109 | # Set compile option CFLAGS if not set elsewhere |
110 | CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g | 110 | CFLAGS ?= -g -DCONFIG_LOCKDEP -DCONFIG_STACKTRACE -DCONFIG_PROVE_LOCKING -DBITS_PER_LONG=__WORDSIZE -DLIBLOCKDEP_VERSION='"$(LIBLOCKDEP_VERSION)"' -rdynamic -O0 -g |
diff --git a/tools/lib/lockdep/uinclude/linux/export.h b/tools/lib/lockdep/uinclude/linux/export.h deleted file mode 100644 index 6bdf3492c535..000000000000 --- a/tools/lib/lockdep/uinclude/linux/export.h +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | #ifndef _LIBLOCKDEP_LINUX_EXPORT_H_ | ||
2 | #define _LIBLOCKDEP_LINUX_EXPORT_H_ | ||
3 | |||
4 | #define EXPORT_SYMBOL(sym) | ||
5 | #define EXPORT_SYMBOL_GPL(sym) | ||
6 | |||
7 | #endif | ||
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index fdfceee0ffd0..b3b8abae62b8 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -33,21 +33,25 @@ OPTIONS | |||
33 | -d:: | 33 | -d:: |
34 | --dsos=:: | 34 | --dsos=:: |
35 | Only consider symbols in these dsos. CSV that understands | 35 | Only consider symbols in these dsos. CSV that understands |
36 | file://filename entries. | 36 | file://filename entries. This option will affect the percentage |
37 | of the Baseline/Delta column. See --percentage for more info. | ||
37 | 38 | ||
38 | -C:: | 39 | -C:: |
39 | --comms=:: | 40 | --comms=:: |
40 | Only consider symbols in these comms. CSV that understands | 41 | Only consider symbols in these comms. CSV that understands |
41 | file://filename entries. | 42 | file://filename entries. This option will affect the percentage |
43 | of the Baseline/Delta column. See --percentage for more info. | ||
42 | 44 | ||
43 | -S:: | 45 | -S:: |
44 | --symbols=:: | 46 | --symbols=:: |
45 | Only consider these symbols. CSV that understands | 47 | Only consider these symbols. CSV that understands |
46 | file://filename entries. | 48 | file://filename entries. This option will affect the percentage |
49 | of the Baseline/Delta column. See --percentage for more info. | ||
47 | 50 | ||
48 | -s:: | 51 | -s:: |
49 | --sort=:: | 52 | --sort=:: |
50 | Sort by key(s): pid, comm, dso, symbol. | 53 | Sort by key(s): pid, comm, dso, symbol, cpu, parent, srcline. |
54 | Please see description of --sort in the perf-report man page. | ||
51 | 55 | ||
52 | -t:: | 56 | -t:: |
53 | --field-separator=:: | 57 | --field-separator=:: |
@@ -89,6 +93,14 @@ OPTIONS | |||
89 | --order:: | 93 | --order:: |
90 | Specify compute sorting column number. | 94 | Specify compute sorting column number. |
91 | 95 | ||
96 | --percentage:: | ||
97 | Determine how to display the overhead percentage of filtered entries. | ||
98 | Filters can be applied by --comms, --dsos and/or --symbols options. | ||
99 | |||
100 | "relative" means it's relative to filtered entries only so that the | ||
101 | sum of shown entries will be always 100%. "absolute" means it retains | ||
102 | the original value before and after the filter is applied. | ||
103 | |||
92 | COMPARISON | 104 | COMPARISON |
93 | ---------- | 105 | ---------- |
94 | The comparison is governed by the baseline file. The baseline perf.data | 106 | The comparison is governed by the baseline file. The baseline perf.data |
@@ -157,6 +169,10 @@ with: | |||
157 | - period_percent being the % of the hist entry period value within | 169 | - period_percent being the % of the hist entry period value within |
158 | single data file | 170 | single data file |
159 | 171 | ||
172 | - with filtering by -C, -d and/or -S, period_percent might be changed | ||
173 | relative to how entries are filtered. Use --percentage=absolute to | ||
174 | prevent such fluctuation. | ||
175 | |||
160 | ratio | 176 | ratio |
161 | ~~~~~ | 177 | ~~~~~ |
162 | If specified the 'Ratio' column is displayed with value 'r' computed as: | 178 | If specified the 'Ratio' column is displayed with value 'r' computed as: |
@@ -187,4 +203,4 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as: | |||
187 | 203 | ||
188 | SEE ALSO | 204 | SEE ALSO |
189 | -------- | 205 | -------- |
190 | linkperf:perf-record[1] | 206 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8eab8a4bdeb8..a1b5185402d5 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -25,10 +25,6 @@ OPTIONS | |||
25 | --verbose:: | 25 | --verbose:: |
26 | Be more verbose. (show symbol address, etc) | 26 | Be more verbose. (show symbol address, etc) |
27 | 27 | ||
28 | -d:: | ||
29 | --dsos=:: | ||
30 | Only consider symbols in these dsos. CSV that understands | ||
31 | file://filename entries. | ||
32 | -n:: | 28 | -n:: |
33 | --show-nr-samples:: | 29 | --show-nr-samples:: |
34 | Show the number of samples for each symbol | 30 | Show the number of samples for each symbol |
@@ -42,11 +38,18 @@ OPTIONS | |||
42 | -c:: | 38 | -c:: |
43 | --comms=:: | 39 | --comms=:: |
44 | Only consider symbols in these comms. CSV that understands | 40 | Only consider symbols in these comms. CSV that understands |
45 | file://filename entries. | 41 | file://filename entries. This option will affect the percentage of |
42 | the overhead column. See --percentage for more info. | ||
43 | -d:: | ||
44 | --dsos=:: | ||
45 | Only consider symbols in these dsos. CSV that understands | ||
46 | file://filename entries. This option will affect the percentage of | ||
47 | the overhead column. See --percentage for more info. | ||
46 | -S:: | 48 | -S:: |
47 | --symbols=:: | 49 | --symbols=:: |
48 | Only consider these symbols. CSV that understands | 50 | Only consider these symbols. CSV that understands |
49 | file://filename entries. | 51 | file://filename entries. This option will affect the percentage of |
52 | the overhead column. See --percentage for more info. | ||
50 | 53 | ||
51 | --symbol-filter=:: | 54 | --symbol-filter=:: |
52 | Only show symbols that match (partially) with this filter. | 55 | Only show symbols that match (partially) with this filter. |
@@ -76,6 +79,15 @@ OPTIONS | |||
76 | abort cost. This is the global weight. | 79 | abort cost. This is the global weight. |
77 | - local_weight: Local weight version of the weight above. | 80 | - local_weight: Local weight version of the weight above. |
78 | - transaction: Transaction abort flags. | 81 | - transaction: Transaction abort flags. |
82 | - overhead: Overhead percentage of sample | ||
83 | - overhead_sys: Overhead percentage of sample running in system mode | ||
84 | - overhead_us: Overhead percentage of sample running in user mode | ||
85 | - overhead_guest_sys: Overhead percentage of sample running in system mode | ||
86 | on guest machine | ||
87 | - overhead_guest_us: Overhead percentage of sample running in user mode on | ||
88 | guest machine | ||
89 | - sample: Number of sample | ||
90 | - period: Raw number of event count of sample | ||
79 | 91 | ||
80 | By default, comm, dso and symbol keys are used. | 92 | By default, comm, dso and symbol keys are used. |
81 | (i.e. --sort comm,dso,symbol) | 93 | (i.e. --sort comm,dso,symbol) |
@@ -95,6 +107,16 @@ OPTIONS | |||
95 | And default sort keys are changed to comm, dso_from, symbol_from, dso_to | 107 | And default sort keys are changed to comm, dso_from, symbol_from, dso_to |
96 | and symbol_to, see '--branch-stack'. | 108 | and symbol_to, see '--branch-stack'. |
97 | 109 | ||
110 | -F:: | ||
111 | --fields=:: | ||
112 | Specify output field - multiple keys can be specified in CSV format. | ||
113 | Following fields are available: | ||
114 | overhead, overhead_sys, overhead_us, sample and period. | ||
115 | Also it can contain any sort key(s). | ||
116 | |||
117 | By default, every sort keys not specified in -F will be appended | ||
118 | automatically. | ||
119 | |||
98 | -p:: | 120 | -p:: |
99 | --parent=<regex>:: | 121 | --parent=<regex>:: |
100 | A regex filter to identify parent. The parent is a caller of this | 122 | A regex filter to identify parent. The parent is a caller of this |
@@ -237,6 +259,15 @@ OPTIONS | |||
237 | Do not show entries which have an overhead under that percent. | 259 | Do not show entries which have an overhead under that percent. |
238 | (Default: 0). | 260 | (Default: 0). |
239 | 261 | ||
262 | --percentage:: | ||
263 | Determine how to display the overhead percentage of filtered entries. | ||
264 | Filters can be applied by --comms, --dsos and/or --symbols options and | ||
265 | Zoom operations on the TUI (thread, dso, etc). | ||
266 | |||
267 | "relative" means it's relative to filtered entries only so that the | ||
268 | sum of shown entries will be always 100%. "absolute" means it retains | ||
269 | the original value before and after the filter is applied. | ||
270 | |||
240 | --header:: | 271 | --header:: |
241 | Show header information in the perf.data file. This includes | 272 | Show header information in the perf.data file. This includes |
242 | various information like hostname, OS and perf version, cpu/mem | 273 | various information like hostname, OS and perf version, cpu/mem |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 976b00c6cdb1..dcfa54c851e9 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -113,7 +113,17 @@ Default is to monitor all CPUS. | |||
113 | -s:: | 113 | -s:: |
114 | --sort:: | 114 | --sort:: |
115 | Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, | 115 | Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, |
116 | local_weight, abort, in_tx, transaction | 116 | local_weight, abort, in_tx, transaction, overhead, sample, period. |
117 | Please see description of --sort in the perf-report man page. | ||
118 | |||
119 | --fields=:: | ||
120 | Specify output field - multiple keys can be specified in CSV format. | ||
121 | Following fields are available: | ||
122 | overhead, overhead_sys, overhead_us, sample and period. | ||
123 | Also it can contain any sort key(s). | ||
124 | |||
125 | By default, every sort keys not specified in --field will be appended | ||
126 | automatically. | ||
117 | 127 | ||
118 | -n:: | 128 | -n:: |
119 | --show-nr-samples:: | 129 | --show-nr-samples:: |
@@ -123,13 +133,16 @@ Default is to monitor all CPUS. | |||
123 | Show a column with the sum of periods. | 133 | Show a column with the sum of periods. |
124 | 134 | ||
125 | --dsos:: | 135 | --dsos:: |
126 | Only consider symbols in these dsos. | 136 | Only consider symbols in these dsos. This option will affect the |
137 | percentage of the overhead column. See --percentage for more info. | ||
127 | 138 | ||
128 | --comms:: | 139 | --comms:: |
129 | Only consider symbols in these comms. | 140 | Only consider symbols in these comms. This option will affect the |
141 | percentage of the overhead column. See --percentage for more info. | ||
130 | 142 | ||
131 | --symbols:: | 143 | --symbols:: |
132 | Only consider these symbols. | 144 | Only consider these symbols. This option will affect the |
145 | percentage of the overhead column. See --percentage for more info. | ||
133 | 146 | ||
134 | -M:: | 147 | -M:: |
135 | --disassembler-style=:: Set disassembler style for objdump. | 148 | --disassembler-style=:: Set disassembler style for objdump. |
@@ -165,6 +178,15 @@ Default is to monitor all CPUS. | |||
165 | Do not show entries which have an overhead under that percent. | 178 | Do not show entries which have an overhead under that percent. |
166 | (Default: 0). | 179 | (Default: 0). |
167 | 180 | ||
181 | --percentage:: | ||
182 | Determine how to display the overhead percentage of filtered entries. | ||
183 | Filters can be applied by --comms, --dsos and/or --symbols options and | ||
184 | Zoom operations on the TUI (thread, dso, etc). | ||
185 | |||
186 | "relative" means it's relative to filtered entries only so that the | ||
187 | sum of shown entries will be always 100%. "absolute" means it retains | ||
188 | the original value before and after the filter is applied. | ||
189 | |||
168 | INTERACTIVE PROMPTING KEYS | 190 | INTERACTIVE PROMPTING KEYS |
169 | -------------------------- | 191 | -------------------------- |
170 | 192 | ||
@@ -200,4 +222,4 @@ Pressing any unmapped key displays a menu, and prompts for input. | |||
200 | 222 | ||
201 | SEE ALSO | 223 | SEE ALSO |
202 | -------- | 224 | -------- |
203 | linkperf:perf-stat[1], linkperf:perf-list[1] | 225 | linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-report[1] |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index c0c87c87b60f..45da209b6ed3 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -7,6 +7,8 @@ tools/lib/symbol/kallsyms.h | |||
7 | tools/include/asm/bug.h | 7 | tools/include/asm/bug.h |
8 | tools/include/linux/compiler.h | 8 | tools/include/linux/compiler.h |
9 | tools/include/linux/hash.h | 9 | tools/include/linux/hash.h |
10 | tools/include/linux/export.h | ||
11 | tools/include/linux/types.h | ||
10 | include/linux/const.h | 12 | include/linux/const.h |
11 | include/linux/perf_event.h | 13 | include/linux/perf_event.h |
12 | include/linux/rbtree.h | 14 | include/linux/rbtree.h |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 895edd32930c..02f0a4dd1a80 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -222,12 +222,12 @@ LIB_H += util/include/linux/const.h | |||
222 | LIB_H += util/include/linux/ctype.h | 222 | LIB_H += util/include/linux/ctype.h |
223 | LIB_H += util/include/linux/kernel.h | 223 | LIB_H += util/include/linux/kernel.h |
224 | LIB_H += util/include/linux/list.h | 224 | LIB_H += util/include/linux/list.h |
225 | LIB_H += util/include/linux/export.h | 225 | LIB_H += ../include/linux/export.h |
226 | LIB_H += util/include/linux/poison.h | 226 | LIB_H += util/include/linux/poison.h |
227 | LIB_H += util/include/linux/rbtree.h | 227 | LIB_H += util/include/linux/rbtree.h |
228 | LIB_H += util/include/linux/rbtree_augmented.h | 228 | LIB_H += util/include/linux/rbtree_augmented.h |
229 | LIB_H += util/include/linux/string.h | 229 | LIB_H += util/include/linux/string.h |
230 | LIB_H += util/include/linux/types.h | 230 | LIB_H += ../include/linux/types.h |
231 | LIB_H += util/include/linux/linkage.h | 231 | LIB_H += util/include/linux/linkage.h |
232 | LIB_H += util/include/asm/asm-offsets.h | 232 | LIB_H += util/include/asm/asm-offsets.h |
233 | LIB_H += ../include/asm/bug.h | 233 | LIB_H += ../include/asm/bug.h |
@@ -252,7 +252,6 @@ LIB_H += util/event.h | |||
252 | LIB_H += util/evsel.h | 252 | LIB_H += util/evsel.h |
253 | LIB_H += util/evlist.h | 253 | LIB_H += util/evlist.h |
254 | LIB_H += util/exec_cmd.h | 254 | LIB_H += util/exec_cmd.h |
255 | LIB_H += util/types.h | ||
256 | LIB_H += util/levenshtein.h | 255 | LIB_H += util/levenshtein.h |
257 | LIB_H += util/machine.h | 256 | LIB_H += util/machine.h |
258 | LIB_H += util/map.h | 257 | LIB_H += util/map.h |
@@ -397,7 +396,10 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o | |||
397 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | 396 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o |
398 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | 397 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o |
399 | LIB_OBJS += $(OUTPUT)tests/pmu.o | 398 | LIB_OBJS += $(OUTPUT)tests/pmu.o |
399 | LIB_OBJS += $(OUTPUT)tests/hists_common.o | ||
400 | LIB_OBJS += $(OUTPUT)tests/hists_link.o | 400 | LIB_OBJS += $(OUTPUT)tests/hists_link.o |
401 | LIB_OBJS += $(OUTPUT)tests/hists_filter.o | ||
402 | LIB_OBJS += $(OUTPUT)tests/hists_output.o | ||
401 | LIB_OBJS += $(OUTPUT)tests/python-use.o | 403 | LIB_OBJS += $(OUTPUT)tests/python-use.o |
402 | LIB_OBJS += $(OUTPUT)tests/bp_signal.o | 404 | LIB_OBJS += $(OUTPUT)tests/bp_signal.o |
403 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o | 405 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o |
@@ -410,10 +412,12 @@ LIB_OBJS += $(OUTPUT)tests/code-reading.o | |||
410 | LIB_OBJS += $(OUTPUT)tests/sample-parsing.o | 412 | LIB_OBJS += $(OUTPUT)tests/sample-parsing.o |
411 | LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o | 413 | LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o |
412 | ifndef NO_DWARF_UNWIND | 414 | ifndef NO_DWARF_UNWIND |
413 | ifeq ($(ARCH),x86) | 415 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm)) |
414 | LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o | 416 | LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o |
415 | endif | 417 | endif |
416 | endif | 418 | endif |
419 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o | ||
420 | LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o | ||
417 | 421 | ||
418 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 422 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
419 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 423 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
diff --git a/tools/perf/arch/arm/Makefile b/tools/perf/arch/arm/Makefile index 67e9b3d38e89..09d62153d384 100644 --- a/tools/perf/arch/arm/Makefile +++ b/tools/perf/arch/arm/Makefile | |||
@@ -5,3 +5,10 @@ endif | |||
5 | ifndef NO_LIBUNWIND | 5 | ifndef NO_LIBUNWIND |
6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o | 6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o |
7 | endif | 7 | endif |
8 | ifndef NO_LIBDW_DWARF_UNWIND | ||
9 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libdw.o | ||
10 | endif | ||
11 | ifndef NO_DWARF_UNWIND | ||
12 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/regs_load.o | ||
13 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/tests/dwarf-unwind.o | ||
14 | endif | ||
diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h index 2a1cfde66b69..f619c9c5a4bf 100644 --- a/tools/perf/arch/arm/include/perf_regs.h +++ b/tools/perf/arch/arm/include/perf_regs.h | |||
@@ -2,10 +2,15 @@ | |||
2 | #define ARCH_PERF_REGS_H | 2 | #define ARCH_PERF_REGS_H |
3 | 3 | ||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include "../../util/types.h" | 5 | #include <linux/types.h> |
6 | #include <asm/perf_regs.h> | 6 | #include <asm/perf_regs.h> |
7 | 7 | ||
8 | void perf_regs_load(u64 *regs); | ||
9 | |||
8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1) | 10 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM_MAX) - 1) |
11 | #define PERF_REGS_MAX PERF_REG_ARM_MAX | ||
12 | #define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32 | ||
13 | |||
9 | #define PERF_REG_IP PERF_REG_ARM_PC | 14 | #define PERF_REG_IP PERF_REG_ARM_PC |
10 | #define PERF_REG_SP PERF_REG_ARM_SP | 15 | #define PERF_REG_SP PERF_REG_ARM_SP |
11 | 16 | ||
diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c new file mode 100644 index 000000000000..9f870d27cb39 --- /dev/null +++ b/tools/perf/arch/arm/tests/dwarf-unwind.c | |||
@@ -0,0 +1,60 @@ | |||
1 | #include <string.h> | ||
2 | #include "perf_regs.h" | ||
3 | #include "thread.h" | ||
4 | #include "map.h" | ||
5 | #include "event.h" | ||
6 | #include "tests/tests.h" | ||
7 | |||
8 | #define STACK_SIZE 8192 | ||
9 | |||
10 | static int sample_ustack(struct perf_sample *sample, | ||
11 | struct thread *thread, u64 *regs) | ||
12 | { | ||
13 | struct stack_dump *stack = &sample->user_stack; | ||
14 | struct map *map; | ||
15 | unsigned long sp; | ||
16 | u64 stack_size, *buf; | ||
17 | |||
18 | buf = malloc(STACK_SIZE); | ||
19 | if (!buf) { | ||
20 | pr_debug("failed to allocate sample uregs data\n"); | ||
21 | return -1; | ||
22 | } | ||
23 | |||
24 | sp = (unsigned long) regs[PERF_REG_ARM_SP]; | ||
25 | |||
26 | map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp); | ||
27 | if (!map) { | ||
28 | pr_debug("failed to get stack map\n"); | ||
29 | free(buf); | ||
30 | return -1; | ||
31 | } | ||
32 | |||
33 | stack_size = map->end - sp; | ||
34 | stack_size = stack_size > STACK_SIZE ? STACK_SIZE : stack_size; | ||
35 | |||
36 | memcpy(buf, (void *) sp, stack_size); | ||
37 | stack->data = (char *) buf; | ||
38 | stack->size = stack_size; | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | int test__arch_unwind_sample(struct perf_sample *sample, | ||
43 | struct thread *thread) | ||
44 | { | ||
45 | struct regs_dump *regs = &sample->user_regs; | ||
46 | u64 *buf; | ||
47 | |||
48 | buf = calloc(1, sizeof(u64) * PERF_REGS_MAX); | ||
49 | if (!buf) { | ||
50 | pr_debug("failed to allocate sample uregs data\n"); | ||
51 | return -1; | ||
52 | } | ||
53 | |||
54 | perf_regs_load(buf); | ||
55 | regs->abi = PERF_SAMPLE_REGS_ABI; | ||
56 | regs->regs = buf; | ||
57 | regs->mask = PERF_REGS_MASK; | ||
58 | |||
59 | return sample_ustack(sample, thread, buf); | ||
60 | } | ||
diff --git a/tools/perf/arch/arm/tests/regs_load.S b/tools/perf/arch/arm/tests/regs_load.S new file mode 100644 index 000000000000..e09e983946fe --- /dev/null +++ b/tools/perf/arch/arm/tests/regs_load.S | |||
@@ -0,0 +1,58 @@ | |||
1 | #include <linux/linkage.h> | ||
2 | |||
3 | #define R0 0x00 | ||
4 | #define R1 0x08 | ||
5 | #define R2 0x10 | ||
6 | #define R3 0x18 | ||
7 | #define R4 0x20 | ||
8 | #define R5 0x28 | ||
9 | #define R6 0x30 | ||
10 | #define R7 0x38 | ||
11 | #define R8 0x40 | ||
12 | #define R9 0x48 | ||
13 | #define SL 0x50 | ||
14 | #define FP 0x58 | ||
15 | #define IP 0x60 | ||
16 | #define SP 0x68 | ||
17 | #define LR 0x70 | ||
18 | #define PC 0x78 | ||
19 | |||
20 | /* | ||
21 | * Implementation of void perf_regs_load(u64 *regs); | ||
22 | * | ||
23 | * This functions fills in the 'regs' buffer from the actual registers values, | ||
24 | * in the way the perf built-in unwinding test expects them: | ||
25 | * - the PC at the time at the call to this function. Since this function | ||
26 | * is called using a bl instruction, the PC value is taken from LR. | ||
27 | * The built-in unwinding test then unwinds the call stack from the dwarf | ||
28 | * information in unwind__get_entries. | ||
29 | * | ||
30 | * Notes: | ||
31 | * - the 8 bytes stride in the registers offsets comes from the fact | ||
32 | * that the registers are stored in an u64 array (u64 *regs), | ||
33 | * - the regs buffer needs to be zeroed before the call to this function, | ||
34 | * in this case using a calloc in dwarf-unwind.c. | ||
35 | */ | ||
36 | |||
37 | .text | ||
38 | .type perf_regs_load,%function | ||
39 | ENTRY(perf_regs_load) | ||
40 | str r0, [r0, #R0] | ||
41 | str r1, [r0, #R1] | ||
42 | str r2, [r0, #R2] | ||
43 | str r3, [r0, #R3] | ||
44 | str r4, [r0, #R4] | ||
45 | str r5, [r0, #R5] | ||
46 | str r6, [r0, #R6] | ||
47 | str r7, [r0, #R7] | ||
48 | str r8, [r0, #R8] | ||
49 | str r9, [r0, #R9] | ||
50 | str sl, [r0, #SL] | ||
51 | str fp, [r0, #FP] | ||
52 | str ip, [r0, #IP] | ||
53 | str sp, [r0, #SP] | ||
54 | str lr, [r0, #LR] | ||
55 | str lr, [r0, #PC] // store pc as lr in order to skip the call | ||
56 | // to this function | ||
57 | mov pc, lr | ||
58 | ENDPROC(perf_regs_load) | ||
diff --git a/tools/perf/arch/arm/util/unwind-libdw.c b/tools/perf/arch/arm/util/unwind-libdw.c new file mode 100644 index 000000000000..b4176c60117a --- /dev/null +++ b/tools/perf/arch/arm/util/unwind-libdw.c | |||
@@ -0,0 +1,36 @@ | |||
1 | #include <elfutils/libdwfl.h> | ||
2 | #include "../../util/unwind-libdw.h" | ||
3 | #include "../../util/perf_regs.h" | ||
4 | |||
5 | bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg) | ||
6 | { | ||
7 | struct unwind_info *ui = arg; | ||
8 | struct regs_dump *user_regs = &ui->sample->user_regs; | ||
9 | Dwarf_Word dwarf_regs[PERF_REG_ARM_MAX]; | ||
10 | |||
11 | #define REG(r) ({ \ | ||
12 | Dwarf_Word val = 0; \ | ||
13 | perf_reg_value(&val, user_regs, PERF_REG_ARM_##r); \ | ||
14 | val; \ | ||
15 | }) | ||
16 | |||
17 | dwarf_regs[0] = REG(R0); | ||
18 | dwarf_regs[1] = REG(R1); | ||
19 | dwarf_regs[2] = REG(R2); | ||
20 | dwarf_regs[3] = REG(R3); | ||
21 | dwarf_regs[4] = REG(R4); | ||
22 | dwarf_regs[5] = REG(R5); | ||
23 | dwarf_regs[6] = REG(R6); | ||
24 | dwarf_regs[7] = REG(R7); | ||
25 | dwarf_regs[8] = REG(R8); | ||
26 | dwarf_regs[9] = REG(R9); | ||
27 | dwarf_regs[10] = REG(R10); | ||
28 | dwarf_regs[11] = REG(FP); | ||
29 | dwarf_regs[12] = REG(IP); | ||
30 | dwarf_regs[13] = REG(SP); | ||
31 | dwarf_regs[14] = REG(LR); | ||
32 | dwarf_regs[15] = REG(PC); | ||
33 | |||
34 | return dwfl_thread_state_registers(thread, 0, PERF_REG_ARM_MAX, | ||
35 | dwarf_regs); | ||
36 | } | ||
diff --git a/tools/perf/arch/arm64/Makefile b/tools/perf/arch/arm64/Makefile new file mode 100644 index 000000000000..67e9b3d38e89 --- /dev/null +++ b/tools/perf/arch/arm64/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | ifndef NO_DWARF | ||
2 | PERF_HAVE_DWARF_REGS := 1 | ||
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||
4 | endif | ||
5 | ifndef NO_LIBUNWIND | ||
6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind-libunwind.o | ||
7 | endif | ||
diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h new file mode 100644 index 000000000000..e9441b9e2a30 --- /dev/null +++ b/tools/perf/arch/arm64/include/perf_regs.h | |||
@@ -0,0 +1,88 @@ | |||
1 | #ifndef ARCH_PERF_REGS_H | ||
2 | #define ARCH_PERF_REGS_H | ||
3 | |||
4 | #include <stdlib.h> | ||
5 | #include <linux/types.h> | ||
6 | #include <asm/perf_regs.h> | ||
7 | |||
8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1) | ||
9 | #define PERF_REG_IP PERF_REG_ARM64_PC | ||
10 | #define PERF_REG_SP PERF_REG_ARM64_SP | ||
11 | |||
12 | static inline const char *perf_reg_name(int id) | ||
13 | { | ||
14 | switch (id) { | ||
15 | case PERF_REG_ARM64_X0: | ||
16 | return "x0"; | ||
17 | case PERF_REG_ARM64_X1: | ||
18 | return "x1"; | ||
19 | case PERF_REG_ARM64_X2: | ||
20 | return "x2"; | ||
21 | case PERF_REG_ARM64_X3: | ||
22 | return "x3"; | ||
23 | case PERF_REG_ARM64_X4: | ||
24 | return "x4"; | ||
25 | case PERF_REG_ARM64_X5: | ||
26 | return "x5"; | ||
27 | case PERF_REG_ARM64_X6: | ||
28 | return "x6"; | ||
29 | case PERF_REG_ARM64_X7: | ||
30 | return "x7"; | ||
31 | case PERF_REG_ARM64_X8: | ||
32 | return "x8"; | ||
33 | case PERF_REG_ARM64_X9: | ||
34 | return "x9"; | ||
35 | case PERF_REG_ARM64_X10: | ||
36 | return "x10"; | ||
37 | case PERF_REG_ARM64_X11: | ||
38 | return "x11"; | ||
39 | case PERF_REG_ARM64_X12: | ||
40 | return "x12"; | ||
41 | case PERF_REG_ARM64_X13: | ||
42 | return "x13"; | ||
43 | case PERF_REG_ARM64_X14: | ||
44 | return "x14"; | ||
45 | case PERF_REG_ARM64_X15: | ||
46 | return "x15"; | ||
47 | case PERF_REG_ARM64_X16: | ||
48 | return "x16"; | ||
49 | case PERF_REG_ARM64_X17: | ||
50 | return "x17"; | ||
51 | case PERF_REG_ARM64_X18: | ||
52 | return "x18"; | ||
53 | case PERF_REG_ARM64_X19: | ||
54 | return "x19"; | ||
55 | case PERF_REG_ARM64_X20: | ||
56 | return "x20"; | ||
57 | case PERF_REG_ARM64_X21: | ||
58 | return "x21"; | ||
59 | case PERF_REG_ARM64_X22: | ||
60 | return "x22"; | ||
61 | case PERF_REG_ARM64_X23: | ||
62 | return "x23"; | ||
63 | case PERF_REG_ARM64_X24: | ||
64 | return "x24"; | ||
65 | case PERF_REG_ARM64_X25: | ||
66 | return "x25"; | ||
67 | case PERF_REG_ARM64_X26: | ||
68 | return "x26"; | ||
69 | case PERF_REG_ARM64_X27: | ||
70 | return "x27"; | ||
71 | case PERF_REG_ARM64_X28: | ||
72 | return "x28"; | ||
73 | case PERF_REG_ARM64_X29: | ||
74 | return "x29"; | ||
75 | case PERF_REG_ARM64_SP: | ||
76 | return "sp"; | ||
77 | case PERF_REG_ARM64_LR: | ||
78 | return "lr"; | ||
79 | case PERF_REG_ARM64_PC: | ||
80 | return "pc"; | ||
81 | default: | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | #endif /* ARCH_PERF_REGS_H */ | ||
diff --git a/tools/perf/arch/arm64/util/dwarf-regs.c b/tools/perf/arch/arm64/util/dwarf-regs.c new file mode 100644 index 000000000000..d49efeb8172e --- /dev/null +++ b/tools/perf/arch/arm64/util/dwarf-regs.c | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Mapping of DWARF debug register numbers into register names. | ||
3 | * | ||
4 | * Copyright (C) 2010 Will Deacon, ARM Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <stddef.h> | ||
12 | #include <dwarf-regs.h> | ||
13 | |||
14 | struct pt_regs_dwarfnum { | ||
15 | const char *name; | ||
16 | unsigned int dwarfnum; | ||
17 | }; | ||
18 | |||
19 | #define STR(s) #s | ||
20 | #define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} | ||
21 | #define GPR_DWARFNUM_NAME(num) \ | ||
22 | {.name = STR(%x##num), .dwarfnum = num} | ||
23 | #define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} | ||
24 | |||
25 | /* | ||
26 | * Reference: | ||
27 | * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0057b/IHI0057B_aadwarf64.pdf | ||
28 | */ | ||
29 | static const struct pt_regs_dwarfnum regdwarfnum_table[] = { | ||
30 | GPR_DWARFNUM_NAME(0), | ||
31 | GPR_DWARFNUM_NAME(1), | ||
32 | GPR_DWARFNUM_NAME(2), | ||
33 | GPR_DWARFNUM_NAME(3), | ||
34 | GPR_DWARFNUM_NAME(4), | ||
35 | GPR_DWARFNUM_NAME(5), | ||
36 | GPR_DWARFNUM_NAME(6), | ||
37 | GPR_DWARFNUM_NAME(7), | ||
38 | GPR_DWARFNUM_NAME(8), | ||
39 | GPR_DWARFNUM_NAME(9), | ||
40 | GPR_DWARFNUM_NAME(10), | ||
41 | GPR_DWARFNUM_NAME(11), | ||
42 | GPR_DWARFNUM_NAME(12), | ||
43 | GPR_DWARFNUM_NAME(13), | ||
44 | GPR_DWARFNUM_NAME(14), | ||
45 | GPR_DWARFNUM_NAME(15), | ||
46 | GPR_DWARFNUM_NAME(16), | ||
47 | GPR_DWARFNUM_NAME(17), | ||
48 | GPR_DWARFNUM_NAME(18), | ||
49 | GPR_DWARFNUM_NAME(19), | ||
50 | GPR_DWARFNUM_NAME(20), | ||
51 | GPR_DWARFNUM_NAME(21), | ||
52 | GPR_DWARFNUM_NAME(22), | ||
53 | GPR_DWARFNUM_NAME(23), | ||
54 | GPR_DWARFNUM_NAME(24), | ||
55 | GPR_DWARFNUM_NAME(25), | ||
56 | GPR_DWARFNUM_NAME(26), | ||
57 | GPR_DWARFNUM_NAME(27), | ||
58 | GPR_DWARFNUM_NAME(28), | ||
59 | GPR_DWARFNUM_NAME(29), | ||
60 | REG_DWARFNUM_NAME("%lr", 30), | ||
61 | REG_DWARFNUM_NAME("%sp", 31), | ||
62 | REG_DWARFNUM_END, | ||
63 | }; | ||
64 | |||
65 | /** | ||
66 | * get_arch_regstr() - lookup register name from it's DWARF register number | ||
67 | * @n: the DWARF register number | ||
68 | * | ||
69 | * get_arch_regstr() returns the name of the register in struct | ||
70 | * regdwarfnum_table from it's DWARF register number. If the register is not | ||
71 | * found in the table, this returns NULL; | ||
72 | */ | ||
73 | const char *get_arch_regstr(unsigned int n) | ||
74 | { | ||
75 | const struct pt_regs_dwarfnum *roff; | ||
76 | for (roff = regdwarfnum_table; roff->name != NULL; roff++) | ||
77 | if (roff->dwarfnum == n) | ||
78 | return roff->name; | ||
79 | return NULL; | ||
80 | } | ||
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c new file mode 100644 index 000000000000..436ee43859dc --- /dev/null +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c | |||
@@ -0,0 +1,82 @@ | |||
1 | |||
2 | #include <errno.h> | ||
3 | #include <libunwind.h> | ||
4 | #include "perf_regs.h" | ||
5 | #include "../../util/unwind.h" | ||
6 | |||
7 | int libunwind__arch_reg_id(int regnum) | ||
8 | { | ||
9 | switch (regnum) { | ||
10 | case UNW_AARCH64_X0: | ||
11 | return PERF_REG_ARM64_X0; | ||
12 | case UNW_AARCH64_X1: | ||
13 | return PERF_REG_ARM64_X1; | ||
14 | case UNW_AARCH64_X2: | ||
15 | return PERF_REG_ARM64_X2; | ||
16 | case UNW_AARCH64_X3: | ||
17 | return PERF_REG_ARM64_X3; | ||
18 | case UNW_AARCH64_X4: | ||
19 | return PERF_REG_ARM64_X4; | ||
20 | case UNW_AARCH64_X5: | ||
21 | return PERF_REG_ARM64_X5; | ||
22 | case UNW_AARCH64_X6: | ||
23 | return PERF_REG_ARM64_X6; | ||
24 | case UNW_AARCH64_X7: | ||
25 | return PERF_REG_ARM64_X7; | ||
26 | case UNW_AARCH64_X8: | ||
27 | return PERF_REG_ARM64_X8; | ||
28 | case UNW_AARCH64_X9: | ||
29 | return PERF_REG_ARM64_X9; | ||
30 | case UNW_AARCH64_X10: | ||
31 | return PERF_REG_ARM64_X10; | ||
32 | case UNW_AARCH64_X11: | ||
33 | return PERF_REG_ARM64_X11; | ||
34 | case UNW_AARCH64_X12: | ||
35 | return PERF_REG_ARM64_X12; | ||
36 | case UNW_AARCH64_X13: | ||
37 | return PERF_REG_ARM64_X13; | ||
38 | case UNW_AARCH64_X14: | ||
39 | return PERF_REG_ARM64_X14; | ||
40 | case UNW_AARCH64_X15: | ||
41 | return PERF_REG_ARM64_X15; | ||
42 | case UNW_AARCH64_X16: | ||
43 | return PERF_REG_ARM64_X16; | ||
44 | case UNW_AARCH64_X17: | ||
45 | return PERF_REG_ARM64_X17; | ||
46 | case UNW_AARCH64_X18: | ||
47 | return PERF_REG_ARM64_X18; | ||
48 | case UNW_AARCH64_X19: | ||
49 | return PERF_REG_ARM64_X19; | ||
50 | case UNW_AARCH64_X20: | ||
51 | return PERF_REG_ARM64_X20; | ||
52 | case UNW_AARCH64_X21: | ||
53 | return PERF_REG_ARM64_X21; | ||
54 | case UNW_AARCH64_X22: | ||
55 | return PERF_REG_ARM64_X22; | ||
56 | case UNW_AARCH64_X23: | ||
57 | return PERF_REG_ARM64_X23; | ||
58 | case UNW_AARCH64_X24: | ||
59 | return PERF_REG_ARM64_X24; | ||
60 | case UNW_AARCH64_X25: | ||
61 | return PERF_REG_ARM64_X25; | ||
62 | case UNW_AARCH64_X26: | ||
63 | return PERF_REG_ARM64_X26; | ||
64 | case UNW_AARCH64_X27: | ||
65 | return PERF_REG_ARM64_X27; | ||
66 | case UNW_AARCH64_X28: | ||
67 | return PERF_REG_ARM64_X28; | ||
68 | case UNW_AARCH64_X29: | ||
69 | return PERF_REG_ARM64_X29; | ||
70 | case UNW_AARCH64_X30: | ||
71 | return PERF_REG_ARM64_LR; | ||
72 | case UNW_AARCH64_SP: | ||
73 | return PERF_REG_ARM64_SP; | ||
74 | case UNW_AARCH64_PC: | ||
75 | return PERF_REG_ARM64_PC; | ||
76 | default: | ||
77 | pr_err("unwind: invalid reg id %d\n", regnum); | ||
78 | return -EINVAL; | ||
79 | } | ||
80 | |||
81 | return -EINVAL; | ||
82 | } | ||
diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index fc819ca34a7e..7df517acfef8 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #define ARCH_PERF_REGS_H | 2 | #define ARCH_PERF_REGS_H |
3 | 3 | ||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include "../../util/types.h" | 5 | #include <linux/types.h> |
6 | #include <asm/perf_regs.h> | 6 | #include <asm/perf_regs.h> |
7 | 7 | ||
8 | void perf_regs_load(u64 *regs); | 8 | void perf_regs_load(u64 *regs); |
diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c index 83bc2385e6d3..9f89f899ccc7 100644 --- a/tools/perf/arch/x86/tests/dwarf-unwind.c +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c | |||
@@ -23,7 +23,7 @@ static int sample_ustack(struct perf_sample *sample, | |||
23 | 23 | ||
24 | sp = (unsigned long) regs[PERF_REG_X86_SP]; | 24 | sp = (unsigned long) regs[PERF_REG_X86_SP]; |
25 | 25 | ||
26 | map = map_groups__find(&thread->mg, MAP__VARIABLE, (u64) sp); | 26 | map = map_groups__find(thread->mg, MAP__VARIABLE, (u64) sp); |
27 | if (!map) { | 27 | if (!map) { |
28 | pr_debug("failed to get stack map\n"); | 28 | pr_debug("failed to get stack map\n"); |
29 | free(buf); | 29 | free(buf); |
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index b2519e49424f..40021fa3129b 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <linux/perf_event.h> | 4 | #include <linux/perf_event.h> |
5 | 5 | ||
6 | #include "../../perf.h" | 6 | #include "../../perf.h" |
7 | #include "../../util/types.h" | 7 | #include <linux/types.h> |
8 | #include "../../util/debug.h" | 8 | #include "../../util/debug.h" |
9 | #include "tsc.h" | 9 | #include "tsc.h" |
10 | 10 | ||
diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h index a24dec81c795..2affe0366b59 100644 --- a/tools/perf/arch/x86/util/tsc.h +++ b/tools/perf/arch/x86/util/tsc.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ | 1 | #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ |
2 | #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ | 2 | #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ |
3 | 3 | ||
4 | #include "../../util/types.h" | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | struct perf_tsc_conversion { | 6 | struct perf_tsc_conversion { |
7 | u16 time_shift; | 7 | u16 time_shift; |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 0da603b79b61..d30d2c2e2a7a 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -46,7 +46,7 @@ struct perf_annotate { | |||
46 | }; | 46 | }; |
47 | 47 | ||
48 | static int perf_evsel__add_sample(struct perf_evsel *evsel, | 48 | static int perf_evsel__add_sample(struct perf_evsel *evsel, |
49 | struct perf_sample *sample, | 49 | struct perf_sample *sample __maybe_unused, |
50 | struct addr_location *al, | 50 | struct addr_location *al, |
51 | struct perf_annotate *ann) | 51 | struct perf_annotate *ann) |
52 | { | 52 | { |
@@ -70,7 +70,6 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
70 | return -ENOMEM; | 70 | return -ENOMEM; |
71 | 71 | ||
72 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 72 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
73 | evsel->hists.stats.total_period += sample->period; | ||
74 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 73 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
75 | return ret; | 74 | return ret; |
76 | } | 75 | } |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 204fffe22532..8bff543acaab 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -60,7 +60,6 @@ static int data__files_cnt; | |||
60 | #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) | 60 | #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) |
61 | #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) | 61 | #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) |
62 | 62 | ||
63 | static char diff__default_sort_order[] = "dso,symbol"; | ||
64 | static bool force; | 63 | static bool force; |
65 | static bool show_period; | 64 | static bool show_period; |
66 | static bool show_formula; | 65 | static bool show_formula; |
@@ -220,7 +219,8 @@ static int setup_compute(const struct option *opt, const char *str, | |||
220 | 219 | ||
221 | static double period_percent(struct hist_entry *he, u64 period) | 220 | static double period_percent(struct hist_entry *he, u64 period) |
222 | { | 221 | { |
223 | u64 total = he->hists->stats.total_period; | 222 | u64 total = hists__total_period(he->hists); |
223 | |||
224 | return (period * 100.0) / total; | 224 | return (period * 100.0) / total; |
225 | } | 225 | } |
226 | 226 | ||
@@ -259,11 +259,18 @@ static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair) | |||
259 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, | 259 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, |
260 | char *buf, size_t size) | 260 | char *buf, size_t size) |
261 | { | 261 | { |
262 | u64 he_total = he->hists->stats.total_period; | ||
263 | u64 pair_total = pair->hists->stats.total_period; | ||
264 | |||
265 | if (symbol_conf.filter_relative) { | ||
266 | he_total = he->hists->stats.total_non_filtered_period; | ||
267 | pair_total = pair->hists->stats.total_non_filtered_period; | ||
268 | } | ||
262 | return scnprintf(buf, size, | 269 | return scnprintf(buf, size, |
263 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " | 270 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " |
264 | "(%" PRIu64 " * 100 / %" PRIu64 ")", | 271 | "(%" PRIu64 " * 100 / %" PRIu64 ")", |
265 | pair->stat.period, pair->hists->stats.total_period, | 272 | pair->stat.period, pair_total, |
266 | he->stat.period, he->hists->stats.total_period); | 273 | he->stat.period, he_total); |
267 | } | 274 | } |
268 | 275 | ||
269 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, | 276 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, |
@@ -327,16 +334,22 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
327 | return -1; | 334 | return -1; |
328 | } | 335 | } |
329 | 336 | ||
330 | if (al.filtered) | ||
331 | return 0; | ||
332 | |||
333 | if (hists__add_entry(&evsel->hists, &al, sample->period, | 337 | if (hists__add_entry(&evsel->hists, &al, sample->period, |
334 | sample->weight, sample->transaction)) { | 338 | sample->weight, sample->transaction)) { |
335 | pr_warning("problem incrementing symbol period, skipping event\n"); | 339 | pr_warning("problem incrementing symbol period, skipping event\n"); |
336 | return -1; | 340 | return -1; |
337 | } | 341 | } |
338 | 342 | ||
343 | /* | ||
344 | * The total_period is updated here before going to the output | ||
345 | * tree since normally only the baseline hists will call | ||
346 | * hists__output_resort() and precompute needs the total | ||
347 | * period in order to sort entries by percentage delta. | ||
348 | */ | ||
339 | evsel->hists.stats.total_period += sample->period; | 349 | evsel->hists.stats.total_period += sample->period; |
350 | if (!al.filtered) | ||
351 | evsel->hists.stats.total_non_filtered_period += sample->period; | ||
352 | |||
340 | return 0; | 353 | return 0; |
341 | } | 354 | } |
342 | 355 | ||
@@ -564,8 +577,7 @@ static void hists__compute_resort(struct hists *hists) | |||
564 | hists->entries = RB_ROOT; | 577 | hists->entries = RB_ROOT; |
565 | next = rb_first(root); | 578 | next = rb_first(root); |
566 | 579 | ||
567 | hists->nr_entries = 0; | 580 | hists__reset_stats(hists); |
568 | hists->stats.total_period = 0; | ||
569 | hists__reset_col_len(hists); | 581 | hists__reset_col_len(hists); |
570 | 582 | ||
571 | while (next != NULL) { | 583 | while (next != NULL) { |
@@ -575,7 +587,10 @@ static void hists__compute_resort(struct hists *hists) | |||
575 | next = rb_next(&he->rb_node_in); | 587 | next = rb_next(&he->rb_node_in); |
576 | 588 | ||
577 | insert_hist_entry_by_compute(&hists->entries, he, compute); | 589 | insert_hist_entry_by_compute(&hists->entries, he, compute); |
578 | hists__inc_nr_entries(hists, he); | 590 | hists__inc_stats(hists, he); |
591 | |||
592 | if (!he->filtered) | ||
593 | hists__calc_col_len(hists, he); | ||
579 | } | 594 | } |
580 | } | 595 | } |
581 | 596 | ||
@@ -725,20 +740,24 @@ static const struct option options[] = { | |||
725 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 740 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
726 | "only consider these symbols"), | 741 | "only consider these symbols"), |
727 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 742 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
728 | "sort by key(s): pid, comm, dso, symbol, parent"), | 743 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." |
744 | " Please refer the man page for the complete list."), | ||
729 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", | 745 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
730 | "separator for columns, no spaces will be added between " | 746 | "separator for columns, no spaces will be added between " |
731 | "columns '.' is reserved."), | 747 | "columns '.' is reserved."), |
732 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 748 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
733 | "Look for files with symbols relative to this directory"), | 749 | "Look for files with symbols relative to this directory"), |
734 | OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), | 750 | OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), |
751 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | ||
752 | "How to display percentage of filtered entries", parse_filter_percentage), | ||
735 | OPT_END() | 753 | OPT_END() |
736 | }; | 754 | }; |
737 | 755 | ||
738 | static double baseline_percent(struct hist_entry *he) | 756 | static double baseline_percent(struct hist_entry *he) |
739 | { | 757 | { |
740 | struct hists *hists = he->hists; | 758 | u64 total = hists__total_period(he->hists); |
741 | return 100.0 * he->stat.period / hists->stats.total_period; | 759 | |
760 | return 100.0 * he->stat.period / total; | ||
742 | } | 761 | } |
743 | 762 | ||
744 | static int hpp__color_baseline(struct perf_hpp_fmt *fmt, | 763 | static int hpp__color_baseline(struct perf_hpp_fmt *fmt, |
@@ -1120,7 +1139,8 @@ static int data_init(int argc, const char **argv) | |||
1120 | 1139 | ||
1121 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | 1140 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) |
1122 | { | 1141 | { |
1123 | sort_order = diff__default_sort_order; | 1142 | perf_config(perf_default_config, NULL); |
1143 | |||
1124 | argc = parse_options(argc, argv, options, diff_usage, 0); | 1144 | argc = parse_options(argc, argv, options, diff_usage, 0); |
1125 | 1145 | ||
1126 | if (symbol__init() < 0) | 1146 | if (symbol__init() < 0) |
@@ -1131,6 +1151,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1131 | 1151 | ||
1132 | ui_init(); | 1152 | ui_init(); |
1133 | 1153 | ||
1154 | sort__mode = SORT_MODE__DIFF; | ||
1155 | |||
1134 | if (setup_sorting() < 0) | 1156 | if (setup_sorting() < 0) |
1135 | usage_with_options(diff_usage, options); | 1157 | usage_with_options(diff_usage, options); |
1136 | 1158 | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 3a7387551369..6a3af0013d68 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -209,7 +209,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
209 | 209 | ||
210 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 210 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
211 | 211 | ||
212 | thread = machine__findnew_thread(machine, sample->pid, sample->pid); | 212 | thread = machine__findnew_thread(machine, sample->pid, sample->tid); |
213 | if (thread == NULL) { | 213 | if (thread == NULL) { |
214 | pr_err("problem processing %d event, skipping it.\n", | 214 | pr_err("problem processing %d event, skipping it.\n", |
215 | event->header.type); | 215 | event->header.type); |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 929462aa4943..bef3376bfaf3 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
15 | #include "util/trace-event.h" | 15 | #include "util/trace-event.h" |
16 | #include "util/data.h" | 16 | #include "util/data.h" |
17 | #include "util/cpumap.h" | ||
17 | 18 | ||
18 | #include "util/debug.h" | 19 | #include "util/debug.h" |
19 | 20 | ||
@@ -31,9 +32,6 @@ static int caller_lines = -1; | |||
31 | 32 | ||
32 | static bool raw_ip; | 33 | static bool raw_ip; |
33 | 34 | ||
34 | static int *cpunode_map; | ||
35 | static int max_cpu_num; | ||
36 | |||
37 | struct alloc_stat { | 35 | struct alloc_stat { |
38 | u64 call_site; | 36 | u64 call_site; |
39 | u64 ptr; | 37 | u64 ptr; |
@@ -55,76 +53,6 @@ static struct rb_root root_caller_sorted; | |||
55 | static unsigned long total_requested, total_allocated; | 53 | static unsigned long total_requested, total_allocated; |
56 | static unsigned long nr_allocs, nr_cross_allocs; | 54 | static unsigned long nr_allocs, nr_cross_allocs; |
57 | 55 | ||
58 | #define PATH_SYS_NODE "/sys/devices/system/node" | ||
59 | |||
60 | static int init_cpunode_map(void) | ||
61 | { | ||
62 | FILE *fp; | ||
63 | int i, err = -1; | ||
64 | |||
65 | fp = fopen("/sys/devices/system/cpu/kernel_max", "r"); | ||
66 | if (!fp) { | ||
67 | max_cpu_num = 4096; | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | if (fscanf(fp, "%d", &max_cpu_num) < 1) { | ||
72 | pr_err("Failed to read 'kernel_max' from sysfs"); | ||
73 | goto out_close; | ||
74 | } | ||
75 | |||
76 | max_cpu_num++; | ||
77 | |||
78 | cpunode_map = calloc(max_cpu_num, sizeof(int)); | ||
79 | if (!cpunode_map) { | ||
80 | pr_err("%s: calloc failed\n", __func__); | ||
81 | goto out_close; | ||
82 | } | ||
83 | |||
84 | for (i = 0; i < max_cpu_num; i++) | ||
85 | cpunode_map[i] = -1; | ||
86 | |||
87 | err = 0; | ||
88 | out_close: | ||
89 | fclose(fp); | ||
90 | return err; | ||
91 | } | ||
92 | |||
93 | static int setup_cpunode_map(void) | ||
94 | { | ||
95 | struct dirent *dent1, *dent2; | ||
96 | DIR *dir1, *dir2; | ||
97 | unsigned int cpu, mem; | ||
98 | char buf[PATH_MAX]; | ||
99 | |||
100 | if (init_cpunode_map()) | ||
101 | return -1; | ||
102 | |||
103 | dir1 = opendir(PATH_SYS_NODE); | ||
104 | if (!dir1) | ||
105 | return 0; | ||
106 | |||
107 | while ((dent1 = readdir(dir1)) != NULL) { | ||
108 | if (dent1->d_type != DT_DIR || | ||
109 | sscanf(dent1->d_name, "node%u", &mem) < 1) | ||
110 | continue; | ||
111 | |||
112 | snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); | ||
113 | dir2 = opendir(buf); | ||
114 | if (!dir2) | ||
115 | continue; | ||
116 | while ((dent2 = readdir(dir2)) != NULL) { | ||
117 | if (dent2->d_type != DT_LNK || | ||
118 | sscanf(dent2->d_name, "cpu%u", &cpu) < 1) | ||
119 | continue; | ||
120 | cpunode_map[cpu] = mem; | ||
121 | } | ||
122 | closedir(dir2); | ||
123 | } | ||
124 | closedir(dir1); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, | 56 | static int insert_alloc_stat(unsigned long call_site, unsigned long ptr, |
129 | int bytes_req, int bytes_alloc, int cpu) | 57 | int bytes_req, int bytes_alloc, int cpu) |
130 | { | 58 | { |
@@ -235,7 +163,7 @@ static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel, | |||
235 | int ret = perf_evsel__process_alloc_event(evsel, sample); | 163 | int ret = perf_evsel__process_alloc_event(evsel, sample); |
236 | 164 | ||
237 | if (!ret) { | 165 | if (!ret) { |
238 | int node1 = cpunode_map[sample->cpu], | 166 | int node1 = cpu__get_node(sample->cpu), |
239 | node2 = perf_evsel__intval(evsel, sample, "node"); | 167 | node2 = perf_evsel__intval(evsel, sample, "node"); |
240 | 168 | ||
241 | if (node1 != node2) | 169 | if (node1 != node2) |
@@ -307,7 +235,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
307 | struct machine *machine) | 235 | struct machine *machine) |
308 | { | 236 | { |
309 | struct thread *thread = machine__findnew_thread(machine, sample->pid, | 237 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
310 | sample->pid); | 238 | sample->tid); |
311 | 239 | ||
312 | if (thread == NULL) { | 240 | if (thread == NULL) { |
313 | pr_debug("problem processing %d event, skipping it.\n", | 241 | pr_debug("problem processing %d event, skipping it.\n", |
@@ -756,11 +684,13 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
756 | OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), | 684 | OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), |
757 | OPT_END() | 685 | OPT_END() |
758 | }; | 686 | }; |
759 | const char * const kmem_usage[] = { | 687 | const char *const kmem_subcommands[] = { "record", "stat", NULL }; |
760 | "perf kmem [<options>] {record|stat}", | 688 | const char *kmem_usage[] = { |
689 | NULL, | ||
761 | NULL | 690 | NULL |
762 | }; | 691 | }; |
763 | argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); | 692 | argc = parse_options_subcommand(argc, argv, kmem_options, |
693 | kmem_subcommands, kmem_usage, 0); | ||
764 | 694 | ||
765 | if (!argc) | 695 | if (!argc) |
766 | usage_with_options(kmem_usage, kmem_options); | 696 | usage_with_options(kmem_usage, kmem_options); |
@@ -770,7 +700,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
770 | if (!strncmp(argv[0], "rec", 3)) { | 700 | if (!strncmp(argv[0], "rec", 3)) { |
771 | return __cmd_record(argc, argv); | 701 | return __cmd_record(argc, argv); |
772 | } else if (!strcmp(argv[0], "stat")) { | 702 | } else if (!strcmp(argv[0], "stat")) { |
773 | if (setup_cpunode_map()) | 703 | if (cpu__setup_cpunode_map()) |
774 | return -1; | 704 | return -1; |
775 | 705 | ||
776 | if (list_empty(&caller_sort)) | 706 | if (list_empty(&caller_sort)) |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index c852c7a85d32..6148afc995c6 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -961,8 +961,10 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) | |||
961 | "perf lock info [<options>]", | 961 | "perf lock info [<options>]", |
962 | NULL | 962 | NULL |
963 | }; | 963 | }; |
964 | const char * const lock_usage[] = { | 964 | const char *const lock_subcommands[] = { "record", "report", "script", |
965 | "perf lock [<options>] {record|report|script|info}", | 965 | "info", NULL }; |
966 | const char *lock_usage[] = { | ||
967 | NULL, | ||
966 | NULL | 968 | NULL |
967 | }; | 969 | }; |
968 | const char * const report_usage[] = { | 970 | const char * const report_usage[] = { |
@@ -976,8 +978,8 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) | |||
976 | for (i = 0; i < LOCKHASH_SIZE; i++) | 978 | for (i = 0; i < LOCKHASH_SIZE; i++) |
977 | INIT_LIST_HEAD(lockhash_table + i); | 979 | INIT_LIST_HEAD(lockhash_table + i); |
978 | 980 | ||
979 | argc = parse_options(argc, argv, lock_options, lock_usage, | 981 | argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands, |
980 | PARSE_OPT_STOP_AT_NON_OPTION); | 982 | lock_usage, PARSE_OPT_STOP_AT_NON_OPTION); |
981 | if (!argc) | 983 | if (!argc) |
982 | usage_with_options(lock_usage, lock_options); | 984 | usage_with_options(lock_usage, lock_options); |
983 | 985 | ||
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 2e3ade69a58e..4a1a6c94a5eb 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
@@ -21,11 +21,6 @@ struct perf_mem { | |||
21 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 21 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
22 | }; | 22 | }; |
23 | 23 | ||
24 | static const char * const mem_usage[] = { | ||
25 | "perf mem [<options>] {record <command> |report}", | ||
26 | NULL | ||
27 | }; | ||
28 | |||
29 | static int __cmd_record(int argc, const char **argv) | 24 | static int __cmd_record(int argc, const char **argv) |
30 | { | 25 | { |
31 | int rec_argc, i = 0, j; | 26 | int rec_argc, i = 0, j; |
@@ -220,9 +215,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
220 | " between columns '.' is reserved."), | 215 | " between columns '.' is reserved."), |
221 | OPT_END() | 216 | OPT_END() |
222 | }; | 217 | }; |
218 | const char *const mem_subcommands[] = { "record", "report", NULL }; | ||
219 | const char *mem_usage[] = { | ||
220 | NULL, | ||
221 | NULL | ||
222 | }; | ||
223 | |||
223 | 224 | ||
224 | argc = parse_options(argc, argv, mem_options, mem_usage, | 225 | argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, |
225 | PARSE_OPT_STOP_AT_NON_OPTION); | 226 | mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); |
226 | 227 | ||
227 | if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) | 228 | if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) |
228 | usage_with_options(mem_usage, mem_options); | 229 | usage_with_options(mem_usage, mem_options); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8ce62ef7f6c3..e4c85b8f46c2 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -30,37 +30,6 @@ | |||
30 | #include <sched.h> | 30 | #include <sched.h> |
31 | #include <sys/mman.h> | 31 | #include <sys/mman.h> |
32 | 32 | ||
33 | #ifndef HAVE_ON_EXIT_SUPPORT | ||
34 | #ifndef ATEXIT_MAX | ||
35 | #define ATEXIT_MAX 32 | ||
36 | #endif | ||
37 | static int __on_exit_count = 0; | ||
38 | typedef void (*on_exit_func_t) (int, void *); | ||
39 | static on_exit_func_t __on_exit_funcs[ATEXIT_MAX]; | ||
40 | static void *__on_exit_args[ATEXIT_MAX]; | ||
41 | static int __exitcode = 0; | ||
42 | static void __handle_on_exit_funcs(void); | ||
43 | static int on_exit(on_exit_func_t function, void *arg); | ||
44 | #define exit(x) (exit)(__exitcode = (x)) | ||
45 | |||
46 | static int on_exit(on_exit_func_t function, void *arg) | ||
47 | { | ||
48 | if (__on_exit_count == ATEXIT_MAX) | ||
49 | return -ENOMEM; | ||
50 | else if (__on_exit_count == 0) | ||
51 | atexit(__handle_on_exit_funcs); | ||
52 | __on_exit_funcs[__on_exit_count] = function; | ||
53 | __on_exit_args[__on_exit_count++] = arg; | ||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static void __handle_on_exit_funcs(void) | ||
58 | { | ||
59 | int i; | ||
60 | for (i = 0; i < __on_exit_count; i++) | ||
61 | __on_exit_funcs[i] (__exitcode, __on_exit_args[i]); | ||
62 | } | ||
63 | #endif | ||
64 | 33 | ||
65 | struct record { | 34 | struct record { |
66 | struct perf_tool tool; | 35 | struct perf_tool tool; |
@@ -147,29 +116,19 @@ static void sig_handler(int sig) | |||
147 | { | 116 | { |
148 | if (sig == SIGCHLD) | 117 | if (sig == SIGCHLD) |
149 | child_finished = 1; | 118 | child_finished = 1; |
119 | else | ||
120 | signr = sig; | ||
150 | 121 | ||
151 | done = 1; | 122 | done = 1; |
152 | signr = sig; | ||
153 | } | 123 | } |
154 | 124 | ||
155 | static void record__sig_exit(int exit_status __maybe_unused, void *arg) | 125 | static void record__sig_exit(void) |
156 | { | 126 | { |
157 | struct record *rec = arg; | 127 | if (signr == -1) |
158 | int status; | ||
159 | |||
160 | if (rec->evlist->workload.pid > 0) { | ||
161 | if (!child_finished) | ||
162 | kill(rec->evlist->workload.pid, SIGTERM); | ||
163 | |||
164 | wait(&status); | ||
165 | if (WIFSIGNALED(status)) | ||
166 | psignal(WTERMSIG(status), rec->progname); | ||
167 | } | ||
168 | |||
169 | if (signr == -1 || signr == SIGUSR1) | ||
170 | return; | 128 | return; |
171 | 129 | ||
172 | signal(signr, SIG_DFL); | 130 | signal(signr, SIG_DFL); |
131 | raise(signr); | ||
173 | } | 132 | } |
174 | 133 | ||
175 | static int record__open(struct record *rec) | 134 | static int record__open(struct record *rec) |
@@ -243,27 +202,6 @@ static int process_buildids(struct record *rec) | |||
243 | size, &build_id__mark_dso_hit_ops); | 202 | size, &build_id__mark_dso_hit_ops); |
244 | } | 203 | } |
245 | 204 | ||
246 | static void record__exit(int status, void *arg) | ||
247 | { | ||
248 | struct record *rec = arg; | ||
249 | struct perf_data_file *file = &rec->file; | ||
250 | |||
251 | if (status != 0) | ||
252 | return; | ||
253 | |||
254 | if (!file->is_pipe) { | ||
255 | rec->session->header.data_size += rec->bytes_written; | ||
256 | |||
257 | if (!rec->no_buildid) | ||
258 | process_buildids(rec); | ||
259 | perf_session__write_header(rec->session, rec->evlist, | ||
260 | file->fd, true); | ||
261 | perf_session__delete(rec->session); | ||
262 | perf_evlist__delete(rec->evlist); | ||
263 | symbol__exit(); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) | 205 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) |
268 | { | 206 | { |
269 | int err; | 207 | int err; |
@@ -344,18 +282,19 @@ static volatile int workload_exec_errno; | |||
344 | * if the fork fails, since we asked by setting its | 282 | * if the fork fails, since we asked by setting its |
345 | * want_signal to true. | 283 | * want_signal to true. |
346 | */ | 284 | */ |
347 | static void workload_exec_failed_signal(int signo, siginfo_t *info, | 285 | static void workload_exec_failed_signal(int signo __maybe_unused, |
286 | siginfo_t *info, | ||
348 | void *ucontext __maybe_unused) | 287 | void *ucontext __maybe_unused) |
349 | { | 288 | { |
350 | workload_exec_errno = info->si_value.sival_int; | 289 | workload_exec_errno = info->si_value.sival_int; |
351 | done = 1; | 290 | done = 1; |
352 | signr = signo; | ||
353 | child_finished = 1; | 291 | child_finished = 1; |
354 | } | 292 | } |
355 | 293 | ||
356 | static int __cmd_record(struct record *rec, int argc, const char **argv) | 294 | static int __cmd_record(struct record *rec, int argc, const char **argv) |
357 | { | 295 | { |
358 | int err; | 296 | int err; |
297 | int status = 0; | ||
359 | unsigned long waking = 0; | 298 | unsigned long waking = 0; |
360 | const bool forks = argc > 0; | 299 | const bool forks = argc > 0; |
361 | struct machine *machine; | 300 | struct machine *machine; |
@@ -367,7 +306,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
367 | 306 | ||
368 | rec->progname = argv[0]; | 307 | rec->progname = argv[0]; |
369 | 308 | ||
370 | on_exit(record__sig_exit, rec); | 309 | atexit(record__sig_exit); |
371 | signal(SIGCHLD, sig_handler); | 310 | signal(SIGCHLD, sig_handler); |
372 | signal(SIGINT, sig_handler); | 311 | signal(SIGINT, sig_handler); |
373 | signal(SIGTERM, sig_handler); | 312 | signal(SIGTERM, sig_handler); |
@@ -388,32 +327,28 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
388 | workload_exec_failed_signal); | 327 | workload_exec_failed_signal); |
389 | if (err < 0) { | 328 | if (err < 0) { |
390 | pr_err("Couldn't run the workload!\n"); | 329 | pr_err("Couldn't run the workload!\n"); |
330 | status = err; | ||
391 | goto out_delete_session; | 331 | goto out_delete_session; |
392 | } | 332 | } |
393 | } | 333 | } |
394 | 334 | ||
395 | if (record__open(rec) != 0) { | 335 | if (record__open(rec) != 0) { |
396 | err = -1; | 336 | err = -1; |
397 | goto out_delete_session; | 337 | goto out_child; |
398 | } | 338 | } |
399 | 339 | ||
400 | if (!rec->evlist->nr_groups) | 340 | if (!rec->evlist->nr_groups) |
401 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); | 341 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); |
402 | 342 | ||
403 | /* | ||
404 | * perf_session__delete(session) will be called at record__exit() | ||
405 | */ | ||
406 | on_exit(record__exit, rec); | ||
407 | |||
408 | if (file->is_pipe) { | 343 | if (file->is_pipe) { |
409 | err = perf_header__write_pipe(file->fd); | 344 | err = perf_header__write_pipe(file->fd); |
410 | if (err < 0) | 345 | if (err < 0) |
411 | goto out_delete_session; | 346 | goto out_child; |
412 | } else { | 347 | } else { |
413 | err = perf_session__write_header(session, rec->evlist, | 348 | err = perf_session__write_header(session, rec->evlist, |
414 | file->fd, false); | 349 | file->fd, false); |
415 | if (err < 0) | 350 | if (err < 0) |
416 | goto out_delete_session; | 351 | goto out_child; |
417 | } | 352 | } |
418 | 353 | ||
419 | if (!rec->no_buildid | 354 | if (!rec->no_buildid |
@@ -421,7 +356,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
421 | pr_err("Couldn't generate buildids. " | 356 | pr_err("Couldn't generate buildids. " |
422 | "Use --no-buildid to profile anyway.\n"); | 357 | "Use --no-buildid to profile anyway.\n"); |
423 | err = -1; | 358 | err = -1; |
424 | goto out_delete_session; | 359 | goto out_child; |
425 | } | 360 | } |
426 | 361 | ||
427 | machine = &session->machines.host; | 362 | machine = &session->machines.host; |
@@ -431,7 +366,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
431 | process_synthesized_event); | 366 | process_synthesized_event); |
432 | if (err < 0) { | 367 | if (err < 0) { |
433 | pr_err("Couldn't synthesize attrs.\n"); | 368 | pr_err("Couldn't synthesize attrs.\n"); |
434 | goto out_delete_session; | 369 | goto out_child; |
435 | } | 370 | } |
436 | 371 | ||
437 | if (have_tracepoints(&rec->evlist->entries)) { | 372 | if (have_tracepoints(&rec->evlist->entries)) { |
@@ -447,7 +382,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
447 | process_synthesized_event); | 382 | process_synthesized_event); |
448 | if (err <= 0) { | 383 | if (err <= 0) { |
449 | pr_err("Couldn't record tracing data.\n"); | 384 | pr_err("Couldn't record tracing data.\n"); |
450 | goto out_delete_session; | 385 | goto out_child; |
451 | } | 386 | } |
452 | rec->bytes_written += err; | 387 | rec->bytes_written += err; |
453 | } | 388 | } |
@@ -475,7 +410,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
475 | err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, | 410 | err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->threads, |
476 | process_synthesized_event, opts->sample_address); | 411 | process_synthesized_event, opts->sample_address); |
477 | if (err != 0) | 412 | if (err != 0) |
478 | goto out_delete_session; | 413 | goto out_child; |
479 | 414 | ||
480 | if (rec->realtime_prio) { | 415 | if (rec->realtime_prio) { |
481 | struct sched_param param; | 416 | struct sched_param param; |
@@ -484,7 +419,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
484 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 419 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
485 | pr_err("Could not set realtime priority.\n"); | 420 | pr_err("Could not set realtime priority.\n"); |
486 | err = -1; | 421 | err = -1; |
487 | goto out_delete_session; | 422 | goto out_child; |
488 | } | 423 | } |
489 | } | 424 | } |
490 | 425 | ||
@@ -512,13 +447,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
512 | 447 | ||
513 | if (record__mmap_read_all(rec) < 0) { | 448 | if (record__mmap_read_all(rec) < 0) { |
514 | err = -1; | 449 | err = -1; |
515 | goto out_delete_session; | 450 | goto out_child; |
516 | } | 451 | } |
517 | 452 | ||
518 | if (hits == rec->samples) { | 453 | if (hits == rec->samples) { |
519 | if (done) | 454 | if (done) |
520 | break; | 455 | break; |
521 | err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); | 456 | err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); |
457 | if (err < 0 && errno == EINTR) | ||
458 | err = 0; | ||
522 | waking++; | 459 | waking++; |
523 | } | 460 | } |
524 | 461 | ||
@@ -538,28 +475,52 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
538 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); | 475 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); |
539 | pr_err("Workload failed: %s\n", emsg); | 476 | pr_err("Workload failed: %s\n", emsg); |
540 | err = -1; | 477 | err = -1; |
541 | goto out_delete_session; | 478 | goto out_child; |
542 | } | 479 | } |
543 | 480 | ||
544 | if (quiet || signr == SIGUSR1) | 481 | if (!quiet) { |
545 | return 0; | 482 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); |
546 | 483 | ||
547 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); | 484 | /* |
485 | * Approximate RIP event size: 24 bytes. | ||
486 | */ | ||
487 | fprintf(stderr, | ||
488 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | ||
489 | (double)rec->bytes_written / 1024.0 / 1024.0, | ||
490 | file->path, | ||
491 | rec->bytes_written / 24); | ||
492 | } | ||
548 | 493 | ||
549 | /* | 494 | out_child: |
550 | * Approximate RIP event size: 24 bytes. | 495 | if (forks) { |
551 | */ | 496 | int exit_status; |
552 | fprintf(stderr, | ||
553 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | ||
554 | (double)rec->bytes_written / 1024.0 / 1024.0, | ||
555 | file->path, | ||
556 | rec->bytes_written / 24); | ||
557 | 497 | ||
558 | return 0; | 498 | if (!child_finished) |
499 | kill(rec->evlist->workload.pid, SIGTERM); | ||
500 | |||
501 | wait(&exit_status); | ||
502 | |||
503 | if (err < 0) | ||
504 | status = err; | ||
505 | else if (WIFEXITED(exit_status)) | ||
506 | status = WEXITSTATUS(exit_status); | ||
507 | else if (WIFSIGNALED(exit_status)) | ||
508 | signr = WTERMSIG(exit_status); | ||
509 | } else | ||
510 | status = err; | ||
511 | |||
512 | if (!err && !file->is_pipe) { | ||
513 | rec->session->header.data_size += rec->bytes_written; | ||
514 | |||
515 | if (!rec->no_buildid) | ||
516 | process_buildids(rec); | ||
517 | perf_session__write_header(rec->session, rec->evlist, | ||
518 | file->fd, true); | ||
519 | } | ||
559 | 520 | ||
560 | out_delete_session: | 521 | out_delete_session: |
561 | perf_session__delete(session); | 522 | perf_session__delete(session); |
562 | return err; | 523 | return status; |
563 | } | 524 | } |
564 | 525 | ||
565 | #define BRANCH_OPT(n, m) \ | 526 | #define BRANCH_OPT(n, m) \ |
@@ -988,6 +949,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
988 | 949 | ||
989 | err = __cmd_record(&record, argc, argv); | 950 | err = __cmd_record(&record, argc, argv); |
990 | out_symbol_exit: | 951 | out_symbol_exit: |
952 | perf_evlist__delete(rec->evlist); | ||
991 | symbol__exit(); | 953 | symbol__exit(); |
992 | return err; | 954 | return err; |
993 | } | 955 | } |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c8f21137dfd8..bc0eec1ce4be 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -57,6 +57,7 @@ struct report { | |||
57 | const char *cpu_list; | 57 | const char *cpu_list; |
58 | const char *symbol_filter_str; | 58 | const char *symbol_filter_str; |
59 | float min_percent; | 59 | float min_percent; |
60 | u64 nr_entries; | ||
60 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 61 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
61 | }; | 62 | }; |
62 | 63 | ||
@@ -75,6 +76,27 @@ static int report__config(const char *var, const char *value, void *cb) | |||
75 | return perf_default_config(var, value, cb); | 76 | return perf_default_config(var, value, cb); |
76 | } | 77 | } |
77 | 78 | ||
79 | static void report__inc_stats(struct report *rep, struct hist_entry *he) | ||
80 | { | ||
81 | /* | ||
82 | * The @he is either of a newly created one or an existing one | ||
83 | * merging current sample. We only want to count a new one so | ||
84 | * checking ->nr_events being 1. | ||
85 | */ | ||
86 | if (he->stat.nr_events == 1) | ||
87 | rep->nr_entries++; | ||
88 | |||
89 | /* | ||
90 | * Only counts number of samples at this stage as it's more | ||
91 | * natural to do it here and non-sample events are also | ||
92 | * counted in perf_session_deliver_event(). The dump_trace | ||
93 | * requires this info is ready before going to the output tree. | ||
94 | */ | ||
95 | hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE); | ||
96 | if (!he->filtered) | ||
97 | he->hists->stats.nr_non_filtered_samples++; | ||
98 | } | ||
99 | |||
78 | static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, | 100 | static int report__add_mem_hist_entry(struct report *rep, struct addr_location *al, |
79 | struct perf_sample *sample, struct perf_evsel *evsel) | 101 | struct perf_sample *sample, struct perf_evsel *evsel) |
80 | { | 102 | { |
@@ -121,8 +143,8 @@ static int report__add_mem_hist_entry(struct report *rep, struct addr_location * | |||
121 | goto out; | 143 | goto out; |
122 | } | 144 | } |
123 | 145 | ||
124 | evsel->hists.stats.total_period += cost; | 146 | report__inc_stats(rep, he); |
125 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 147 | |
126 | err = hist_entry__append_callchain(he, sample); | 148 | err = hist_entry__append_callchain(he, sample); |
127 | out: | 149 | out: |
128 | return err; | 150 | return err; |
@@ -173,9 +195,7 @@ static int report__add_branch_hist_entry(struct report *rep, struct addr_locatio | |||
173 | if (err) | 195 | if (err) |
174 | goto out; | 196 | goto out; |
175 | } | 197 | } |
176 | 198 | report__inc_stats(rep, he); | |
177 | evsel->hists.stats.total_period += 1; | ||
178 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
179 | } else | 199 | } else |
180 | goto out; | 200 | goto out; |
181 | } | 201 | } |
@@ -208,8 +228,8 @@ static int report__add_hist_entry(struct report *rep, struct perf_evsel *evsel, | |||
208 | if (ui__has_annotation()) | 228 | if (ui__has_annotation()) |
209 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 229 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
210 | 230 | ||
211 | evsel->hists.stats.total_period += sample->period; | 231 | report__inc_stats(rep, he); |
212 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 232 | |
213 | out: | 233 | out: |
214 | return err; | 234 | return err; |
215 | } | 235 | } |
@@ -337,6 +357,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report | |||
337 | char buf[512]; | 357 | char buf[512]; |
338 | size_t size = sizeof(buf); | 358 | size_t size = sizeof(buf); |
339 | 359 | ||
360 | if (symbol_conf.filter_relative) { | ||
361 | nr_samples = hists->stats.nr_non_filtered_samples; | ||
362 | nr_events = hists->stats.total_non_filtered_period; | ||
363 | } | ||
364 | |||
340 | if (perf_evsel__is_group_event(evsel)) { | 365 | if (perf_evsel__is_group_event(evsel)) { |
341 | struct perf_evsel *pos; | 366 | struct perf_evsel *pos; |
342 | 367 | ||
@@ -344,8 +369,13 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report | |||
344 | evname = buf; | 369 | evname = buf; |
345 | 370 | ||
346 | for_each_group_member(pos, evsel) { | 371 | for_each_group_member(pos, evsel) { |
347 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 372 | if (symbol_conf.filter_relative) { |
348 | nr_events += pos->hists.stats.total_period; | 373 | nr_samples += pos->hists.stats.nr_non_filtered_samples; |
374 | nr_events += pos->hists.stats.total_non_filtered_period; | ||
375 | } else { | ||
376 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
377 | nr_events += pos->hists.stats.total_period; | ||
378 | } | ||
349 | } | 379 | } |
350 | } | 380 | } |
351 | 381 | ||
@@ -470,24 +500,12 @@ static int report__browse_hists(struct report *rep) | |||
470 | return ret; | 500 | return ret; |
471 | } | 501 | } |
472 | 502 | ||
473 | static u64 report__collapse_hists(struct report *rep) | 503 | static void report__collapse_hists(struct report *rep) |
474 | { | 504 | { |
475 | struct ui_progress prog; | 505 | struct ui_progress prog; |
476 | struct perf_evsel *pos; | 506 | struct perf_evsel *pos; |
477 | u64 nr_samples = 0; | ||
478 | /* | ||
479 | * Count number of histogram entries to use when showing progress, | ||
480 | * reusing nr_samples variable. | ||
481 | */ | ||
482 | evlist__for_each(rep->session->evlist, pos) | ||
483 | nr_samples += pos->hists.nr_entries; | ||
484 | 507 | ||
485 | ui_progress__init(&prog, nr_samples, "Merging related events..."); | 508 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); |
486 | /* | ||
487 | * Count total number of samples, will be used to check if this | ||
488 | * session had any. | ||
489 | */ | ||
490 | nr_samples = 0; | ||
491 | 509 | ||
492 | evlist__for_each(rep->session->evlist, pos) { | 510 | evlist__for_each(rep->session->evlist, pos) { |
493 | struct hists *hists = &pos->hists; | 511 | struct hists *hists = &pos->hists; |
@@ -496,7 +514,6 @@ static u64 report__collapse_hists(struct report *rep) | |||
496 | hists->symbol_filter_str = rep->symbol_filter_str; | 514 | hists->symbol_filter_str = rep->symbol_filter_str; |
497 | 515 | ||
498 | hists__collapse_resort(hists, &prog); | 516 | hists__collapse_resort(hists, &prog); |
499 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
500 | 517 | ||
501 | /* Non-group events are considered as leader */ | 518 | /* Non-group events are considered as leader */ |
502 | if (symbol_conf.event_group && | 519 | if (symbol_conf.event_group && |
@@ -509,14 +526,11 @@ static u64 report__collapse_hists(struct report *rep) | |||
509 | } | 526 | } |
510 | 527 | ||
511 | ui_progress__finish(); | 528 | ui_progress__finish(); |
512 | |||
513 | return nr_samples; | ||
514 | } | 529 | } |
515 | 530 | ||
516 | static int __cmd_report(struct report *rep) | 531 | static int __cmd_report(struct report *rep) |
517 | { | 532 | { |
518 | int ret; | 533 | int ret; |
519 | u64 nr_samples; | ||
520 | struct perf_session *session = rep->session; | 534 | struct perf_session *session = rep->session; |
521 | struct perf_evsel *pos; | 535 | struct perf_evsel *pos; |
522 | struct perf_data_file *file = session->file; | 536 | struct perf_data_file *file = session->file; |
@@ -556,12 +570,12 @@ static int __cmd_report(struct report *rep) | |||
556 | } | 570 | } |
557 | } | 571 | } |
558 | 572 | ||
559 | nr_samples = report__collapse_hists(rep); | 573 | report__collapse_hists(rep); |
560 | 574 | ||
561 | if (session_done()) | 575 | if (session_done()) |
562 | return 0; | 576 | return 0; |
563 | 577 | ||
564 | if (nr_samples == 0) { | 578 | if (rep->nr_entries == 0) { |
565 | ui__error("The %s file has no samples!\n", file->path); | 579 | ui__error("The %s file has no samples!\n", file->path); |
566 | return 0; | 580 | return 0; |
567 | } | 581 | } |
@@ -573,11 +587,9 @@ static int __cmd_report(struct report *rep) | |||
573 | } | 587 | } |
574 | 588 | ||
575 | static int | 589 | static int |
576 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) | 590 | report_parse_callchain_opt(const struct option *opt, const char *arg, int unset) |
577 | { | 591 | { |
578 | struct report *rep = (struct report *)opt->value; | 592 | struct report *rep = (struct report *)opt->value; |
579 | char *tok, *tok2; | ||
580 | char *endptr; | ||
581 | 593 | ||
582 | /* | 594 | /* |
583 | * --no-call-graph | 595 | * --no-call-graph |
@@ -587,80 +599,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
587 | return 0; | 599 | return 0; |
588 | } | 600 | } |
589 | 601 | ||
590 | symbol_conf.use_callchain = true; | 602 | return parse_callchain_report_opt(arg); |
591 | |||
592 | if (!arg) | ||
593 | return 0; | ||
594 | |||
595 | tok = strtok((char *)arg, ","); | ||
596 | if (!tok) | ||
597 | return -1; | ||
598 | |||
599 | /* get the output mode */ | ||
600 | if (!strncmp(tok, "graph", strlen(arg))) | ||
601 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
602 | |||
603 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
604 | callchain_param.mode = CHAIN_FLAT; | ||
605 | |||
606 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
607 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
608 | |||
609 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
610 | callchain_param.mode = CHAIN_NONE; | ||
611 | symbol_conf.use_callchain = false; | ||
612 | |||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | else | ||
617 | return -1; | ||
618 | |||
619 | /* get the min percentage */ | ||
620 | tok = strtok(NULL, ","); | ||
621 | if (!tok) | ||
622 | goto setup; | ||
623 | |||
624 | callchain_param.min_percent = strtod(tok, &endptr); | ||
625 | if (tok == endptr) | ||
626 | return -1; | ||
627 | |||
628 | /* get the print limit */ | ||
629 | tok2 = strtok(NULL, ","); | ||
630 | if (!tok2) | ||
631 | goto setup; | ||
632 | |||
633 | if (tok2[0] != 'c') { | ||
634 | callchain_param.print_limit = strtoul(tok2, &endptr, 0); | ||
635 | tok2 = strtok(NULL, ","); | ||
636 | if (!tok2) | ||
637 | goto setup; | ||
638 | } | ||
639 | |||
640 | /* get the call chain order */ | ||
641 | if (!strncmp(tok2, "caller", strlen("caller"))) | ||
642 | callchain_param.order = ORDER_CALLER; | ||
643 | else if (!strncmp(tok2, "callee", strlen("callee"))) | ||
644 | callchain_param.order = ORDER_CALLEE; | ||
645 | else | ||
646 | return -1; | ||
647 | |||
648 | /* Get the sort key */ | ||
649 | tok2 = strtok(NULL, ","); | ||
650 | if (!tok2) | ||
651 | goto setup; | ||
652 | if (!strncmp(tok2, "function", strlen("function"))) | ||
653 | callchain_param.key = CCKEY_FUNCTION; | ||
654 | else if (!strncmp(tok2, "address", strlen("address"))) | ||
655 | callchain_param.key = CCKEY_ADDRESS; | ||
656 | else | ||
657 | return -1; | ||
658 | setup: | ||
659 | if (callchain_register_param(&callchain_param) < 0) { | ||
660 | pr_err("Can't register callchain params\n"); | ||
661 | return -1; | ||
662 | } | ||
663 | return 0; | ||
664 | } | 603 | } |
665 | 604 | ||
666 | int | 605 | int |
@@ -760,10 +699,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
760 | OPT_BOOLEAN(0, "header-only", &report.header_only, | 699 | OPT_BOOLEAN(0, "header-only", &report.header_only, |
761 | "Show only data header."), | 700 | "Show only data header."), |
762 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 701 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
763 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," | 702 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." |
764 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," | 703 | " Please refer the man page for the complete list."), |
765 | " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " | 704 | OPT_STRING('F', "fields", &field_order, "key[,keys...]", |
766 | "snoop, locked, abort, in_tx, transaction"), | 705 | "output field(s): overhead, period, sample plus all of sort keys"), |
767 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 706 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
768 | "Show sample percentage for different cpu modes"), | 707 | "Show sample percentage for different cpu modes"), |
769 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 708 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
@@ -772,7 +711,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
772 | "Only display entries with parent-match"), | 711 | "Only display entries with parent-match"), |
773 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", | 712 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", |
774 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " | 713 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " |
775 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), | 714 | "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt), |
776 | OPT_INTEGER(0, "max-stack", &report.max_stack, | 715 | OPT_INTEGER(0, "max-stack", &report.max_stack, |
777 | "Set the maximum stack depth when parsing the callchain, " | 716 | "Set the maximum stack depth when parsing the callchain, " |
778 | "anything beyond the specified depth will be ignored. " | 717 | "anything beyond the specified depth will be ignored. " |
@@ -823,6 +762,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
823 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), | 762 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), |
824 | OPT_CALLBACK(0, "percent-limit", &report, "percent", | 763 | OPT_CALLBACK(0, "percent-limit", &report, "percent", |
825 | "Don't show entries under that percent", parse_percent_limit), | 764 | "Don't show entries under that percent", parse_percent_limit), |
765 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | ||
766 | "how to display percentage of filtered entries", parse_filter_percentage), | ||
826 | OPT_END() | 767 | OPT_END() |
827 | }; | 768 | }; |
828 | struct perf_data_file file = { | 769 | struct perf_data_file file = { |
@@ -866,52 +807,31 @@ repeat: | |||
866 | if (branch_mode == -1 && has_br_stack) | 807 | if (branch_mode == -1 && has_br_stack) |
867 | sort__mode = SORT_MODE__BRANCH; | 808 | sort__mode = SORT_MODE__BRANCH; |
868 | 809 | ||
869 | /* sort__mode could be NORMAL if --no-branch-stack */ | ||
870 | if (sort__mode == SORT_MODE__BRANCH) { | ||
871 | /* | ||
872 | * if no sort_order is provided, then specify | ||
873 | * branch-mode specific order | ||
874 | */ | ||
875 | if (sort_order == default_sort_order) | ||
876 | sort_order = "comm,dso_from,symbol_from," | ||
877 | "dso_to,symbol_to"; | ||
878 | |||
879 | } | ||
880 | if (report.mem_mode) { | 810 | if (report.mem_mode) { |
881 | if (sort__mode == SORT_MODE__BRANCH) { | 811 | if (sort__mode == SORT_MODE__BRANCH) { |
882 | pr_err("branch and mem mode incompatible\n"); | 812 | pr_err("branch and mem mode incompatible\n"); |
883 | goto error; | 813 | goto error; |
884 | } | 814 | } |
885 | sort__mode = SORT_MODE__MEMORY; | 815 | sort__mode = SORT_MODE__MEMORY; |
886 | |||
887 | /* | ||
888 | * if no sort_order is provided, then specify | ||
889 | * branch-mode specific order | ||
890 | */ | ||
891 | if (sort_order == default_sort_order) | ||
892 | sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; | ||
893 | } | 816 | } |
894 | 817 | ||
895 | if (setup_sorting() < 0) { | 818 | if (setup_sorting() < 0) { |
896 | parse_options_usage(report_usage, options, "s", 1); | 819 | if (sort_order) |
820 | parse_options_usage(report_usage, options, "s", 1); | ||
821 | if (field_order) | ||
822 | parse_options_usage(sort_order ? NULL : report_usage, | ||
823 | options, "F", 1); | ||
897 | goto error; | 824 | goto error; |
898 | } | 825 | } |
899 | 826 | ||
900 | if (parent_pattern != default_parent_pattern) { | ||
901 | if (sort_dimension__add("parent") < 0) | ||
902 | goto error; | ||
903 | } | ||
904 | |||
905 | /* Force tty output for header output. */ | 827 | /* Force tty output for header output. */ |
906 | if (report.header || report.header_only) | 828 | if (report.header || report.header_only) |
907 | use_browser = 0; | 829 | use_browser = 0; |
908 | 830 | ||
909 | if (strcmp(input_name, "-") != 0) | 831 | if (strcmp(input_name, "-") != 0) |
910 | setup_browser(true); | 832 | setup_browser(true); |
911 | else { | 833 | else |
912 | use_browser = 0; | 834 | use_browser = 0; |
913 | perf_hpp__init(); | ||
914 | } | ||
915 | 835 | ||
916 | if (report.header || report.header_only) { | 836 | if (report.header || report.header_only) { |
917 | perf_session__fprintf_info(session, stdout, | 837 | perf_session__fprintf_info(session, stdout, |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9ac0a495c954..d7176830b9b2 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -66,7 +66,7 @@ struct sched_atom { | |||
66 | struct task_desc *wakee; | 66 | struct task_desc *wakee; |
67 | }; | 67 | }; |
68 | 68 | ||
69 | #define TASK_STATE_TO_CHAR_STR "RSDTtZX" | 69 | #define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP" |
70 | 70 | ||
71 | enum thread_state { | 71 | enum thread_state { |
72 | THREAD_SLEEPING = 0, | 72 | THREAD_SLEEPING = 0, |
@@ -149,7 +149,6 @@ struct perf_sched { | |||
149 | unsigned long nr_runs; | 149 | unsigned long nr_runs; |
150 | unsigned long nr_timestamps; | 150 | unsigned long nr_timestamps; |
151 | unsigned long nr_unordered_timestamps; | 151 | unsigned long nr_unordered_timestamps; |
152 | unsigned long nr_state_machine_bugs; | ||
153 | unsigned long nr_context_switch_bugs; | 152 | unsigned long nr_context_switch_bugs; |
154 | unsigned long nr_events; | 153 | unsigned long nr_events; |
155 | unsigned long nr_lost_chunks; | 154 | unsigned long nr_lost_chunks; |
@@ -1007,17 +1006,12 @@ static int latency_wakeup_event(struct perf_sched *sched, | |||
1007 | struct perf_sample *sample, | 1006 | struct perf_sample *sample, |
1008 | struct machine *machine) | 1007 | struct machine *machine) |
1009 | { | 1008 | { |
1010 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"), | 1009 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); |
1011 | success = perf_evsel__intval(evsel, sample, "success"); | ||
1012 | struct work_atoms *atoms; | 1010 | struct work_atoms *atoms; |
1013 | struct work_atom *atom; | 1011 | struct work_atom *atom; |
1014 | struct thread *wakee; | 1012 | struct thread *wakee; |
1015 | u64 timestamp = sample->time; | 1013 | u64 timestamp = sample->time; |
1016 | 1014 | ||
1017 | /* Note for later, it may be interesting to observe the failing cases */ | ||
1018 | if (!success) | ||
1019 | return 0; | ||
1020 | |||
1021 | wakee = machine__findnew_thread(machine, 0, pid); | 1015 | wakee = machine__findnew_thread(machine, 0, pid); |
1022 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); | 1016 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); |
1023 | if (!atoms) { | 1017 | if (!atoms) { |
@@ -1037,12 +1031,18 @@ static int latency_wakeup_event(struct perf_sched *sched, | |||
1037 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 1031 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
1038 | 1032 | ||
1039 | /* | 1033 | /* |
1034 | * As we do not guarantee the wakeup event happens when | ||
1035 | * task is out of run queue, also may happen when task is | ||
1036 | * on run queue and wakeup only change ->state to TASK_RUNNING, | ||
1037 | * then we should not set the ->wake_up_time when wake up a | ||
1038 | * task which is on run queue. | ||
1039 | * | ||
1040 | * You WILL be missing events if you've recorded only | 1040 | * You WILL be missing events if you've recorded only |
1041 | * one CPU, or are only looking at only one, so don't | 1041 | * one CPU, or are only looking at only one, so don't |
1042 | * make useless noise. | 1042 | * skip in this case. |
1043 | */ | 1043 | */ |
1044 | if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING) | 1044 | if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING) |
1045 | sched->nr_state_machine_bugs++; | 1045 | return 0; |
1046 | 1046 | ||
1047 | sched->nr_timestamps++; | 1047 | sched->nr_timestamps++; |
1048 | if (atom->sched_out_time > timestamp) { | 1048 | if (atom->sched_out_time > timestamp) { |
@@ -1266,9 +1266,8 @@ static int process_sched_wakeup_event(struct perf_tool *tool, | |||
1266 | static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | 1266 | static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, |
1267 | struct perf_sample *sample, struct machine *machine) | 1267 | struct perf_sample *sample, struct machine *machine) |
1268 | { | 1268 | { |
1269 | const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"), | 1269 | const u32 next_pid = perf_evsel__intval(evsel, sample, "next_pid"); |
1270 | next_pid = perf_evsel__intval(evsel, sample, "next_pid"); | 1270 | struct thread *sched_in; |
1271 | struct thread *sched_out __maybe_unused, *sched_in; | ||
1272 | int new_shortname; | 1271 | int new_shortname; |
1273 | u64 timestamp0, timestamp = sample->time; | 1272 | u64 timestamp0, timestamp = sample->time; |
1274 | s64 delta; | 1273 | s64 delta; |
@@ -1291,7 +1290,6 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1291 | return -1; | 1290 | return -1; |
1292 | } | 1291 | } |
1293 | 1292 | ||
1294 | sched_out = machine__findnew_thread(machine, 0, prev_pid); | ||
1295 | sched_in = machine__findnew_thread(machine, 0, next_pid); | 1293 | sched_in = machine__findnew_thread(machine, 0, next_pid); |
1296 | 1294 | ||
1297 | sched->curr_thread[this_cpu] = sched_in; | 1295 | sched->curr_thread[this_cpu] = sched_in; |
@@ -1300,17 +1298,25 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1300 | 1298 | ||
1301 | new_shortname = 0; | 1299 | new_shortname = 0; |
1302 | if (!sched_in->shortname[0]) { | 1300 | if (!sched_in->shortname[0]) { |
1303 | sched_in->shortname[0] = sched->next_shortname1; | 1301 | if (!strcmp(thread__comm_str(sched_in), "swapper")) { |
1304 | sched_in->shortname[1] = sched->next_shortname2; | 1302 | /* |
1305 | 1303 | * Don't allocate a letter-number for swapper:0 | |
1306 | if (sched->next_shortname1 < 'Z') { | 1304 | * as a shortname. Instead, we use '.' for it. |
1307 | sched->next_shortname1++; | 1305 | */ |
1306 | sched_in->shortname[0] = '.'; | ||
1307 | sched_in->shortname[1] = ' '; | ||
1308 | } else { | 1308 | } else { |
1309 | sched->next_shortname1='A'; | 1309 | sched_in->shortname[0] = sched->next_shortname1; |
1310 | if (sched->next_shortname2 < '9') { | 1310 | sched_in->shortname[1] = sched->next_shortname2; |
1311 | sched->next_shortname2++; | 1311 | |
1312 | if (sched->next_shortname1 < 'Z') { | ||
1313 | sched->next_shortname1++; | ||
1312 | } else { | 1314 | } else { |
1313 | sched->next_shortname2='0'; | 1315 | sched->next_shortname1 = 'A'; |
1316 | if (sched->next_shortname2 < '9') | ||
1317 | sched->next_shortname2++; | ||
1318 | else | ||
1319 | sched->next_shortname2 = '0'; | ||
1314 | } | 1320 | } |
1315 | } | 1321 | } |
1316 | new_shortname = 1; | 1322 | new_shortname = 1; |
@@ -1322,12 +1328,9 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1322 | else | 1328 | else |
1323 | printf("*"); | 1329 | printf("*"); |
1324 | 1330 | ||
1325 | if (sched->curr_thread[cpu]) { | 1331 | if (sched->curr_thread[cpu]) |
1326 | if (sched->curr_thread[cpu]->tid) | 1332 | printf("%2s ", sched->curr_thread[cpu]->shortname); |
1327 | printf("%2s ", sched->curr_thread[cpu]->shortname); | 1333 | else |
1328 | else | ||
1329 | printf(". "); | ||
1330 | } else | ||
1331 | printf(" "); | 1334 | printf(" "); |
1332 | } | 1335 | } |
1333 | 1336 | ||
@@ -1496,14 +1499,6 @@ static void print_bad_events(struct perf_sched *sched) | |||
1496 | (double)sched->nr_lost_events/(double)sched->nr_events * 100.0, | 1499 | (double)sched->nr_lost_events/(double)sched->nr_events * 100.0, |
1497 | sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks); | 1500 | sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks); |
1498 | } | 1501 | } |
1499 | if (sched->nr_state_machine_bugs && sched->nr_timestamps) { | ||
1500 | printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)", | ||
1501 | (double)sched->nr_state_machine_bugs/(double)sched->nr_timestamps*100.0, | ||
1502 | sched->nr_state_machine_bugs, sched->nr_timestamps); | ||
1503 | if (sched->nr_lost_events) | ||
1504 | printf(" (due to lost events?)"); | ||
1505 | printf("\n"); | ||
1506 | } | ||
1507 | if (sched->nr_context_switch_bugs && sched->nr_timestamps) { | 1502 | if (sched->nr_context_switch_bugs && sched->nr_timestamps) { |
1508 | printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", | 1503 | printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", |
1509 | (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0, | 1504 | (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0, |
@@ -1635,6 +1630,7 @@ static int __cmd_record(int argc, const char **argv) | |||
1635 | "-e", "sched:sched_stat_runtime", | 1630 | "-e", "sched:sched_stat_runtime", |
1636 | "-e", "sched:sched_process_fork", | 1631 | "-e", "sched:sched_process_fork", |
1637 | "-e", "sched:sched_wakeup", | 1632 | "-e", "sched:sched_wakeup", |
1633 | "-e", "sched:sched_wakeup_new", | ||
1638 | "-e", "sched:sched_migrate_task", | 1634 | "-e", "sched:sched_migrate_task", |
1639 | }; | 1635 | }; |
1640 | 1636 | ||
@@ -1713,8 +1709,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1713 | "perf sched replay [<options>]", | 1709 | "perf sched replay [<options>]", |
1714 | NULL | 1710 | NULL |
1715 | }; | 1711 | }; |
1716 | const char * const sched_usage[] = { | 1712 | const char *const sched_subcommands[] = { "record", "latency", "map", |
1717 | "perf sched [<options>] {record|latency|map|replay|script}", | 1713 | "replay", "script", NULL }; |
1714 | const char *sched_usage[] = { | ||
1715 | NULL, | ||
1718 | NULL | 1716 | NULL |
1719 | }; | 1717 | }; |
1720 | struct trace_sched_handler lat_ops = { | 1718 | struct trace_sched_handler lat_ops = { |
@@ -1736,8 +1734,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1736 | for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) | 1734 | for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) |
1737 | sched.curr_pid[i] = -1; | 1735 | sched.curr_pid[i] = -1; |
1738 | 1736 | ||
1739 | argc = parse_options(argc, argv, sched_options, sched_usage, | 1737 | argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands, |
1740 | PARSE_OPT_STOP_AT_NON_OPTION); | 1738 | sched_usage, PARSE_OPT_STOP_AT_NON_OPTION); |
1741 | if (!argc) | 1739 | if (!argc) |
1742 | usage_with_options(sched_usage, sched_options); | 1740 | usage_with_options(sched_usage, sched_options); |
1743 | 1741 | ||
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 65aaa5bbf7ec..5b389ce4cd15 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -253,6 +253,9 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
253 | return NULL; | 253 | return NULL; |
254 | 254 | ||
255 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 255 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
256 | if (!he->filtered) | ||
257 | evsel->hists.stats.nr_non_filtered_samples++; | ||
258 | |||
256 | return he; | 259 | return he; |
257 | } | 260 | } |
258 | 261 | ||
@@ -694,8 +697,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
694 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) | 697 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
695 | top->exact_samples++; | 698 | top->exact_samples++; |
696 | 699 | ||
697 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 || | 700 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) |
698 | al.filtered) | ||
699 | return; | 701 | return; |
700 | 702 | ||
701 | if (!top->kptr_restrict_warned && | 703 | if (!top->kptr_restrict_warned && |
@@ -1081,8 +1083,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1081 | OPT_INCR('v', "verbose", &verbose, | 1083 | OPT_INCR('v', "verbose", &verbose, |
1082 | "be more verbose (show counter open errors, etc)"), | 1084 | "be more verbose (show counter open errors, etc)"), |
1083 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1085 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1084 | "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight," | 1086 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." |
1085 | " abort, in_tx, transaction"), | 1087 | " Please refer the man page for the complete list."), |
1088 | OPT_STRING(0, "fields", &field_order, "key[,keys...]", | ||
1089 | "output field(s): overhead, period, sample plus all of sort keys"), | ||
1086 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1090 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1087 | "Show a column with the number of samples"), | 1091 | "Show a column with the number of samples"), |
1088 | OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, | 1092 | OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts, |
@@ -1116,6 +1120,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1116 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), | 1120 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), |
1117 | OPT_CALLBACK(0, "percent-limit", &top, "percent", | 1121 | OPT_CALLBACK(0, "percent-limit", &top, "percent", |
1118 | "Don't show entries under that percent", parse_percent_limit), | 1122 | "Don't show entries under that percent", parse_percent_limit), |
1123 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | ||
1124 | "How to display percentage of filtered entries", parse_filter_percentage), | ||
1119 | OPT_END() | 1125 | OPT_END() |
1120 | }; | 1126 | }; |
1121 | const char * const top_usage[] = { | 1127 | const char * const top_usage[] = { |
@@ -1133,17 +1139,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1133 | if (argc) | 1139 | if (argc) |
1134 | usage_with_options(top_usage, options); | 1140 | usage_with_options(top_usage, options); |
1135 | 1141 | ||
1136 | if (sort_order == default_sort_order) | 1142 | sort__mode = SORT_MODE__TOP; |
1137 | sort_order = "dso,symbol"; | 1143 | /* display thread wants entries to be collapsed in a different tree */ |
1144 | sort__need_collapse = 1; | ||
1138 | 1145 | ||
1139 | if (setup_sorting() < 0) { | 1146 | if (setup_sorting() < 0) { |
1140 | parse_options_usage(top_usage, options, "s", 1); | 1147 | if (sort_order) |
1148 | parse_options_usage(top_usage, options, "s", 1); | ||
1149 | if (field_order) | ||
1150 | parse_options_usage(sort_order ? NULL : top_usage, | ||
1151 | options, "fields", 0); | ||
1141 | goto out_delete_evlist; | 1152 | goto out_delete_evlist; |
1142 | } | 1153 | } |
1143 | 1154 | ||
1144 | /* display thread wants entries to be collapsed in a different tree */ | ||
1145 | sort__need_collapse = 1; | ||
1146 | |||
1147 | if (top.use_stdio) | 1155 | if (top.use_stdio) |
1148 | use_browser = 0; | 1156 | use_browser = 0; |
1149 | else if (top.use_tui) | 1157 | else if (top.use_tui) |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 802cf544202b..729bbdf5cec7 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -29,16 +29,22 @@ ifeq ($(ARCH),x86) | |||
29 | endif | 29 | endif |
30 | NO_PERF_REGS := 0 | 30 | NO_PERF_REGS := 0 |
31 | endif | 31 | endif |
32 | |||
32 | ifeq ($(ARCH),arm) | 33 | ifeq ($(ARCH),arm) |
33 | NO_PERF_REGS := 0 | 34 | NO_PERF_REGS := 0 |
34 | LIBUNWIND_LIBS = -lunwind -lunwind-arm | 35 | LIBUNWIND_LIBS = -lunwind -lunwind-arm |
35 | endif | 36 | endif |
36 | 37 | ||
37 | # So far there's only x86 libdw unwind support merged in perf. | 38 | ifeq ($(ARCH),arm64) |
39 | NO_PERF_REGS := 0 | ||
40 | LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 | ||
41 | endif | ||
42 | |||
43 | # So far there's only x86 and arm libdw unwind support merged in perf. | ||
38 | # Disable it on all other architectures in case libdw unwind | 44 | # Disable it on all other architectures in case libdw unwind |
39 | # support is detected in system. Add supported architectures | 45 | # support is detected in system. Add supported architectures |
40 | # to the check. | 46 | # to the check. |
41 | ifneq ($(ARCH),x86) | 47 | ifneq ($(ARCH),$(filter $(ARCH),x86 arm)) |
42 | NO_LIBDW_DWARF_UNWIND := 1 | 48 | NO_LIBDW_DWARF_UNWIND := 1 |
43 | endif | 49 | endif |
44 | 50 | ||
@@ -168,7 +174,6 @@ CORE_FEATURE_TESTS = \ | |||
168 | libpython-version \ | 174 | libpython-version \ |
169 | libslang \ | 175 | libslang \ |
170 | libunwind \ | 176 | libunwind \ |
171 | on-exit \ | ||
172 | stackprotector-all \ | 177 | stackprotector-all \ |
173 | timerfd \ | 178 | timerfd \ |
174 | libdw-dwarf-unwind | 179 | libdw-dwarf-unwind |
@@ -194,7 +199,6 @@ VF_FEATURE_TESTS = \ | |||
194 | libelf-getphdrnum \ | 199 | libelf-getphdrnum \ |
195 | libelf-mmap \ | 200 | libelf-mmap \ |
196 | libpython-version \ | 201 | libpython-version \ |
197 | on-exit \ | ||
198 | stackprotector-all \ | 202 | stackprotector-all \ |
199 | timerfd \ | 203 | timerfd \ |
200 | libunwind-debug-frame \ | 204 | libunwind-debug-frame \ |
@@ -370,7 +374,7 @@ else | |||
370 | endif | 374 | endif |
371 | 375 | ||
372 | ifndef NO_LIBUNWIND | 376 | ifndef NO_LIBUNWIND |
373 | ifeq ($(ARCH),arm) | 377 | ifeq ($(ARCH),$(filter $(ARCH),arm arm64)) |
374 | $(call feature_check,libunwind-debug-frame) | 378 | $(call feature_check,libunwind-debug-frame) |
375 | ifneq ($(feature-libunwind-debug-frame), 1) | 379 | ifneq ($(feature-libunwind-debug-frame), 1) |
376 | msg := $(warning No debug_frame support found in libunwind); | 380 | msg := $(warning No debug_frame support found in libunwind); |
@@ -565,12 +569,6 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),) | |||
565 | CFLAGS += -DHAVE_LIBBFD_SUPPORT | 569 | CFLAGS += -DHAVE_LIBBFD_SUPPORT |
566 | endif | 570 | endif |
567 | 571 | ||
568 | ifndef NO_ON_EXIT | ||
569 | ifeq ($(feature-on-exit), 1) | ||
570 | CFLAGS += -DHAVE_ON_EXIT_SUPPORT | ||
571 | endif | ||
572 | endif | ||
573 | |||
574 | ifndef NO_BACKTRACE | 572 | ifndef NO_BACKTRACE |
575 | ifeq ($(feature-backtrace), 1) | 573 | ifeq ($(feature-backtrace), 1) |
576 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT | 574 | CFLAGS += -DHAVE_BACKTRACE_SUPPORT |
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 2da103c53f89..64c84e5f0514 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
@@ -24,7 +24,6 @@ FILES= \ | |||
24 | test-libslang.bin \ | 24 | test-libslang.bin \ |
25 | test-libunwind.bin \ | 25 | test-libunwind.bin \ |
26 | test-libunwind-debug-frame.bin \ | 26 | test-libunwind-debug-frame.bin \ |
27 | test-on-exit.bin \ | ||
28 | test-stackprotector-all.bin \ | 27 | test-stackprotector-all.bin \ |
29 | test-timerfd.bin \ | 28 | test-timerfd.bin \ |
30 | test-libdw-dwarf-unwind.bin | 29 | test-libdw-dwarf-unwind.bin |
@@ -133,9 +132,6 @@ test-liberty-z.bin: | |||
133 | test-cplus-demangle.bin: | 132 | test-cplus-demangle.bin: |
134 | $(BUILD) -liberty | 133 | $(BUILD) -liberty |
135 | 134 | ||
136 | test-on-exit.bin: | ||
137 | $(BUILD) | ||
138 | |||
139 | test-backtrace.bin: | 135 | test-backtrace.bin: |
140 | $(BUILD) | 136 | $(BUILD) |
141 | 137 | ||
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index fc37eb3ca17b..fe5c1e5c952f 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
@@ -69,10 +69,6 @@ | |||
69 | # include "test-libbfd.c" | 69 | # include "test-libbfd.c" |
70 | #undef main | 70 | #undef main |
71 | 71 | ||
72 | #define main main_test_on_exit | ||
73 | # include "test-on-exit.c" | ||
74 | #undef main | ||
75 | |||
76 | #define main main_test_backtrace | 72 | #define main main_test_backtrace |
77 | # include "test-backtrace.c" | 73 | # include "test-backtrace.c" |
78 | #undef main | 74 | #undef main |
@@ -110,7 +106,6 @@ int main(int argc, char *argv[]) | |||
110 | main_test_gtk2(argc, argv); | 106 | main_test_gtk2(argc, argv); |
111 | main_test_gtk2_infobar(argc, argv); | 107 | main_test_gtk2_infobar(argc, argv); |
112 | main_test_libbfd(); | 108 | main_test_libbfd(); |
113 | main_test_on_exit(); | ||
114 | main_test_backtrace(); | 109 | main_test_backtrace(); |
115 | main_test_libnuma(); | 110 | main_test_libnuma(); |
116 | main_test_timerfd(); | 111 | main_test_timerfd(); |
diff --git a/tools/perf/config/feature-checks/test-on-exit.c b/tools/perf/config/feature-checks/test-on-exit.c deleted file mode 100644 index 8e88b16e6ded..000000000000 --- a/tools/perf/config/feature-checks/test-on-exit.c +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | |||
4 | static void exit_fn(int status, void *__data) | ||
5 | { | ||
6 | printf("exit status: %d, data: %d\n", status, *(int *)__data); | ||
7 | } | ||
8 | |||
9 | static int data = 123; | ||
10 | |||
11 | int main(void) | ||
12 | { | ||
13 | on_exit(exit_fn, &data); | ||
14 | |||
15 | return 321; | ||
16 | } | ||
diff --git a/tools/perf/perf-completion.sh b/tools/perf/perf-completion.sh index ae3a57694b6b..33569847fdcc 100644 --- a/tools/perf/perf-completion.sh +++ b/tools/perf/perf-completion.sh | |||
@@ -121,8 +121,8 @@ __perf_main () | |||
121 | elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then | 121 | elif [[ $prev == "-e" && "${words[1]}" == @(record|stat|top) ]]; then |
122 | evts=$($cmd list --raw-dump) | 122 | evts=$($cmd list --raw-dump) |
123 | __perfcomp_colon "$evts" "$cur" | 123 | __perfcomp_colon "$evts" "$cur" |
124 | # List subcommands for 'perf kvm' | 124 | # List subcommands for perf commands |
125 | elif [[ $prev == "kvm" ]]; then | 125 | elif [[ $prev == @(kvm|kmem|mem|lock|sched) ]]; then |
126 | subcmds=$($cmd $prev --list-cmds) | 126 | subcmds=$($cmd $prev --list-cmds) |
127 | __perfcomp_colon "$subcmds" "$cur" | 127 | __perfcomp_colon "$subcmds" "$cur" |
128 | # List long option names | 128 | # List long option names |
diff --git a/tools/perf/perf-sys.h b/tools/perf/perf-sys.h new file mode 100644 index 000000000000..5268a1481d23 --- /dev/null +++ b/tools/perf/perf-sys.h | |||
@@ -0,0 +1,190 @@ | |||
1 | #ifndef _PERF_SYS_H | ||
2 | #define _PERF_SYS_H | ||
3 | |||
4 | #include <unistd.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <sys/syscall.h> | ||
7 | #include <linux/types.h> | ||
8 | #include <linux/perf_event.h> | ||
9 | #include <asm/unistd.h> | ||
10 | |||
11 | #if defined(__i386__) | ||
12 | #define mb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
13 | #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
14 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
15 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
16 | #define CPUINFO_PROC "model name" | ||
17 | #ifndef __NR_perf_event_open | ||
18 | # define __NR_perf_event_open 336 | ||
19 | #endif | ||
20 | #ifndef __NR_futex | ||
21 | # define __NR_futex 240 | ||
22 | #endif | ||
23 | #ifndef __NR_gettid | ||
24 | # define __NR_gettid 224 | ||
25 | #endif | ||
26 | #endif | ||
27 | |||
28 | #if defined(__x86_64__) | ||
29 | #define mb() asm volatile("mfence" ::: "memory") | ||
30 | #define wmb() asm volatile("sfence" ::: "memory") | ||
31 | #define rmb() asm volatile("lfence" ::: "memory") | ||
32 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
33 | #define CPUINFO_PROC "model name" | ||
34 | #ifndef __NR_perf_event_open | ||
35 | # define __NR_perf_event_open 298 | ||
36 | #endif | ||
37 | #ifndef __NR_futex | ||
38 | # define __NR_futex 202 | ||
39 | #endif | ||
40 | #ifndef __NR_gettid | ||
41 | # define __NR_gettid 186 | ||
42 | #endif | ||
43 | #endif | ||
44 | |||
45 | #ifdef __powerpc__ | ||
46 | #include "../../arch/powerpc/include/uapi/asm/unistd.h" | ||
47 | #define mb() asm volatile ("sync" ::: "memory") | ||
48 | #define wmb() asm volatile ("sync" ::: "memory") | ||
49 | #define rmb() asm volatile ("sync" ::: "memory") | ||
50 | #define CPUINFO_PROC "cpu" | ||
51 | #endif | ||
52 | |||
53 | #ifdef __s390__ | ||
54 | #define mb() asm volatile("bcr 15,0" ::: "memory") | ||
55 | #define wmb() asm volatile("bcr 15,0" ::: "memory") | ||
56 | #define rmb() asm volatile("bcr 15,0" ::: "memory") | ||
57 | #endif | ||
58 | |||
59 | #ifdef __sh__ | ||
60 | #if defined(__SH4A__) || defined(__SH5__) | ||
61 | # define mb() asm volatile("synco" ::: "memory") | ||
62 | # define wmb() asm volatile("synco" ::: "memory") | ||
63 | # define rmb() asm volatile("synco" ::: "memory") | ||
64 | #else | ||
65 | # define mb() asm volatile("" ::: "memory") | ||
66 | # define wmb() asm volatile("" ::: "memory") | ||
67 | # define rmb() asm volatile("" ::: "memory") | ||
68 | #endif | ||
69 | #define CPUINFO_PROC "cpu type" | ||
70 | #endif | ||
71 | |||
72 | #ifdef __hppa__ | ||
73 | #define mb() asm volatile("" ::: "memory") | ||
74 | #define wmb() asm volatile("" ::: "memory") | ||
75 | #define rmb() asm volatile("" ::: "memory") | ||
76 | #define CPUINFO_PROC "cpu" | ||
77 | #endif | ||
78 | |||
79 | #ifdef __sparc__ | ||
80 | #ifdef __LP64__ | ||
81 | #define mb() asm volatile("ba,pt %%xcc, 1f\n" \ | ||
82 | "membar #StoreLoad\n" \ | ||
83 | "1:\n":::"memory") | ||
84 | #else | ||
85 | #define mb() asm volatile("":::"memory") | ||
86 | #endif | ||
87 | #define wmb() asm volatile("":::"memory") | ||
88 | #define rmb() asm volatile("":::"memory") | ||
89 | #define CPUINFO_PROC "cpu" | ||
90 | #endif | ||
91 | |||
92 | #ifdef __alpha__ | ||
93 | #define mb() asm volatile("mb" ::: "memory") | ||
94 | #define wmb() asm volatile("wmb" ::: "memory") | ||
95 | #define rmb() asm volatile("mb" ::: "memory") | ||
96 | #define CPUINFO_PROC "cpu model" | ||
97 | #endif | ||
98 | |||
99 | #ifdef __ia64__ | ||
100 | #define mb() asm volatile ("mf" ::: "memory") | ||
101 | #define wmb() asm volatile ("mf" ::: "memory") | ||
102 | #define rmb() asm volatile ("mf" ::: "memory") | ||
103 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | ||
104 | #define CPUINFO_PROC "model name" | ||
105 | #endif | ||
106 | |||
107 | #ifdef __arm__ | ||
108 | /* | ||
109 | * Use the __kuser_memory_barrier helper in the CPU helper page. See | ||
110 | * arch/arm/kernel/entry-armv.S in the kernel source for details. | ||
111 | */ | ||
112 | #define mb() ((void(*)(void))0xffff0fa0)() | ||
113 | #define wmb() ((void(*)(void))0xffff0fa0)() | ||
114 | #define rmb() ((void(*)(void))0xffff0fa0)() | ||
115 | #define CPUINFO_PROC "Processor" | ||
116 | #endif | ||
117 | |||
118 | #ifdef __aarch64__ | ||
119 | #define mb() asm volatile("dmb ish" ::: "memory") | ||
120 | #define wmb() asm volatile("dmb ishst" ::: "memory") | ||
121 | #define rmb() asm volatile("dmb ishld" ::: "memory") | ||
122 | #define cpu_relax() asm volatile("yield" ::: "memory") | ||
123 | #endif | ||
124 | |||
125 | #ifdef __mips__ | ||
126 | #define mb() asm volatile( \ | ||
127 | ".set mips2\n\t" \ | ||
128 | "sync\n\t" \ | ||
129 | ".set mips0" \ | ||
130 | : /* no output */ \ | ||
131 | : /* no input */ \ | ||
132 | : "memory") | ||
133 | #define wmb() mb() | ||
134 | #define rmb() mb() | ||
135 | #define CPUINFO_PROC "cpu model" | ||
136 | #endif | ||
137 | |||
138 | #ifdef __arc__ | ||
139 | #define mb() asm volatile("" ::: "memory") | ||
140 | #define wmb() asm volatile("" ::: "memory") | ||
141 | #define rmb() asm volatile("" ::: "memory") | ||
142 | #define CPUINFO_PROC "Processor" | ||
143 | #endif | ||
144 | |||
145 | #ifdef __metag__ | ||
146 | #define mb() asm volatile("" ::: "memory") | ||
147 | #define wmb() asm volatile("" ::: "memory") | ||
148 | #define rmb() asm volatile("" ::: "memory") | ||
149 | #define CPUINFO_PROC "CPU" | ||
150 | #endif | ||
151 | |||
152 | #ifdef __xtensa__ | ||
153 | #define mb() asm volatile("memw" ::: "memory") | ||
154 | #define wmb() asm volatile("memw" ::: "memory") | ||
155 | #define rmb() asm volatile("" ::: "memory") | ||
156 | #define CPUINFO_PROC "core ID" | ||
157 | #endif | ||
158 | |||
159 | #ifdef __tile__ | ||
160 | #define mb() asm volatile ("mf" ::: "memory") | ||
161 | #define wmb() asm volatile ("mf" ::: "memory") | ||
162 | #define rmb() asm volatile ("mf" ::: "memory") | ||
163 | #define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory") | ||
164 | #define CPUINFO_PROC "model name" | ||
165 | #endif | ||
166 | |||
167 | #define barrier() asm volatile ("" ::: "memory") | ||
168 | |||
169 | #ifndef cpu_relax | ||
170 | #define cpu_relax() barrier() | ||
171 | #endif | ||
172 | |||
173 | static inline int | ||
174 | sys_perf_event_open(struct perf_event_attr *attr, | ||
175 | pid_t pid, int cpu, int group_fd, | ||
176 | unsigned long flags) | ||
177 | { | ||
178 | int fd; | ||
179 | |||
180 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, | ||
181 | group_fd, flags); | ||
182 | |||
183 | #ifdef HAVE_ATTR_TEST | ||
184 | if (unlikely(test_attr__enabled)) | ||
185 | test_attr__open(attr, pid, cpu, fd, group_fd, flags); | ||
186 | #endif | ||
187 | return fd; | ||
188 | } | ||
189 | |||
190 | #endif /* _PERF_SYS_H */ | ||
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 5c11ecad02a9..510c65f72858 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -1,182 +1,18 @@ | |||
1 | #ifndef _PERF_PERF_H | 1 | #ifndef _PERF_PERF_H |
2 | #define _PERF_PERF_H | 2 | #define _PERF_PERF_H |
3 | 3 | ||
4 | #include <asm/unistd.h> | ||
5 | |||
6 | #if defined(__i386__) | ||
7 | #define mb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
8 | #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
9 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
10 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
11 | #define CPUINFO_PROC "model name" | ||
12 | #ifndef __NR_perf_event_open | ||
13 | # define __NR_perf_event_open 336 | ||
14 | #endif | ||
15 | #ifndef __NR_futex | ||
16 | # define __NR_futex 240 | ||
17 | #endif | ||
18 | #endif | ||
19 | |||
20 | #if defined(__x86_64__) | ||
21 | #define mb() asm volatile("mfence" ::: "memory") | ||
22 | #define wmb() asm volatile("sfence" ::: "memory") | ||
23 | #define rmb() asm volatile("lfence" ::: "memory") | ||
24 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
25 | #define CPUINFO_PROC "model name" | ||
26 | #ifndef __NR_perf_event_open | ||
27 | # define __NR_perf_event_open 298 | ||
28 | #endif | ||
29 | #ifndef __NR_futex | ||
30 | # define __NR_futex 202 | ||
31 | #endif | ||
32 | #endif | ||
33 | |||
34 | #ifdef __powerpc__ | ||
35 | #include "../../arch/powerpc/include/uapi/asm/unistd.h" | ||
36 | #define mb() asm volatile ("sync" ::: "memory") | ||
37 | #define wmb() asm volatile ("sync" ::: "memory") | ||
38 | #define rmb() asm volatile ("sync" ::: "memory") | ||
39 | #define CPUINFO_PROC "cpu" | ||
40 | #endif | ||
41 | |||
42 | #ifdef __s390__ | ||
43 | #define mb() asm volatile("bcr 15,0" ::: "memory") | ||
44 | #define wmb() asm volatile("bcr 15,0" ::: "memory") | ||
45 | #define rmb() asm volatile("bcr 15,0" ::: "memory") | ||
46 | #endif | ||
47 | |||
48 | #ifdef __sh__ | ||
49 | #if defined(__SH4A__) || defined(__SH5__) | ||
50 | # define mb() asm volatile("synco" ::: "memory") | ||
51 | # define wmb() asm volatile("synco" ::: "memory") | ||
52 | # define rmb() asm volatile("synco" ::: "memory") | ||
53 | #else | ||
54 | # define mb() asm volatile("" ::: "memory") | ||
55 | # define wmb() asm volatile("" ::: "memory") | ||
56 | # define rmb() asm volatile("" ::: "memory") | ||
57 | #endif | ||
58 | #define CPUINFO_PROC "cpu type" | ||
59 | #endif | ||
60 | |||
61 | #ifdef __hppa__ | ||
62 | #define mb() asm volatile("" ::: "memory") | ||
63 | #define wmb() asm volatile("" ::: "memory") | ||
64 | #define rmb() asm volatile("" ::: "memory") | ||
65 | #define CPUINFO_PROC "cpu" | ||
66 | #endif | ||
67 | |||
68 | #ifdef __sparc__ | ||
69 | #ifdef __LP64__ | ||
70 | #define mb() asm volatile("ba,pt %%xcc, 1f\n" \ | ||
71 | "membar #StoreLoad\n" \ | ||
72 | "1:\n":::"memory") | ||
73 | #else | ||
74 | #define mb() asm volatile("":::"memory") | ||
75 | #endif | ||
76 | #define wmb() asm volatile("":::"memory") | ||
77 | #define rmb() asm volatile("":::"memory") | ||
78 | #define CPUINFO_PROC "cpu" | ||
79 | #endif | ||
80 | |||
81 | #ifdef __alpha__ | ||
82 | #define mb() asm volatile("mb" ::: "memory") | ||
83 | #define wmb() asm volatile("wmb" ::: "memory") | ||
84 | #define rmb() asm volatile("mb" ::: "memory") | ||
85 | #define CPUINFO_PROC "cpu model" | ||
86 | #endif | ||
87 | |||
88 | #ifdef __ia64__ | ||
89 | #define mb() asm volatile ("mf" ::: "memory") | ||
90 | #define wmb() asm volatile ("mf" ::: "memory") | ||
91 | #define rmb() asm volatile ("mf" ::: "memory") | ||
92 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | ||
93 | #define CPUINFO_PROC "model name" | ||
94 | #endif | ||
95 | |||
96 | #ifdef __arm__ | ||
97 | /* | ||
98 | * Use the __kuser_memory_barrier helper in the CPU helper page. See | ||
99 | * arch/arm/kernel/entry-armv.S in the kernel source for details. | ||
100 | */ | ||
101 | #define mb() ((void(*)(void))0xffff0fa0)() | ||
102 | #define wmb() ((void(*)(void))0xffff0fa0)() | ||
103 | #define rmb() ((void(*)(void))0xffff0fa0)() | ||
104 | #define CPUINFO_PROC "Processor" | ||
105 | #endif | ||
106 | |||
107 | #ifdef __aarch64__ | ||
108 | #define mb() asm volatile("dmb ish" ::: "memory") | ||
109 | #define wmb() asm volatile("dmb ishst" ::: "memory") | ||
110 | #define rmb() asm volatile("dmb ishld" ::: "memory") | ||
111 | #define cpu_relax() asm volatile("yield" ::: "memory") | ||
112 | #endif | ||
113 | |||
114 | #ifdef __mips__ | ||
115 | #define mb() asm volatile( \ | ||
116 | ".set mips2\n\t" \ | ||
117 | "sync\n\t" \ | ||
118 | ".set mips0" \ | ||
119 | : /* no output */ \ | ||
120 | : /* no input */ \ | ||
121 | : "memory") | ||
122 | #define wmb() mb() | ||
123 | #define rmb() mb() | ||
124 | #define CPUINFO_PROC "cpu model" | ||
125 | #endif | ||
126 | |||
127 | #ifdef __arc__ | ||
128 | #define mb() asm volatile("" ::: "memory") | ||
129 | #define wmb() asm volatile("" ::: "memory") | ||
130 | #define rmb() asm volatile("" ::: "memory") | ||
131 | #define CPUINFO_PROC "Processor" | ||
132 | #endif | ||
133 | |||
134 | #ifdef __metag__ | ||
135 | #define mb() asm volatile("" ::: "memory") | ||
136 | #define wmb() asm volatile("" ::: "memory") | ||
137 | #define rmb() asm volatile("" ::: "memory") | ||
138 | #define CPUINFO_PROC "CPU" | ||
139 | #endif | ||
140 | |||
141 | #ifdef __xtensa__ | ||
142 | #define mb() asm volatile("memw" ::: "memory") | ||
143 | #define wmb() asm volatile("memw" ::: "memory") | ||
144 | #define rmb() asm volatile("" ::: "memory") | ||
145 | #define CPUINFO_PROC "core ID" | ||
146 | #endif | ||
147 | |||
148 | #ifdef __tile__ | ||
149 | #define mb() asm volatile ("mf" ::: "memory") | ||
150 | #define wmb() asm volatile ("mf" ::: "memory") | ||
151 | #define rmb() asm volatile ("mf" ::: "memory") | ||
152 | #define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory") | ||
153 | #define CPUINFO_PROC "model name" | ||
154 | #endif | ||
155 | |||
156 | #define barrier() asm volatile ("" ::: "memory") | ||
157 | |||
158 | #ifndef cpu_relax | ||
159 | #define cpu_relax() barrier() | ||
160 | #endif | ||
161 | |||
162 | #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) | ||
163 | |||
164 | |||
165 | #include <time.h> | 4 | #include <time.h> |
166 | #include <unistd.h> | ||
167 | #include <sys/types.h> | ||
168 | #include <sys/syscall.h> | ||
169 | |||
170 | #include <linux/perf_event.h> | ||
171 | #include "util/types.h" | ||
172 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <linux/types.h> | ||
7 | #include <linux/perf_event.h> | ||
173 | 8 | ||
174 | /* | 9 | extern bool test_attr__enabled; |
175 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all | 10 | void test_attr__init(void); |
176 | * counters in the current task. | 11 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, |
177 | */ | 12 | int fd, int group_fd, unsigned long flags); |
178 | #define PR_TASK_PERF_EVENTS_DISABLE 31 | 13 | |
179 | #define PR_TASK_PERF_EVENTS_ENABLE 32 | 14 | #define HAVE_ATTR_TEST |
15 | #include "perf-sys.h" | ||
180 | 16 | ||
181 | #ifndef NSEC_PER_SEC | 17 | #ifndef NSEC_PER_SEC |
182 | # define NSEC_PER_SEC 1000000000ULL | 18 | # define NSEC_PER_SEC 1000000000ULL |
@@ -193,67 +29,8 @@ static inline unsigned long long rdclock(void) | |||
193 | return ts.tv_sec * 1000000000ULL + ts.tv_nsec; | 29 | return ts.tv_sec * 1000000000ULL + ts.tv_nsec; |
194 | } | 30 | } |
195 | 31 | ||
196 | /* | ||
197 | * Pick up some kernel type conventions: | ||
198 | */ | ||
199 | #define __user | ||
200 | #define asmlinkage | ||
201 | |||
202 | #define unlikely(x) __builtin_expect(!!(x), 0) | ||
203 | #define min(x, y) ({ \ | ||
204 | typeof(x) _min1 = (x); \ | ||
205 | typeof(y) _min2 = (y); \ | ||
206 | (void) (&_min1 == &_min2); \ | ||
207 | _min1 < _min2 ? _min1 : _min2; }) | ||
208 | |||
209 | extern bool test_attr__enabled; | ||
210 | void test_attr__init(void); | ||
211 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
212 | int fd, int group_fd, unsigned long flags); | ||
213 | |||
214 | static inline int | ||
215 | sys_perf_event_open(struct perf_event_attr *attr, | ||
216 | pid_t pid, int cpu, int group_fd, | ||
217 | unsigned long flags) | ||
218 | { | ||
219 | int fd; | ||
220 | |||
221 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, | ||
222 | group_fd, flags); | ||
223 | |||
224 | if (unlikely(test_attr__enabled)) | ||
225 | test_attr__open(attr, pid, cpu, fd, group_fd, flags); | ||
226 | |||
227 | return fd; | ||
228 | } | ||
229 | |||
230 | #define MAX_COUNTERS 256 | ||
231 | #define MAX_NR_CPUS 256 | 32 | #define MAX_NR_CPUS 256 |
232 | 33 | ||
233 | struct ip_callchain { | ||
234 | u64 nr; | ||
235 | u64 ips[0]; | ||
236 | }; | ||
237 | |||
238 | struct branch_flags { | ||
239 | u64 mispred:1; | ||
240 | u64 predicted:1; | ||
241 | u64 in_tx:1; | ||
242 | u64 abort:1; | ||
243 | u64 reserved:60; | ||
244 | }; | ||
245 | |||
246 | struct branch_entry { | ||
247 | u64 from; | ||
248 | u64 to; | ||
249 | struct branch_flags flags; | ||
250 | }; | ||
251 | |||
252 | struct branch_stack { | ||
253 | u64 nr; | ||
254 | struct branch_entry entries[0]; | ||
255 | }; | ||
256 | |||
257 | extern const char *input_name; | 34 | extern const char *input_name; |
258 | extern bool perf_host, perf_guest; | 35 | extern bool perf_host, perf_guest; |
259 | extern const char perf_version_string[]; | 36 | extern const char perf_version_string[]; |
@@ -262,13 +39,6 @@ void pthread__unblock_sigwinch(void); | |||
262 | 39 | ||
263 | #include "util/target.h" | 40 | #include "util/target.h" |
264 | 41 | ||
265 | enum perf_call_graph_mode { | ||
266 | CALLCHAIN_NONE, | ||
267 | CALLCHAIN_FP, | ||
268 | CALLCHAIN_DWARF, | ||
269 | CALLCHAIN_MAX | ||
270 | }; | ||
271 | |||
272 | struct record_opts { | 42 | struct record_opts { |
273 | struct target target; | 43 | struct target target; |
274 | int call_graph; | 44 | int call_graph; |
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 00218f503b2e..2dfc9ad0e6f2 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | /* | 1 | /* |
3 | * The struct perf_event_attr test support. | 2 | * The struct perf_event_attr test support. |
4 | * | 3 | * |
@@ -19,14 +18,8 @@ | |||
19 | * permissions. All the event text files are stored there. | 18 | * permissions. All the event text files are stored there. |
20 | */ | 19 | */ |
21 | 20 | ||
22 | /* | ||
23 | * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select | ||
24 | * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. | ||
25 | */ | ||
26 | #define __SANE_USERSPACE_TYPES__ | ||
27 | #include <stdlib.h> | 21 | #include <stdlib.h> |
28 | #include <stdio.h> | 22 | #include <stdio.h> |
29 | #include <inttypes.h> | ||
30 | #include <linux/types.h> | 23 | #include <linux/types.h> |
31 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
32 | #include "../perf.h" | 25 | #include "../perf.h" |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index b11bf8a08430..831f52cae197 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -115,7 +115,7 @@ static struct test { | |||
115 | .desc = "Test parsing with no sample_id_all bit set", | 115 | .desc = "Test parsing with no sample_id_all bit set", |
116 | .func = test__parse_no_sample_id_all, | 116 | .func = test__parse_no_sample_id_all, |
117 | }, | 117 | }, |
118 | #if defined(__x86_64__) || defined(__i386__) | 118 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) |
119 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 119 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
120 | { | 120 | { |
121 | .desc = "Test dwarf unwind", | 121 | .desc = "Test dwarf unwind", |
@@ -124,6 +124,22 @@ static struct test { | |||
124 | #endif | 124 | #endif |
125 | #endif | 125 | #endif |
126 | { | 126 | { |
127 | .desc = "Test filtering hist entries", | ||
128 | .func = test__hists_filter, | ||
129 | }, | ||
130 | { | ||
131 | .desc = "Test mmap thread lookup", | ||
132 | .func = test__mmap_thread_lookup, | ||
133 | }, | ||
134 | { | ||
135 | .desc = "Test thread mg sharing", | ||
136 | .func = test__thread_mg_share, | ||
137 | }, | ||
138 | { | ||
139 | .desc = "Test output sorting of hist entries", | ||
140 | .func = test__hists_output, | ||
141 | }, | ||
142 | { | ||
127 | .func = NULL, | 143 | .func = NULL, |
128 | }, | 144 | }, |
129 | }; | 145 | }; |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index bfb186900ac0..67f2d6323558 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
@@ -1,8 +1,7 @@ | |||
1 | #include <sys/types.h> | 1 | #include <linux/types.h> |
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <unistd.h> | 3 | #include <unistd.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <inttypes.h> | ||
6 | #include <ctype.h> | 5 | #include <ctype.h> |
7 | #include <string.h> | 6 | #include <string.h> |
8 | 7 | ||
@@ -257,7 +256,7 @@ static int process_sample_event(struct machine *machine, | |||
257 | return -1; | 256 | return -1; |
258 | } | 257 | } |
259 | 258 | ||
260 | thread = machine__findnew_thread(machine, sample.pid, sample.pid); | 259 | thread = machine__findnew_thread(machine, sample.pid, sample.tid); |
261 | if (!thread) { | 260 | if (!thread) { |
262 | pr_debug("machine__findnew_thread failed\n"); | 261 | pr_debug("machine__findnew_thread failed\n"); |
263 | return -1; | 262 | return -1; |
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 9cc81a3eb9b4..3e6cb171e3d3 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c | |||
@@ -1,7 +1,7 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | 2 | ||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <sys/types.h> | 4 | #include <linux/types.h> |
5 | #include <sys/stat.h> | 5 | #include <sys/stat.h> |
6 | #include <fcntl.h> | 6 | #include <fcntl.h> |
7 | #include <string.h> | 7 | #include <string.h> |
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index c059ee81c038..108f0cd49f4e 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include <linux/compiler.h> | 1 | #include <linux/compiler.h> |
2 | #include <sys/types.h> | 2 | #include <linux/types.h> |
3 | #include <unistd.h> | 3 | #include <unistd.h> |
4 | #include "tests.h" | 4 | #include "tests.h" |
5 | #include "debug.h" | 5 | #include "debug.h" |
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index 4774f7fbb758..35d7fdb2328d 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c | |||
@@ -74,9 +74,6 @@ int test__perf_evsel__tp_sched_test(void) | |||
74 | if (perf_evsel__test_field(evsel, "prio", 4, true)) | 74 | if (perf_evsel__test_field(evsel, "prio", 4, true)) |
75 | ret = -1; | 75 | ret = -1; |
76 | 76 | ||
77 | if (perf_evsel__test_field(evsel, "success", 4, true)) | ||
78 | ret = -1; | ||
79 | |||
80 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) | 77 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) |
81 | ret = -1; | 78 | ret = -1; |
82 | 79 | ||
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c new file mode 100644 index 000000000000..e4e01aadc3be --- /dev/null +++ b/tools/perf/tests/hists_common.c | |||
@@ -0,0 +1,205 @@ | |||
1 | #include "perf.h" | ||
2 | #include "util/debug.h" | ||
3 | #include "util/symbol.h" | ||
4 | #include "util/sort.h" | ||
5 | #include "util/evsel.h" | ||
6 | #include "util/evlist.h" | ||
7 | #include "util/machine.h" | ||
8 | #include "util/thread.h" | ||
9 | #include "tests/hists_common.h" | ||
10 | |||
11 | static struct { | ||
12 | u32 pid; | ||
13 | const char *comm; | ||
14 | } fake_threads[] = { | ||
15 | { 100, "perf" }, | ||
16 | { 200, "perf" }, | ||
17 | { 300, "bash" }, | ||
18 | }; | ||
19 | |||
20 | static struct { | ||
21 | u32 pid; | ||
22 | u64 start; | ||
23 | const char *filename; | ||
24 | } fake_mmap_info[] = { | ||
25 | { 100, 0x40000, "perf" }, | ||
26 | { 100, 0x50000, "libc" }, | ||
27 | { 100, 0xf0000, "[kernel]" }, | ||
28 | { 200, 0x40000, "perf" }, | ||
29 | { 200, 0x50000, "libc" }, | ||
30 | { 200, 0xf0000, "[kernel]" }, | ||
31 | { 300, 0x40000, "bash" }, | ||
32 | { 300, 0x50000, "libc" }, | ||
33 | { 300, 0xf0000, "[kernel]" }, | ||
34 | }; | ||
35 | |||
36 | struct fake_sym { | ||
37 | u64 start; | ||
38 | u64 length; | ||
39 | const char *name; | ||
40 | }; | ||
41 | |||
42 | static struct fake_sym perf_syms[] = { | ||
43 | { 700, 100, "main" }, | ||
44 | { 800, 100, "run_command" }, | ||
45 | { 900, 100, "cmd_record" }, | ||
46 | }; | ||
47 | |||
48 | static struct fake_sym bash_syms[] = { | ||
49 | { 700, 100, "main" }, | ||
50 | { 800, 100, "xmalloc" }, | ||
51 | { 900, 100, "xfree" }, | ||
52 | }; | ||
53 | |||
54 | static struct fake_sym libc_syms[] = { | ||
55 | { 700, 100, "malloc" }, | ||
56 | { 800, 100, "free" }, | ||
57 | { 900, 100, "realloc" }, | ||
58 | }; | ||
59 | |||
60 | static struct fake_sym kernel_syms[] = { | ||
61 | { 700, 100, "schedule" }, | ||
62 | { 800, 100, "page_fault" }, | ||
63 | { 900, 100, "sys_perf_event_open" }, | ||
64 | }; | ||
65 | |||
66 | static struct { | ||
67 | const char *dso_name; | ||
68 | struct fake_sym *syms; | ||
69 | size_t nr_syms; | ||
70 | } fake_symbols[] = { | ||
71 | { "perf", perf_syms, ARRAY_SIZE(perf_syms) }, | ||
72 | { "bash", bash_syms, ARRAY_SIZE(bash_syms) }, | ||
73 | { "libc", libc_syms, ARRAY_SIZE(libc_syms) }, | ||
74 | { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, | ||
75 | }; | ||
76 | |||
77 | struct machine *setup_fake_machine(struct machines *machines) | ||
78 | { | ||
79 | struct machine *machine = machines__find(machines, HOST_KERNEL_ID); | ||
80 | size_t i; | ||
81 | |||
82 | if (machine == NULL) { | ||
83 | pr_debug("Not enough memory for machine setup\n"); | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | ||
88 | struct thread *thread; | ||
89 | |||
90 | thread = machine__findnew_thread(machine, fake_threads[i].pid, | ||
91 | fake_threads[i].pid); | ||
92 | if (thread == NULL) | ||
93 | goto out; | ||
94 | |||
95 | thread__set_comm(thread, fake_threads[i].comm, 0); | ||
96 | } | ||
97 | |||
98 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | ||
99 | union perf_event fake_mmap_event = { | ||
100 | .mmap = { | ||
101 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
102 | .pid = fake_mmap_info[i].pid, | ||
103 | .tid = fake_mmap_info[i].pid, | ||
104 | .start = fake_mmap_info[i].start, | ||
105 | .len = 0x1000ULL, | ||
106 | .pgoff = 0ULL, | ||
107 | }, | ||
108 | }; | ||
109 | |||
110 | strcpy(fake_mmap_event.mmap.filename, | ||
111 | fake_mmap_info[i].filename); | ||
112 | |||
113 | machine__process_mmap_event(machine, &fake_mmap_event, NULL); | ||
114 | } | ||
115 | |||
116 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | ||
117 | size_t k; | ||
118 | struct dso *dso; | ||
119 | |||
120 | dso = __dsos__findnew(&machine->user_dsos, | ||
121 | fake_symbols[i].dso_name); | ||
122 | if (dso == NULL) | ||
123 | goto out; | ||
124 | |||
125 | /* emulate dso__load() */ | ||
126 | dso__set_loaded(dso, MAP__FUNCTION); | ||
127 | |||
128 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { | ||
129 | struct symbol *sym; | ||
130 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; | ||
131 | |||
132 | sym = symbol__new(fsym->start, fsym->length, | ||
133 | STB_GLOBAL, fsym->name); | ||
134 | if (sym == NULL) | ||
135 | goto out; | ||
136 | |||
137 | symbols__insert(&dso->symbols[MAP__FUNCTION], sym); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | return machine; | ||
142 | |||
143 | out: | ||
144 | pr_debug("Not enough memory for machine setup\n"); | ||
145 | machine__delete_threads(machine); | ||
146 | machine__delete(machine); | ||
147 | return NULL; | ||
148 | } | ||
149 | |||
150 | void print_hists_in(struct hists *hists) | ||
151 | { | ||
152 | int i = 0; | ||
153 | struct rb_root *root; | ||
154 | struct rb_node *node; | ||
155 | |||
156 | if (sort__need_collapse) | ||
157 | root = &hists->entries_collapsed; | ||
158 | else | ||
159 | root = hists->entries_in; | ||
160 | |||
161 | pr_info("----- %s --------\n", __func__); | ||
162 | node = rb_first(root); | ||
163 | while (node) { | ||
164 | struct hist_entry *he; | ||
165 | |||
166 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
167 | |||
168 | if (!he->filtered) { | ||
169 | pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", | ||
170 | i, thread__comm_str(he->thread), | ||
171 | he->ms.map->dso->short_name, | ||
172 | he->ms.sym->name, he->stat.period); | ||
173 | } | ||
174 | |||
175 | i++; | ||
176 | node = rb_next(node); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | void print_hists_out(struct hists *hists) | ||
181 | { | ||
182 | int i = 0; | ||
183 | struct rb_root *root; | ||
184 | struct rb_node *node; | ||
185 | |||
186 | root = &hists->entries; | ||
187 | |||
188 | pr_info("----- %s --------\n", __func__); | ||
189 | node = rb_first(root); | ||
190 | while (node) { | ||
191 | struct hist_entry *he; | ||
192 | |||
193 | he = rb_entry(node, struct hist_entry, rb_node); | ||
194 | |||
195 | if (!he->filtered) { | ||
196 | pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %"PRIu64"\n", | ||
197 | i, thread__comm_str(he->thread), he->thread->tid, | ||
198 | he->ms.map->dso->short_name, | ||
199 | he->ms.sym->name, he->stat.period); | ||
200 | } | ||
201 | |||
202 | i++; | ||
203 | node = rb_next(node); | ||
204 | } | ||
205 | } | ||
diff --git a/tools/perf/tests/hists_common.h b/tools/perf/tests/hists_common.h new file mode 100644 index 000000000000..1415ae69d7b6 --- /dev/null +++ b/tools/perf/tests/hists_common.h | |||
@@ -0,0 +1,47 @@ | |||
1 | #ifndef __PERF_TESTS__HISTS_COMMON_H__ | ||
2 | #define __PERF_TESTS__HISTS_COMMON_H__ | ||
3 | |||
4 | struct machine; | ||
5 | struct machines; | ||
6 | |||
7 | /* | ||
8 | * The setup_fake_machine() provides a test environment which consists | ||
9 | * of 3 processes that have 3 mappings and in turn, have 3 symbols | ||
10 | * respectively. See below table: | ||
11 | * | ||
12 | * Command: Pid Shared Object Symbol | ||
13 | * ............. ............. ................... | ||
14 | * perf: 100 perf main | ||
15 | * perf: 100 perf run_command | ||
16 | * perf: 100 perf comd_record | ||
17 | * perf: 100 libc malloc | ||
18 | * perf: 100 libc free | ||
19 | * perf: 100 libc realloc | ||
20 | * perf: 100 [kernel] schedule | ||
21 | * perf: 100 [kernel] page_fault | ||
22 | * perf: 100 [kernel] sys_perf_event_open | ||
23 | * perf: 200 perf main | ||
24 | * perf: 200 perf run_command | ||
25 | * perf: 200 perf comd_record | ||
26 | * perf: 200 libc malloc | ||
27 | * perf: 200 libc free | ||
28 | * perf: 200 libc realloc | ||
29 | * perf: 200 [kernel] schedule | ||
30 | * perf: 200 [kernel] page_fault | ||
31 | * perf: 200 [kernel] sys_perf_event_open | ||
32 | * bash: 300 bash main | ||
33 | * bash: 300 bash xmalloc | ||
34 | * bash: 300 bash xfree | ||
35 | * bash: 300 libc malloc | ||
36 | * bash: 300 libc free | ||
37 | * bash: 300 libc realloc | ||
38 | * bash: 300 [kernel] schedule | ||
39 | * bash: 300 [kernel] page_fault | ||
40 | * bash: 300 [kernel] sys_perf_event_open | ||
41 | */ | ||
42 | struct machine *setup_fake_machine(struct machines *machines); | ||
43 | |||
44 | void print_hists_in(struct hists *hists); | ||
45 | void print_hists_out(struct hists *hists); | ||
46 | |||
47 | #endif /* __PERF_TESTS__HISTS_COMMON_H__ */ | ||
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c new file mode 100644 index 000000000000..c5ba924a3581 --- /dev/null +++ b/tools/perf/tests/hists_filter.c | |||
@@ -0,0 +1,290 @@ | |||
1 | #include "perf.h" | ||
2 | #include "util/debug.h" | ||
3 | #include "util/symbol.h" | ||
4 | #include "util/sort.h" | ||
5 | #include "util/evsel.h" | ||
6 | #include "util/evlist.h" | ||
7 | #include "util/machine.h" | ||
8 | #include "util/thread.h" | ||
9 | #include "util/parse-events.h" | ||
10 | #include "tests/tests.h" | ||
11 | #include "tests/hists_common.h" | ||
12 | |||
13 | struct sample { | ||
14 | u32 pid; | ||
15 | u64 ip; | ||
16 | struct thread *thread; | ||
17 | struct map *map; | ||
18 | struct symbol *sym; | ||
19 | }; | ||
20 | |||
21 | /* For the numbers, see hists_common.c */ | ||
22 | static struct sample fake_samples[] = { | ||
23 | /* perf [kernel] schedule() */ | ||
24 | { .pid = 100, .ip = 0xf0000 + 700, }, | ||
25 | /* perf [perf] main() */ | ||
26 | { .pid = 100, .ip = 0x40000 + 700, }, | ||
27 | /* perf [libc] malloc() */ | ||
28 | { .pid = 100, .ip = 0x50000 + 700, }, | ||
29 | /* perf [perf] main() */ | ||
30 | { .pid = 200, .ip = 0x40000 + 700, }, /* will be merged */ | ||
31 | /* perf [perf] cmd_record() */ | ||
32 | { .pid = 200, .ip = 0x40000 + 900, }, | ||
33 | /* perf [kernel] page_fault() */ | ||
34 | { .pid = 200, .ip = 0xf0000 + 800, }, | ||
35 | /* bash [bash] main() */ | ||
36 | { .pid = 300, .ip = 0x40000 + 700, }, | ||
37 | /* bash [bash] xmalloc() */ | ||
38 | { .pid = 300, .ip = 0x40000 + 800, }, | ||
39 | /* bash [libc] malloc() */ | ||
40 | { .pid = 300, .ip = 0x50000 + 700, }, | ||
41 | /* bash [kernel] page_fault() */ | ||
42 | { .pid = 300, .ip = 0xf0000 + 800, }, | ||
43 | }; | ||
44 | |||
45 | static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | ||
46 | { | ||
47 | struct perf_evsel *evsel; | ||
48 | struct addr_location al; | ||
49 | struct hist_entry *he; | ||
50 | struct perf_sample sample = { .cpu = 0, }; | ||
51 | size_t i; | ||
52 | |||
53 | /* | ||
54 | * each evsel will have 10 samples but the 4th sample | ||
55 | * (perf [perf] main) will be collapsed to an existing entry | ||
56 | * so total 9 entries will be in the tree. | ||
57 | */ | ||
58 | evlist__for_each(evlist, evsel) { | ||
59 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { | ||
60 | const union perf_event event = { | ||
61 | .header = { | ||
62 | .misc = PERF_RECORD_MISC_USER, | ||
63 | }, | ||
64 | }; | ||
65 | |||
66 | /* make sure it has no filter at first */ | ||
67 | evsel->hists.thread_filter = NULL; | ||
68 | evsel->hists.dso_filter = NULL; | ||
69 | evsel->hists.symbol_filter_str = NULL; | ||
70 | |||
71 | sample.pid = fake_samples[i].pid; | ||
72 | sample.tid = fake_samples[i].pid; | ||
73 | sample.ip = fake_samples[i].ip; | ||
74 | |||
75 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
76 | &sample) < 0) | ||
77 | goto out; | ||
78 | |||
79 | he = __hists__add_entry(&evsel->hists, &al, NULL, | ||
80 | NULL, NULL, 100, 1, 0); | ||
81 | if (he == NULL) | ||
82 | goto out; | ||
83 | |||
84 | fake_samples[i].thread = al.thread; | ||
85 | fake_samples[i].map = al.map; | ||
86 | fake_samples[i].sym = al.sym; | ||
87 | |||
88 | hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE); | ||
89 | if (!he->filtered) | ||
90 | he->hists->stats.nr_non_filtered_samples++; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | return 0; | ||
95 | |||
96 | out: | ||
97 | pr_debug("Not enough memory for adding a hist entry\n"); | ||
98 | return TEST_FAIL; | ||
99 | } | ||
100 | |||
101 | int test__hists_filter(void) | ||
102 | { | ||
103 | int err = TEST_FAIL; | ||
104 | struct machines machines; | ||
105 | struct machine *machine; | ||
106 | struct perf_evsel *evsel; | ||
107 | struct perf_evlist *evlist = perf_evlist__new(); | ||
108 | |||
109 | TEST_ASSERT_VAL("No memory", evlist); | ||
110 | |||
111 | err = parse_events(evlist, "cpu-clock"); | ||
112 | if (err) | ||
113 | goto out; | ||
114 | err = parse_events(evlist, "task-clock"); | ||
115 | if (err) | ||
116 | goto out; | ||
117 | |||
118 | /* default sort order (comm,dso,sym) will be used */ | ||
119 | if (setup_sorting() < 0) | ||
120 | goto out; | ||
121 | |||
122 | machines__init(&machines); | ||
123 | |||
124 | /* setup threads/dso/map/symbols also */ | ||
125 | machine = setup_fake_machine(&machines); | ||
126 | if (!machine) | ||
127 | goto out; | ||
128 | |||
129 | if (verbose > 1) | ||
130 | machine__fprintf(machine, stderr); | ||
131 | |||
132 | /* process sample events */ | ||
133 | err = add_hist_entries(evlist, machine); | ||
134 | if (err < 0) | ||
135 | goto out; | ||
136 | |||
137 | evlist__for_each(evlist, evsel) { | ||
138 | struct hists *hists = &evsel->hists; | ||
139 | |||
140 | hists__collapse_resort(hists, NULL); | ||
141 | hists__output_resort(hists); | ||
142 | |||
143 | if (verbose > 2) { | ||
144 | pr_info("Normal histogram\n"); | ||
145 | print_hists_out(hists); | ||
146 | } | ||
147 | |||
148 | TEST_ASSERT_VAL("Invalid nr samples", | ||
149 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
150 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
151 | hists->nr_entries == 9); | ||
152 | TEST_ASSERT_VAL("Invalid total period", | ||
153 | hists->stats.total_period == 1000); | ||
154 | TEST_ASSERT_VAL("Unmatched nr samples", | ||
155 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == | ||
156 | hists->stats.nr_non_filtered_samples); | ||
157 | TEST_ASSERT_VAL("Unmatched nr hist entries", | ||
158 | hists->nr_entries == hists->nr_non_filtered_entries); | ||
159 | TEST_ASSERT_VAL("Unmatched total period", | ||
160 | hists->stats.total_period == | ||
161 | hists->stats.total_non_filtered_period); | ||
162 | |||
163 | /* now applying thread filter for 'bash' */ | ||
164 | evsel->hists.thread_filter = fake_samples[9].thread; | ||
165 | hists__filter_by_thread(hists); | ||
166 | |||
167 | if (verbose > 2) { | ||
168 | pr_info("Histogram for thread filter\n"); | ||
169 | print_hists_out(hists); | ||
170 | } | ||
171 | |||
172 | /* normal stats should be invariant */ | ||
173 | TEST_ASSERT_VAL("Invalid nr samples", | ||
174 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
175 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
176 | hists->nr_entries == 9); | ||
177 | TEST_ASSERT_VAL("Invalid total period", | ||
178 | hists->stats.total_period == 1000); | ||
179 | |||
180 | /* but filter stats are changed */ | ||
181 | TEST_ASSERT_VAL("Unmatched nr samples for thread filter", | ||
182 | hists->stats.nr_non_filtered_samples == 4); | ||
183 | TEST_ASSERT_VAL("Unmatched nr hist entries for thread filter", | ||
184 | hists->nr_non_filtered_entries == 4); | ||
185 | TEST_ASSERT_VAL("Unmatched total period for thread filter", | ||
186 | hists->stats.total_non_filtered_period == 400); | ||
187 | |||
188 | /* remove thread filter first */ | ||
189 | evsel->hists.thread_filter = NULL; | ||
190 | hists__filter_by_thread(hists); | ||
191 | |||
192 | /* now applying dso filter for 'kernel' */ | ||
193 | evsel->hists.dso_filter = fake_samples[0].map->dso; | ||
194 | hists__filter_by_dso(hists); | ||
195 | |||
196 | if (verbose > 2) { | ||
197 | pr_info("Histogram for dso filter\n"); | ||
198 | print_hists_out(hists); | ||
199 | } | ||
200 | |||
201 | /* normal stats should be invariant */ | ||
202 | TEST_ASSERT_VAL("Invalid nr samples", | ||
203 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
204 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
205 | hists->nr_entries == 9); | ||
206 | TEST_ASSERT_VAL("Invalid total period", | ||
207 | hists->stats.total_period == 1000); | ||
208 | |||
209 | /* but filter stats are changed */ | ||
210 | TEST_ASSERT_VAL("Unmatched nr samples for dso filter", | ||
211 | hists->stats.nr_non_filtered_samples == 3); | ||
212 | TEST_ASSERT_VAL("Unmatched nr hist entries for dso filter", | ||
213 | hists->nr_non_filtered_entries == 3); | ||
214 | TEST_ASSERT_VAL("Unmatched total period for dso filter", | ||
215 | hists->stats.total_non_filtered_period == 300); | ||
216 | |||
217 | /* remove dso filter first */ | ||
218 | evsel->hists.dso_filter = NULL; | ||
219 | hists__filter_by_dso(hists); | ||
220 | |||
221 | /* | ||
222 | * now applying symbol filter for 'main'. Also note that | ||
223 | * there's 3 samples that have 'main' symbol but the 4th | ||
224 | * entry of fake_samples was collapsed already so it won't | ||
225 | * be counted as a separate entry but the sample count and | ||
226 | * total period will be remained. | ||
227 | */ | ||
228 | evsel->hists.symbol_filter_str = "main"; | ||
229 | hists__filter_by_symbol(hists); | ||
230 | |||
231 | if (verbose > 2) { | ||
232 | pr_info("Histogram for symbol filter\n"); | ||
233 | print_hists_out(hists); | ||
234 | } | ||
235 | |||
236 | /* normal stats should be invariant */ | ||
237 | TEST_ASSERT_VAL("Invalid nr samples", | ||
238 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
239 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
240 | hists->nr_entries == 9); | ||
241 | TEST_ASSERT_VAL("Invalid total period", | ||
242 | hists->stats.total_period == 1000); | ||
243 | |||
244 | /* but filter stats are changed */ | ||
245 | TEST_ASSERT_VAL("Unmatched nr samples for symbol filter", | ||
246 | hists->stats.nr_non_filtered_samples == 3); | ||
247 | TEST_ASSERT_VAL("Unmatched nr hist entries for symbol filter", | ||
248 | hists->nr_non_filtered_entries == 2); | ||
249 | TEST_ASSERT_VAL("Unmatched total period for symbol filter", | ||
250 | hists->stats.total_non_filtered_period == 300); | ||
251 | |||
252 | /* now applying all filters at once. */ | ||
253 | evsel->hists.thread_filter = fake_samples[1].thread; | ||
254 | evsel->hists.dso_filter = fake_samples[1].map->dso; | ||
255 | hists__filter_by_thread(hists); | ||
256 | hists__filter_by_dso(hists); | ||
257 | |||
258 | if (verbose > 2) { | ||
259 | pr_info("Histogram for all filters\n"); | ||
260 | print_hists_out(hists); | ||
261 | } | ||
262 | |||
263 | /* normal stats should be invariant */ | ||
264 | TEST_ASSERT_VAL("Invalid nr samples", | ||
265 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
266 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
267 | hists->nr_entries == 9); | ||
268 | TEST_ASSERT_VAL("Invalid total period", | ||
269 | hists->stats.total_period == 1000); | ||
270 | |||
271 | /* but filter stats are changed */ | ||
272 | TEST_ASSERT_VAL("Unmatched nr samples for all filter", | ||
273 | hists->stats.nr_non_filtered_samples == 2); | ||
274 | TEST_ASSERT_VAL("Unmatched nr hist entries for all filter", | ||
275 | hists->nr_non_filtered_entries == 1); | ||
276 | TEST_ASSERT_VAL("Unmatched total period for all filter", | ||
277 | hists->stats.total_non_filtered_period == 200); | ||
278 | } | ||
279 | |||
280 | |||
281 | err = TEST_OK; | ||
282 | |||
283 | out: | ||
284 | /* tear down everything */ | ||
285 | perf_evlist__delete(evlist); | ||
286 | reset_output_field(); | ||
287 | machines__exit(&machines); | ||
288 | |||
289 | return err; | ||
290 | } | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 7ccbc7b6ae77..5ffa2c3eb77d 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -8,145 +8,7 @@ | |||
8 | #include "machine.h" | 8 | #include "machine.h" |
9 | #include "thread.h" | 9 | #include "thread.h" |
10 | #include "parse-events.h" | 10 | #include "parse-events.h" |
11 | 11 | #include "hists_common.h" | |
12 | static struct { | ||
13 | u32 pid; | ||
14 | const char *comm; | ||
15 | } fake_threads[] = { | ||
16 | { 100, "perf" }, | ||
17 | { 200, "perf" }, | ||
18 | { 300, "bash" }, | ||
19 | }; | ||
20 | |||
21 | static struct { | ||
22 | u32 pid; | ||
23 | u64 start; | ||
24 | const char *filename; | ||
25 | } fake_mmap_info[] = { | ||
26 | { 100, 0x40000, "perf" }, | ||
27 | { 100, 0x50000, "libc" }, | ||
28 | { 100, 0xf0000, "[kernel]" }, | ||
29 | { 200, 0x40000, "perf" }, | ||
30 | { 200, 0x50000, "libc" }, | ||
31 | { 200, 0xf0000, "[kernel]" }, | ||
32 | { 300, 0x40000, "bash" }, | ||
33 | { 300, 0x50000, "libc" }, | ||
34 | { 300, 0xf0000, "[kernel]" }, | ||
35 | }; | ||
36 | |||
37 | struct fake_sym { | ||
38 | u64 start; | ||
39 | u64 length; | ||
40 | const char *name; | ||
41 | }; | ||
42 | |||
43 | static struct fake_sym perf_syms[] = { | ||
44 | { 700, 100, "main" }, | ||
45 | { 800, 100, "run_command" }, | ||
46 | { 900, 100, "cmd_record" }, | ||
47 | }; | ||
48 | |||
49 | static struct fake_sym bash_syms[] = { | ||
50 | { 700, 100, "main" }, | ||
51 | { 800, 100, "xmalloc" }, | ||
52 | { 900, 100, "xfree" }, | ||
53 | }; | ||
54 | |||
55 | static struct fake_sym libc_syms[] = { | ||
56 | { 700, 100, "malloc" }, | ||
57 | { 800, 100, "free" }, | ||
58 | { 900, 100, "realloc" }, | ||
59 | }; | ||
60 | |||
61 | static struct fake_sym kernel_syms[] = { | ||
62 | { 700, 100, "schedule" }, | ||
63 | { 800, 100, "page_fault" }, | ||
64 | { 900, 100, "sys_perf_event_open" }, | ||
65 | }; | ||
66 | |||
67 | static struct { | ||
68 | const char *dso_name; | ||
69 | struct fake_sym *syms; | ||
70 | size_t nr_syms; | ||
71 | } fake_symbols[] = { | ||
72 | { "perf", perf_syms, ARRAY_SIZE(perf_syms) }, | ||
73 | { "bash", bash_syms, ARRAY_SIZE(bash_syms) }, | ||
74 | { "libc", libc_syms, ARRAY_SIZE(libc_syms) }, | ||
75 | { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, | ||
76 | }; | ||
77 | |||
78 | static struct machine *setup_fake_machine(struct machines *machines) | ||
79 | { | ||
80 | struct machine *machine = machines__find(machines, HOST_KERNEL_ID); | ||
81 | size_t i; | ||
82 | |||
83 | if (machine == NULL) { | ||
84 | pr_debug("Not enough memory for machine setup\n"); | ||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | ||
89 | struct thread *thread; | ||
90 | |||
91 | thread = machine__findnew_thread(machine, fake_threads[i].pid, | ||
92 | fake_threads[i].pid); | ||
93 | if (thread == NULL) | ||
94 | goto out; | ||
95 | |||
96 | thread__set_comm(thread, fake_threads[i].comm, 0); | ||
97 | } | ||
98 | |||
99 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | ||
100 | union perf_event fake_mmap_event = { | ||
101 | .mmap = { | ||
102 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
103 | .pid = fake_mmap_info[i].pid, | ||
104 | .tid = fake_mmap_info[i].pid, | ||
105 | .start = fake_mmap_info[i].start, | ||
106 | .len = 0x1000ULL, | ||
107 | .pgoff = 0ULL, | ||
108 | }, | ||
109 | }; | ||
110 | |||
111 | strcpy(fake_mmap_event.mmap.filename, | ||
112 | fake_mmap_info[i].filename); | ||
113 | |||
114 | machine__process_mmap_event(machine, &fake_mmap_event, NULL); | ||
115 | } | ||
116 | |||
117 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | ||
118 | size_t k; | ||
119 | struct dso *dso; | ||
120 | |||
121 | dso = __dsos__findnew(&machine->user_dsos, | ||
122 | fake_symbols[i].dso_name); | ||
123 | if (dso == NULL) | ||
124 | goto out; | ||
125 | |||
126 | /* emulate dso__load() */ | ||
127 | dso__set_loaded(dso, MAP__FUNCTION); | ||
128 | |||
129 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { | ||
130 | struct symbol *sym; | ||
131 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; | ||
132 | |||
133 | sym = symbol__new(fsym->start, fsym->length, | ||
134 | STB_GLOBAL, fsym->name); | ||
135 | if (sym == NULL) | ||
136 | goto out; | ||
137 | |||
138 | symbols__insert(&dso->symbols[MAP__FUNCTION], sym); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | return machine; | ||
143 | |||
144 | out: | ||
145 | pr_debug("Not enough memory for machine setup\n"); | ||
146 | machine__delete_threads(machine); | ||
147 | machine__delete(machine); | ||
148 | return NULL; | ||
149 | } | ||
150 | 12 | ||
151 | struct sample { | 13 | struct sample { |
152 | u32 pid; | 14 | u32 pid; |
@@ -156,6 +18,7 @@ struct sample { | |||
156 | struct symbol *sym; | 18 | struct symbol *sym; |
157 | }; | 19 | }; |
158 | 20 | ||
21 | /* For the numbers, see hists_common.c */ | ||
159 | static struct sample fake_common_samples[] = { | 22 | static struct sample fake_common_samples[] = { |
160 | /* perf [kernel] schedule() */ | 23 | /* perf [kernel] schedule() */ |
161 | { .pid = 100, .ip = 0xf0000 + 700, }, | 24 | { .pid = 100, .ip = 0xf0000 + 700, }, |
@@ -218,6 +81,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
218 | }; | 81 | }; |
219 | 82 | ||
220 | sample.pid = fake_common_samples[k].pid; | 83 | sample.pid = fake_common_samples[k].pid; |
84 | sample.tid = fake_common_samples[k].pid; | ||
221 | sample.ip = fake_common_samples[k].ip; | 85 | sample.ip = fake_common_samples[k].ip; |
222 | if (perf_event__preprocess_sample(&event, machine, &al, | 86 | if (perf_event__preprocess_sample(&event, machine, &al, |
223 | &sample) < 0) | 87 | &sample) < 0) |
@@ -241,6 +105,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
241 | }; | 105 | }; |
242 | 106 | ||
243 | sample.pid = fake_samples[i][k].pid; | 107 | sample.pid = fake_samples[i][k].pid; |
108 | sample.tid = fake_samples[i][k].pid; | ||
244 | sample.ip = fake_samples[i][k].ip; | 109 | sample.ip = fake_samples[i][k].ip; |
245 | if (perf_event__preprocess_sample(&event, machine, &al, | 110 | if (perf_event__preprocess_sample(&event, machine, &al, |
246 | &sample) < 0) | 111 | &sample) < 0) |
@@ -403,33 +268,6 @@ static int validate_link(struct hists *leader, struct hists *other) | |||
403 | return __validate_link(leader, 0) || __validate_link(other, 1); | 268 | return __validate_link(leader, 0) || __validate_link(other, 1); |
404 | } | 269 | } |
405 | 270 | ||
406 | static void print_hists(struct hists *hists) | ||
407 | { | ||
408 | int i = 0; | ||
409 | struct rb_root *root; | ||
410 | struct rb_node *node; | ||
411 | |||
412 | if (sort__need_collapse) | ||
413 | root = &hists->entries_collapsed; | ||
414 | else | ||
415 | root = hists->entries_in; | ||
416 | |||
417 | pr_info("----- %s --------\n", __func__); | ||
418 | node = rb_first(root); | ||
419 | while (node) { | ||
420 | struct hist_entry *he; | ||
421 | |||
422 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
423 | |||
424 | pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", | ||
425 | i, thread__comm_str(he->thread), he->ms.map->dso->short_name, | ||
426 | he->ms.sym->name, he->stat.period); | ||
427 | |||
428 | i++; | ||
429 | node = rb_next(node); | ||
430 | } | ||
431 | } | ||
432 | |||
433 | int test__hists_link(void) | 271 | int test__hists_link(void) |
434 | { | 272 | { |
435 | int err = -1; | 273 | int err = -1; |
@@ -471,7 +309,7 @@ int test__hists_link(void) | |||
471 | hists__collapse_resort(&evsel->hists, NULL); | 309 | hists__collapse_resort(&evsel->hists, NULL); |
472 | 310 | ||
473 | if (verbose > 2) | 311 | if (verbose > 2) |
474 | print_hists(&evsel->hists); | 312 | print_hists_in(&evsel->hists); |
475 | } | 313 | } |
476 | 314 | ||
477 | first = perf_evlist__first(evlist); | 315 | first = perf_evlist__first(evlist); |
@@ -494,6 +332,7 @@ int test__hists_link(void) | |||
494 | out: | 332 | out: |
495 | /* tear down everything */ | 333 | /* tear down everything */ |
496 | perf_evlist__delete(evlist); | 334 | perf_evlist__delete(evlist); |
335 | reset_output_field(); | ||
497 | machines__exit(&machines); | 336 | machines__exit(&machines); |
498 | 337 | ||
499 | return err; | 338 | return err; |
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c new file mode 100644 index 000000000000..a16850551797 --- /dev/null +++ b/tools/perf/tests/hists_output.c | |||
@@ -0,0 +1,618 @@ | |||
1 | #include "perf.h" | ||
2 | #include "util/debug.h" | ||
3 | #include "util/symbol.h" | ||
4 | #include "util/sort.h" | ||
5 | #include "util/evsel.h" | ||
6 | #include "util/evlist.h" | ||
7 | #include "util/machine.h" | ||
8 | #include "util/thread.h" | ||
9 | #include "util/parse-events.h" | ||
10 | #include "tests/tests.h" | ||
11 | #include "tests/hists_common.h" | ||
12 | |||
13 | struct sample { | ||
14 | u32 cpu; | ||
15 | u32 pid; | ||
16 | u64 ip; | ||
17 | struct thread *thread; | ||
18 | struct map *map; | ||
19 | struct symbol *sym; | ||
20 | }; | ||
21 | |||
22 | /* For the numbers, see hists_common.c */ | ||
23 | static struct sample fake_samples[] = { | ||
24 | /* perf [kernel] schedule() */ | ||
25 | { .cpu = 0, .pid = 100, .ip = 0xf0000 + 700, }, | ||
26 | /* perf [perf] main() */ | ||
27 | { .cpu = 1, .pid = 100, .ip = 0x40000 + 700, }, | ||
28 | /* perf [perf] cmd_record() */ | ||
29 | { .cpu = 1, .pid = 100, .ip = 0x40000 + 900, }, | ||
30 | /* perf [libc] malloc() */ | ||
31 | { .cpu = 1, .pid = 100, .ip = 0x50000 + 700, }, | ||
32 | /* perf [libc] free() */ | ||
33 | { .cpu = 2, .pid = 100, .ip = 0x50000 + 800, }, | ||
34 | /* perf [perf] main() */ | ||
35 | { .cpu = 2, .pid = 200, .ip = 0x40000 + 700, }, | ||
36 | /* perf [kernel] page_fault() */ | ||
37 | { .cpu = 2, .pid = 200, .ip = 0xf0000 + 800, }, | ||
38 | /* bash [bash] main() */ | ||
39 | { .cpu = 3, .pid = 300, .ip = 0x40000 + 700, }, | ||
40 | /* bash [bash] xmalloc() */ | ||
41 | { .cpu = 0, .pid = 300, .ip = 0x40000 + 800, }, | ||
42 | /* bash [kernel] page_fault() */ | ||
43 | { .cpu = 1, .pid = 300, .ip = 0xf0000 + 800, }, | ||
44 | }; | ||
45 | |||
46 | static int add_hist_entries(struct hists *hists, struct machine *machine) | ||
47 | { | ||
48 | struct addr_location al; | ||
49 | struct hist_entry *he; | ||
50 | struct perf_sample sample = { .period = 100, }; | ||
51 | size_t i; | ||
52 | |||
53 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { | ||
54 | const union perf_event event = { | ||
55 | .header = { | ||
56 | .misc = PERF_RECORD_MISC_USER, | ||
57 | }, | ||
58 | }; | ||
59 | |||
60 | sample.cpu = fake_samples[i].cpu; | ||
61 | sample.pid = fake_samples[i].pid; | ||
62 | sample.tid = fake_samples[i].pid; | ||
63 | sample.ip = fake_samples[i].ip; | ||
64 | |||
65 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
66 | &sample) < 0) | ||
67 | goto out; | ||
68 | |||
69 | he = __hists__add_entry(hists, &al, NULL, NULL, NULL, | ||
70 | sample.period, 1, 0); | ||
71 | if (he == NULL) | ||
72 | goto out; | ||
73 | |||
74 | fake_samples[i].thread = al.thread; | ||
75 | fake_samples[i].map = al.map; | ||
76 | fake_samples[i].sym = al.sym; | ||
77 | } | ||
78 | |||
79 | return TEST_OK; | ||
80 | |||
81 | out: | ||
82 | pr_debug("Not enough memory for adding a hist entry\n"); | ||
83 | return TEST_FAIL; | ||
84 | } | ||
85 | |||
86 | static void del_hist_entries(struct hists *hists) | ||
87 | { | ||
88 | struct hist_entry *he; | ||
89 | struct rb_root *root_in; | ||
90 | struct rb_root *root_out; | ||
91 | struct rb_node *node; | ||
92 | |||
93 | if (sort__need_collapse) | ||
94 | root_in = &hists->entries_collapsed; | ||
95 | else | ||
96 | root_in = hists->entries_in; | ||
97 | |||
98 | root_out = &hists->entries; | ||
99 | |||
100 | while (!RB_EMPTY_ROOT(root_out)) { | ||
101 | node = rb_first(root_out); | ||
102 | |||
103 | he = rb_entry(node, struct hist_entry, rb_node); | ||
104 | rb_erase(node, root_out); | ||
105 | rb_erase(&he->rb_node_in, root_in); | ||
106 | hist_entry__free(he); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | typedef int (*test_fn_t)(struct perf_evsel *, struct machine *); | ||
111 | |||
112 | #define COMM(he) (thread__comm_str(he->thread)) | ||
113 | #define DSO(he) (he->ms.map->dso->short_name) | ||
114 | #define SYM(he) (he->ms.sym->name) | ||
115 | #define CPU(he) (he->cpu) | ||
116 | #define PID(he) (he->thread->tid) | ||
117 | |||
118 | /* default sort keys (no field) */ | ||
119 | static int test1(struct perf_evsel *evsel, struct machine *machine) | ||
120 | { | ||
121 | int err; | ||
122 | struct hists *hists = &evsel->hists; | ||
123 | struct hist_entry *he; | ||
124 | struct rb_root *root; | ||
125 | struct rb_node *node; | ||
126 | |||
127 | field_order = NULL; | ||
128 | sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */ | ||
129 | |||
130 | setup_sorting(); | ||
131 | |||
132 | /* | ||
133 | * expected output: | ||
134 | * | ||
135 | * Overhead Command Shared Object Symbol | ||
136 | * ======== ======= ============= ============== | ||
137 | * 20.00% perf perf [.] main | ||
138 | * 10.00% bash [kernel] [k] page_fault | ||
139 | * 10.00% bash bash [.] main | ||
140 | * 10.00% bash bash [.] xmalloc | ||
141 | * 10.00% perf [kernel] [k] page_fault | ||
142 | * 10.00% perf [kernel] [k] schedule | ||
143 | * 10.00% perf libc [.] free | ||
144 | * 10.00% perf libc [.] malloc | ||
145 | * 10.00% perf perf [.] cmd_record | ||
146 | */ | ||
147 | err = add_hist_entries(hists, machine); | ||
148 | if (err < 0) | ||
149 | goto out; | ||
150 | |||
151 | hists__collapse_resort(hists, NULL); | ||
152 | hists__output_resort(hists); | ||
153 | |||
154 | if (verbose > 2) { | ||
155 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | ||
156 | print_hists_out(hists); | ||
157 | } | ||
158 | |||
159 | root = &evsel->hists.entries; | ||
160 | node = rb_first(root); | ||
161 | he = rb_entry(node, struct hist_entry, rb_node); | ||
162 | TEST_ASSERT_VAL("Invalid hist entry", | ||
163 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && | ||
164 | !strcmp(SYM(he), "main") && he->stat.period == 200); | ||
165 | |||
166 | node = rb_next(node); | ||
167 | he = rb_entry(node, struct hist_entry, rb_node); | ||
168 | TEST_ASSERT_VAL("Invalid hist entry", | ||
169 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && | ||
170 | !strcmp(SYM(he), "page_fault") && he->stat.period == 100); | ||
171 | |||
172 | node = rb_next(node); | ||
173 | he = rb_entry(node, struct hist_entry, rb_node); | ||
174 | TEST_ASSERT_VAL("Invalid hist entry", | ||
175 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && | ||
176 | !strcmp(SYM(he), "main") && he->stat.period == 100); | ||
177 | |||
178 | node = rb_next(node); | ||
179 | he = rb_entry(node, struct hist_entry, rb_node); | ||
180 | TEST_ASSERT_VAL("Invalid hist entry", | ||
181 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && | ||
182 | !strcmp(SYM(he), "xmalloc") && he->stat.period == 100); | ||
183 | |||
184 | node = rb_next(node); | ||
185 | he = rb_entry(node, struct hist_entry, rb_node); | ||
186 | TEST_ASSERT_VAL("Invalid hist entry", | ||
187 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && | ||
188 | !strcmp(SYM(he), "page_fault") && he->stat.period == 100); | ||
189 | |||
190 | node = rb_next(node); | ||
191 | he = rb_entry(node, struct hist_entry, rb_node); | ||
192 | TEST_ASSERT_VAL("Invalid hist entry", | ||
193 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && | ||
194 | !strcmp(SYM(he), "schedule") && he->stat.period == 100); | ||
195 | |||
196 | node = rb_next(node); | ||
197 | he = rb_entry(node, struct hist_entry, rb_node); | ||
198 | TEST_ASSERT_VAL("Invalid hist entry", | ||
199 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && | ||
200 | !strcmp(SYM(he), "free") && he->stat.period == 100); | ||
201 | |||
202 | node = rb_next(node); | ||
203 | he = rb_entry(node, struct hist_entry, rb_node); | ||
204 | TEST_ASSERT_VAL("Invalid hist entry", | ||
205 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && | ||
206 | !strcmp(SYM(he), "malloc") && he->stat.period == 100); | ||
207 | |||
208 | node = rb_next(node); | ||
209 | he = rb_entry(node, struct hist_entry, rb_node); | ||
210 | TEST_ASSERT_VAL("Invalid hist entry", | ||
211 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && | ||
212 | !strcmp(SYM(he), "cmd_record") && he->stat.period == 100); | ||
213 | |||
214 | out: | ||
215 | del_hist_entries(hists); | ||
216 | reset_output_field(); | ||
217 | return err; | ||
218 | } | ||
219 | |||
220 | /* mixed fields and sort keys */ | ||
221 | static int test2(struct perf_evsel *evsel, struct machine *machine) | ||
222 | { | ||
223 | int err; | ||
224 | struct hists *hists = &evsel->hists; | ||
225 | struct hist_entry *he; | ||
226 | struct rb_root *root; | ||
227 | struct rb_node *node; | ||
228 | |||
229 | field_order = "overhead,cpu"; | ||
230 | sort_order = "pid"; | ||
231 | |||
232 | setup_sorting(); | ||
233 | |||
234 | /* | ||
235 | * expected output: | ||
236 | * | ||
237 | * Overhead CPU Command: Pid | ||
238 | * ======== === ============= | ||
239 | * 30.00% 1 perf : 100 | ||
240 | * 10.00% 0 perf : 100 | ||
241 | * 10.00% 2 perf : 100 | ||
242 | * 20.00% 2 perf : 200 | ||
243 | * 10.00% 0 bash : 300 | ||
244 | * 10.00% 1 bash : 300 | ||
245 | * 10.00% 3 bash : 300 | ||
246 | */ | ||
247 | err = add_hist_entries(hists, machine); | ||
248 | if (err < 0) | ||
249 | goto out; | ||
250 | |||
251 | hists__collapse_resort(hists, NULL); | ||
252 | hists__output_resort(hists); | ||
253 | |||
254 | if (verbose > 2) { | ||
255 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | ||
256 | print_hists_out(hists); | ||
257 | } | ||
258 | |||
259 | root = &evsel->hists.entries; | ||
260 | node = rb_first(root); | ||
261 | he = rb_entry(node, struct hist_entry, rb_node); | ||
262 | TEST_ASSERT_VAL("Invalid hist entry", | ||
263 | CPU(he) == 1 && PID(he) == 100 && he->stat.period == 300); | ||
264 | |||
265 | node = rb_next(node); | ||
266 | he = rb_entry(node, struct hist_entry, rb_node); | ||
267 | TEST_ASSERT_VAL("Invalid hist entry", | ||
268 | CPU(he) == 0 && PID(he) == 100 && he->stat.period == 100); | ||
269 | |||
270 | out: | ||
271 | del_hist_entries(hists); | ||
272 | reset_output_field(); | ||
273 | return err; | ||
274 | } | ||
275 | |||
276 | /* fields only (no sort key) */ | ||
277 | static int test3(struct perf_evsel *evsel, struct machine *machine) | ||
278 | { | ||
279 | int err; | ||
280 | struct hists *hists = &evsel->hists; | ||
281 | struct hist_entry *he; | ||
282 | struct rb_root *root; | ||
283 | struct rb_node *node; | ||
284 | |||
285 | field_order = "comm,overhead,dso"; | ||
286 | sort_order = NULL; | ||
287 | |||
288 | setup_sorting(); | ||
289 | |||
290 | /* | ||
291 | * expected output: | ||
292 | * | ||
293 | * Command Overhead Shared Object | ||
294 | * ======= ======== ============= | ||
295 | * bash 20.00% bash | ||
296 | * bash 10.00% [kernel] | ||
297 | * perf 30.00% perf | ||
298 | * perf 20.00% [kernel] | ||
299 | * perf 20.00% libc | ||
300 | */ | ||
301 | err = add_hist_entries(hists, machine); | ||
302 | if (err < 0) | ||
303 | goto out; | ||
304 | |||
305 | hists__collapse_resort(hists, NULL); | ||
306 | hists__output_resort(hists); | ||
307 | |||
308 | if (verbose > 2) { | ||
309 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | ||
310 | print_hists_out(hists); | ||
311 | } | ||
312 | |||
313 | root = &evsel->hists.entries; | ||
314 | node = rb_first(root); | ||
315 | he = rb_entry(node, struct hist_entry, rb_node); | ||
316 | TEST_ASSERT_VAL("Invalid hist entry", | ||
317 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && | ||
318 | he->stat.period == 200); | ||
319 | |||
320 | node = rb_next(node); | ||
321 | he = rb_entry(node, struct hist_entry, rb_node); | ||
322 | TEST_ASSERT_VAL("Invalid hist entry", | ||
323 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && | ||
324 | he->stat.period == 100); | ||
325 | |||
326 | node = rb_next(node); | ||
327 | he = rb_entry(node, struct hist_entry, rb_node); | ||
328 | TEST_ASSERT_VAL("Invalid hist entry", | ||
329 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && | ||
330 | he->stat.period == 300); | ||
331 | |||
332 | node = rb_next(node); | ||
333 | he = rb_entry(node, struct hist_entry, rb_node); | ||
334 | TEST_ASSERT_VAL("Invalid hist entry", | ||
335 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && | ||
336 | he->stat.period == 200); | ||
337 | |||
338 | node = rb_next(node); | ||
339 | he = rb_entry(node, struct hist_entry, rb_node); | ||
340 | TEST_ASSERT_VAL("Invalid hist entry", | ||
341 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && | ||
342 | he->stat.period == 200); | ||
343 | |||
344 | out: | ||
345 | del_hist_entries(hists); | ||
346 | reset_output_field(); | ||
347 | return err; | ||
348 | } | ||
349 | |||
350 | /* handle duplicate 'dso' field */ | ||
351 | static int test4(struct perf_evsel *evsel, struct machine *machine) | ||
352 | { | ||
353 | int err; | ||
354 | struct hists *hists = &evsel->hists; | ||
355 | struct hist_entry *he; | ||
356 | struct rb_root *root; | ||
357 | struct rb_node *node; | ||
358 | |||
359 | field_order = "dso,sym,comm,overhead,dso"; | ||
360 | sort_order = "sym"; | ||
361 | |||
362 | setup_sorting(); | ||
363 | |||
364 | /* | ||
365 | * expected output: | ||
366 | * | ||
367 | * Shared Object Symbol Command Overhead | ||
368 | * ============= ============== ======= ======== | ||
369 | * perf [.] cmd_record perf 10.00% | ||
370 | * libc [.] free perf 10.00% | ||
371 | * bash [.] main bash 10.00% | ||
372 | * perf [.] main perf 20.00% | ||
373 | * libc [.] malloc perf 10.00% | ||
374 | * [kernel] [k] page_fault bash 10.00% | ||
375 | * [kernel] [k] page_fault perf 10.00% | ||
376 | * [kernel] [k] schedule perf 10.00% | ||
377 | * bash [.] xmalloc bash 10.00% | ||
378 | */ | ||
379 | err = add_hist_entries(hists, machine); | ||
380 | if (err < 0) | ||
381 | goto out; | ||
382 | |||
383 | hists__collapse_resort(hists, NULL); | ||
384 | hists__output_resort(hists); | ||
385 | |||
386 | if (verbose > 2) { | ||
387 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | ||
388 | print_hists_out(hists); | ||
389 | } | ||
390 | |||
391 | root = &evsel->hists.entries; | ||
392 | node = rb_first(root); | ||
393 | he = rb_entry(node, struct hist_entry, rb_node); | ||
394 | TEST_ASSERT_VAL("Invalid hist entry", | ||
395 | !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "cmd_record") && | ||
396 | !strcmp(COMM(he), "perf") && he->stat.period == 100); | ||
397 | |||
398 | node = rb_next(node); | ||
399 | he = rb_entry(node, struct hist_entry, rb_node); | ||
400 | TEST_ASSERT_VAL("Invalid hist entry", | ||
401 | !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "free") && | ||
402 | !strcmp(COMM(he), "perf") && he->stat.period == 100); | ||
403 | |||
404 | node = rb_next(node); | ||
405 | he = rb_entry(node, struct hist_entry, rb_node); | ||
406 | TEST_ASSERT_VAL("Invalid hist entry", | ||
407 | !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "main") && | ||
408 | !strcmp(COMM(he), "bash") && he->stat.period == 100); | ||
409 | |||
410 | node = rb_next(node); | ||
411 | he = rb_entry(node, struct hist_entry, rb_node); | ||
412 | TEST_ASSERT_VAL("Invalid hist entry", | ||
413 | !strcmp(DSO(he), "perf") && !strcmp(SYM(he), "main") && | ||
414 | !strcmp(COMM(he), "perf") && he->stat.period == 200); | ||
415 | |||
416 | node = rb_next(node); | ||
417 | he = rb_entry(node, struct hist_entry, rb_node); | ||
418 | TEST_ASSERT_VAL("Invalid hist entry", | ||
419 | !strcmp(DSO(he), "libc") && !strcmp(SYM(he), "malloc") && | ||
420 | !strcmp(COMM(he), "perf") && he->stat.period == 100); | ||
421 | |||
422 | node = rb_next(node); | ||
423 | he = rb_entry(node, struct hist_entry, rb_node); | ||
424 | TEST_ASSERT_VAL("Invalid hist entry", | ||
425 | !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") && | ||
426 | !strcmp(COMM(he), "bash") && he->stat.period == 100); | ||
427 | |||
428 | node = rb_next(node); | ||
429 | he = rb_entry(node, struct hist_entry, rb_node); | ||
430 | TEST_ASSERT_VAL("Invalid hist entry", | ||
431 | !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "page_fault") && | ||
432 | !strcmp(COMM(he), "perf") && he->stat.period == 100); | ||
433 | |||
434 | node = rb_next(node); | ||
435 | he = rb_entry(node, struct hist_entry, rb_node); | ||
436 | TEST_ASSERT_VAL("Invalid hist entry", | ||
437 | !strcmp(DSO(he), "[kernel]") && !strcmp(SYM(he), "schedule") && | ||
438 | !strcmp(COMM(he), "perf") && he->stat.period == 100); | ||
439 | |||
440 | node = rb_next(node); | ||
441 | he = rb_entry(node, struct hist_entry, rb_node); | ||
442 | TEST_ASSERT_VAL("Invalid hist entry", | ||
443 | !strcmp(DSO(he), "bash") && !strcmp(SYM(he), "xmalloc") && | ||
444 | !strcmp(COMM(he), "bash") && he->stat.period == 100); | ||
445 | |||
446 | out: | ||
447 | del_hist_entries(hists); | ||
448 | reset_output_field(); | ||
449 | return err; | ||
450 | } | ||
451 | |||
452 | /* full sort keys w/o overhead field */ | ||
453 | static int test5(struct perf_evsel *evsel, struct machine *machine) | ||
454 | { | ||
455 | int err; | ||
456 | struct hists *hists = &evsel->hists; | ||
457 | struct hist_entry *he; | ||
458 | struct rb_root *root; | ||
459 | struct rb_node *node; | ||
460 | |||
461 | field_order = "cpu,pid,comm,dso,sym"; | ||
462 | sort_order = "dso,pid"; | ||
463 | |||
464 | setup_sorting(); | ||
465 | |||
466 | /* | ||
467 | * expected output: | ||
468 | * | ||
469 | * CPU Command: Pid Command Shared Object Symbol | ||
470 | * === ============= ======= ============= ============== | ||
471 | * 0 perf: 100 perf [kernel] [k] schedule | ||
472 | * 2 perf: 200 perf [kernel] [k] page_fault | ||
473 | * 1 bash: 300 bash [kernel] [k] page_fault | ||
474 | * 0 bash: 300 bash bash [.] xmalloc | ||
475 | * 3 bash: 300 bash bash [.] main | ||
476 | * 1 perf: 100 perf libc [.] malloc | ||
477 | * 2 perf: 100 perf libc [.] free | ||
478 | * 1 perf: 100 perf perf [.] cmd_record | ||
479 | * 1 perf: 100 perf perf [.] main | ||
480 | * 2 perf: 200 perf perf [.] main | ||
481 | */ | ||
482 | err = add_hist_entries(hists, machine); | ||
483 | if (err < 0) | ||
484 | goto out; | ||
485 | |||
486 | hists__collapse_resort(hists, NULL); | ||
487 | hists__output_resort(hists); | ||
488 | |||
489 | if (verbose > 2) { | ||
490 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | ||
491 | print_hists_out(hists); | ||
492 | } | ||
493 | |||
494 | root = &evsel->hists.entries; | ||
495 | node = rb_first(root); | ||
496 | he = rb_entry(node, struct hist_entry, rb_node); | ||
497 | |||
498 | TEST_ASSERT_VAL("Invalid hist entry", | ||
499 | CPU(he) == 0 && PID(he) == 100 && | ||
500 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && | ||
501 | !strcmp(SYM(he), "schedule") && he->stat.period == 100); | ||
502 | |||
503 | node = rb_next(node); | ||
504 | he = rb_entry(node, struct hist_entry, rb_node); | ||
505 | TEST_ASSERT_VAL("Invalid hist entry", | ||
506 | CPU(he) == 2 && PID(he) == 200 && | ||
507 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "[kernel]") && | ||
508 | !strcmp(SYM(he), "page_fault") && he->stat.period == 100); | ||
509 | |||
510 | node = rb_next(node); | ||
511 | he = rb_entry(node, struct hist_entry, rb_node); | ||
512 | TEST_ASSERT_VAL("Invalid hist entry", | ||
513 | CPU(he) == 1 && PID(he) == 300 && | ||
514 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "[kernel]") && | ||
515 | !strcmp(SYM(he), "page_fault") && he->stat.period == 100); | ||
516 | |||
517 | node = rb_next(node); | ||
518 | he = rb_entry(node, struct hist_entry, rb_node); | ||
519 | TEST_ASSERT_VAL("Invalid hist entry", | ||
520 | CPU(he) == 0 && PID(he) == 300 && | ||
521 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && | ||
522 | !strcmp(SYM(he), "xmalloc") && he->stat.period == 100); | ||
523 | |||
524 | node = rb_next(node); | ||
525 | he = rb_entry(node, struct hist_entry, rb_node); | ||
526 | TEST_ASSERT_VAL("Invalid hist entry", | ||
527 | CPU(he) == 3 && PID(he) == 300 && | ||
528 | !strcmp(COMM(he), "bash") && !strcmp(DSO(he), "bash") && | ||
529 | !strcmp(SYM(he), "main") && he->stat.period == 100); | ||
530 | |||
531 | node = rb_next(node); | ||
532 | he = rb_entry(node, struct hist_entry, rb_node); | ||
533 | TEST_ASSERT_VAL("Invalid hist entry", | ||
534 | CPU(he) == 1 && PID(he) == 100 && | ||
535 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && | ||
536 | !strcmp(SYM(he), "malloc") && he->stat.period == 100); | ||
537 | |||
538 | node = rb_next(node); | ||
539 | he = rb_entry(node, struct hist_entry, rb_node); | ||
540 | TEST_ASSERT_VAL("Invalid hist entry", | ||
541 | CPU(he) == 2 && PID(he) == 100 && | ||
542 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "libc") && | ||
543 | !strcmp(SYM(he), "free") && he->stat.period == 100); | ||
544 | |||
545 | node = rb_next(node); | ||
546 | he = rb_entry(node, struct hist_entry, rb_node); | ||
547 | TEST_ASSERT_VAL("Invalid hist entry", | ||
548 | CPU(he) == 1 && PID(he) == 100 && | ||
549 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && | ||
550 | !strcmp(SYM(he), "cmd_record") && he->stat.period == 100); | ||
551 | |||
552 | node = rb_next(node); | ||
553 | he = rb_entry(node, struct hist_entry, rb_node); | ||
554 | TEST_ASSERT_VAL("Invalid hist entry", | ||
555 | CPU(he) == 1 && PID(he) == 100 && | ||
556 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && | ||
557 | !strcmp(SYM(he), "main") && he->stat.period == 100); | ||
558 | |||
559 | node = rb_next(node); | ||
560 | he = rb_entry(node, struct hist_entry, rb_node); | ||
561 | TEST_ASSERT_VAL("Invalid hist entry", | ||
562 | CPU(he) == 2 && PID(he) == 200 && | ||
563 | !strcmp(COMM(he), "perf") && !strcmp(DSO(he), "perf") && | ||
564 | !strcmp(SYM(he), "main") && he->stat.period == 100); | ||
565 | |||
566 | out: | ||
567 | del_hist_entries(hists); | ||
568 | reset_output_field(); | ||
569 | return err; | ||
570 | } | ||
571 | |||
572 | int test__hists_output(void) | ||
573 | { | ||
574 | int err = TEST_FAIL; | ||
575 | struct machines machines; | ||
576 | struct machine *machine; | ||
577 | struct perf_evsel *evsel; | ||
578 | struct perf_evlist *evlist = perf_evlist__new(); | ||
579 | size_t i; | ||
580 | test_fn_t testcases[] = { | ||
581 | test1, | ||
582 | test2, | ||
583 | test3, | ||
584 | test4, | ||
585 | test5, | ||
586 | }; | ||
587 | |||
588 | TEST_ASSERT_VAL("No memory", evlist); | ||
589 | |||
590 | err = parse_events(evlist, "cpu-clock"); | ||
591 | if (err) | ||
592 | goto out; | ||
593 | |||
594 | machines__init(&machines); | ||
595 | |||
596 | /* setup threads/dso/map/symbols also */ | ||
597 | machine = setup_fake_machine(&machines); | ||
598 | if (!machine) | ||
599 | goto out; | ||
600 | |||
601 | if (verbose > 1) | ||
602 | machine__fprintf(machine, stderr); | ||
603 | |||
604 | evsel = perf_evlist__first(evlist); | ||
605 | |||
606 | for (i = 0; i < ARRAY_SIZE(testcases); i++) { | ||
607 | err = testcases[i](evsel, machine); | ||
608 | if (err < 0) | ||
609 | break; | ||
610 | } | ||
611 | |||
612 | out: | ||
613 | /* tear down everything */ | ||
614 | perf_evlist__delete(evlist); | ||
615 | machines__exit(&machines); | ||
616 | |||
617 | return err; | ||
618 | } | ||
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 497957f269d8..7a5ab7b0b8f6 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c | |||
@@ -1,4 +1,4 @@ | |||
1 | #include <sys/types.h> | 1 | #include <linux/types.h> |
2 | #include <unistd.h> | 2 | #include <unistd.h> |
3 | #include <sys/prctl.h> | 3 | #include <sys/prctl.h> |
4 | 4 | ||
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c new file mode 100644 index 000000000000..4a456fef66ca --- /dev/null +++ b/tools/perf/tests/mmap-thread-lookup.c | |||
@@ -0,0 +1,233 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <sys/syscall.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/mman.h> | ||
5 | #include <pthread.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <stdio.h> | ||
8 | #include "debug.h" | ||
9 | #include "tests.h" | ||
10 | #include "machine.h" | ||
11 | #include "thread_map.h" | ||
12 | #include "symbol.h" | ||
13 | #include "thread.h" | ||
14 | |||
15 | #define THREADS 4 | ||
16 | |||
17 | static int go_away; | ||
18 | |||
19 | struct thread_data { | ||
20 | pthread_t pt; | ||
21 | pid_t tid; | ||
22 | void *map; | ||
23 | int ready[2]; | ||
24 | }; | ||
25 | |||
26 | static struct thread_data threads[THREADS]; | ||
27 | |||
28 | static int thread_init(struct thread_data *td) | ||
29 | { | ||
30 | void *map; | ||
31 | |||
32 | map = mmap(NULL, page_size, | ||
33 | PROT_READ|PROT_WRITE|PROT_EXEC, | ||
34 | MAP_SHARED|MAP_ANONYMOUS, -1, 0); | ||
35 | |||
36 | if (map == MAP_FAILED) { | ||
37 | perror("mmap failed"); | ||
38 | return -1; | ||
39 | } | ||
40 | |||
41 | td->map = map; | ||
42 | td->tid = syscall(SYS_gettid); | ||
43 | |||
44 | pr_debug("tid = %d, map = %p\n", td->tid, map); | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | static void *thread_fn(void *arg) | ||
49 | { | ||
50 | struct thread_data *td = arg; | ||
51 | ssize_t ret; | ||
52 | int go; | ||
53 | |||
54 | if (thread_init(td)) | ||
55 | return NULL; | ||
56 | |||
57 | /* Signal thread_create thread is initialized. */ | ||
58 | ret = write(td->ready[1], &go, sizeof(int)); | ||
59 | if (ret != sizeof(int)) { | ||
60 | pr_err("failed to notify\n"); | ||
61 | return NULL; | ||
62 | } | ||
63 | |||
64 | while (!go_away) { | ||
65 | /* Waiting for main thread to kill us. */ | ||
66 | usleep(100); | ||
67 | } | ||
68 | |||
69 | munmap(td->map, page_size); | ||
70 | return NULL; | ||
71 | } | ||
72 | |||
73 | static int thread_create(int i) | ||
74 | { | ||
75 | struct thread_data *td = &threads[i]; | ||
76 | int err, go; | ||
77 | |||
78 | if (pipe(td->ready)) | ||
79 | return -1; | ||
80 | |||
81 | err = pthread_create(&td->pt, NULL, thread_fn, td); | ||
82 | if (!err) { | ||
83 | /* Wait for thread initialization. */ | ||
84 | ssize_t ret = read(td->ready[0], &go, sizeof(int)); | ||
85 | err = ret != sizeof(int); | ||
86 | } | ||
87 | |||
88 | close(td->ready[0]); | ||
89 | close(td->ready[1]); | ||
90 | return err; | ||
91 | } | ||
92 | |||
93 | static int threads_create(void) | ||
94 | { | ||
95 | struct thread_data *td0 = &threads[0]; | ||
96 | int i, err = 0; | ||
97 | |||
98 | go_away = 0; | ||
99 | |||
100 | /* 0 is main thread */ | ||
101 | if (thread_init(td0)) | ||
102 | return -1; | ||
103 | |||
104 | for (i = 1; !err && i < THREADS; i++) | ||
105 | err = thread_create(i); | ||
106 | |||
107 | return err; | ||
108 | } | ||
109 | |||
110 | static int threads_destroy(void) | ||
111 | { | ||
112 | struct thread_data *td0 = &threads[0]; | ||
113 | int i, err = 0; | ||
114 | |||
115 | /* cleanup the main thread */ | ||
116 | munmap(td0->map, page_size); | ||
117 | |||
118 | go_away = 1; | ||
119 | |||
120 | for (i = 1; !err && i < THREADS; i++) | ||
121 | err = pthread_join(threads[i].pt, NULL); | ||
122 | |||
123 | return err; | ||
124 | } | ||
125 | |||
126 | typedef int (*synth_cb)(struct machine *machine); | ||
127 | |||
128 | static int synth_all(struct machine *machine) | ||
129 | { | ||
130 | return perf_event__synthesize_threads(NULL, | ||
131 | perf_event__process, | ||
132 | machine, 0); | ||
133 | } | ||
134 | |||
135 | static int synth_process(struct machine *machine) | ||
136 | { | ||
137 | struct thread_map *map; | ||
138 | int err; | ||
139 | |||
140 | map = thread_map__new_by_pid(getpid()); | ||
141 | |||
142 | err = perf_event__synthesize_thread_map(NULL, map, | ||
143 | perf_event__process, | ||
144 | machine, 0); | ||
145 | |||
146 | thread_map__delete(map); | ||
147 | return err; | ||
148 | } | ||
149 | |||
150 | static int mmap_events(synth_cb synth) | ||
151 | { | ||
152 | struct machines machines; | ||
153 | struct machine *machine; | ||
154 | int err, i; | ||
155 | |||
156 | /* | ||
157 | * The threads_create will not return before all threads | ||
158 | * are spawned and all created memory map. | ||
159 | * | ||
160 | * They will loop until threads_destroy is called, so we | ||
161 | * can safely run synthesizing function. | ||
162 | */ | ||
163 | TEST_ASSERT_VAL("failed to create threads", !threads_create()); | ||
164 | |||
165 | machines__init(&machines); | ||
166 | machine = &machines.host; | ||
167 | |||
168 | dump_trace = verbose > 1 ? 1 : 0; | ||
169 | |||
170 | err = synth(machine); | ||
171 | |||
172 | dump_trace = 0; | ||
173 | |||
174 | TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy()); | ||
175 | TEST_ASSERT_VAL("failed to synthesize maps", !err); | ||
176 | |||
177 | /* | ||
178 | * All data is synthesized, try to find map for each | ||
179 | * thread object. | ||
180 | */ | ||
181 | for (i = 0; i < THREADS; i++) { | ||
182 | struct thread_data *td = &threads[i]; | ||
183 | struct addr_location al; | ||
184 | struct thread *thread; | ||
185 | |||
186 | thread = machine__findnew_thread(machine, getpid(), td->tid); | ||
187 | |||
188 | pr_debug("looking for map %p\n", td->map); | ||
189 | |||
190 | thread__find_addr_map(thread, machine, | ||
191 | PERF_RECORD_MISC_USER, MAP__FUNCTION, | ||
192 | (unsigned long) (td->map + 1), &al); | ||
193 | |||
194 | if (!al.map) { | ||
195 | pr_debug("failed, couldn't find map\n"); | ||
196 | err = -1; | ||
197 | break; | ||
198 | } | ||
199 | |||
200 | pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start); | ||
201 | } | ||
202 | |||
203 | machine__delete_threads(machine); | ||
204 | machines__exit(&machines); | ||
205 | return err; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * This test creates 'THREADS' number of threads (including | ||
210 | * main thread) and each thread creates memory map. | ||
211 | * | ||
212 | * When threads are created, we synthesize them with both | ||
213 | * (separate tests): | ||
214 | * perf_event__synthesize_thread_map (process based) | ||
215 | * perf_event__synthesize_threads (global) | ||
216 | * | ||
217 | * We test we can find all memory maps via: | ||
218 | * thread__find_addr_map | ||
219 | * | ||
220 | * by using all thread objects. | ||
221 | */ | ||
222 | int test__mmap_thread_lookup(void) | ||
223 | { | ||
224 | /* perf_event__synthesize_threads synthesize */ | ||
225 | TEST_ASSERT_VAL("failed with sythesizing all", | ||
226 | !mmap_events(synth_all)); | ||
227 | |||
228 | /* perf_event__synthesize_thread_map synthesize */ | ||
229 | TEST_ASSERT_VAL("failed with sythesizing process", | ||
230 | !mmap_events(synth_process)); | ||
231 | |||
232 | return 0; | ||
233 | } | ||
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 8605ff5572ae..deba66955f8c 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -1174,188 +1174,240 @@ static int test__all_tracepoints(struct perf_evlist *evlist) | |||
1174 | struct evlist_test { | 1174 | struct evlist_test { |
1175 | const char *name; | 1175 | const char *name; |
1176 | __u32 type; | 1176 | __u32 type; |
1177 | const int id; | ||
1177 | int (*check)(struct perf_evlist *evlist); | 1178 | int (*check)(struct perf_evlist *evlist); |
1178 | }; | 1179 | }; |
1179 | 1180 | ||
1180 | static struct evlist_test test__events[] = { | 1181 | static struct evlist_test test__events[] = { |
1181 | [0] = { | 1182 | { |
1182 | .name = "syscalls:sys_enter_open", | 1183 | .name = "syscalls:sys_enter_open", |
1183 | .check = test__checkevent_tracepoint, | 1184 | .check = test__checkevent_tracepoint, |
1185 | .id = 0, | ||
1184 | }, | 1186 | }, |
1185 | [1] = { | 1187 | { |
1186 | .name = "syscalls:*", | 1188 | .name = "syscalls:*", |
1187 | .check = test__checkevent_tracepoint_multi, | 1189 | .check = test__checkevent_tracepoint_multi, |
1190 | .id = 1, | ||
1188 | }, | 1191 | }, |
1189 | [2] = { | 1192 | { |
1190 | .name = "r1a", | 1193 | .name = "r1a", |
1191 | .check = test__checkevent_raw, | 1194 | .check = test__checkevent_raw, |
1195 | .id = 2, | ||
1192 | }, | 1196 | }, |
1193 | [3] = { | 1197 | { |
1194 | .name = "1:1", | 1198 | .name = "1:1", |
1195 | .check = test__checkevent_numeric, | 1199 | .check = test__checkevent_numeric, |
1200 | .id = 3, | ||
1196 | }, | 1201 | }, |
1197 | [4] = { | 1202 | { |
1198 | .name = "instructions", | 1203 | .name = "instructions", |
1199 | .check = test__checkevent_symbolic_name, | 1204 | .check = test__checkevent_symbolic_name, |
1205 | .id = 4, | ||
1200 | }, | 1206 | }, |
1201 | [5] = { | 1207 | { |
1202 | .name = "cycles/period=100000,config2/", | 1208 | .name = "cycles/period=100000,config2/", |
1203 | .check = test__checkevent_symbolic_name_config, | 1209 | .check = test__checkevent_symbolic_name_config, |
1210 | .id = 5, | ||
1204 | }, | 1211 | }, |
1205 | [6] = { | 1212 | { |
1206 | .name = "faults", | 1213 | .name = "faults", |
1207 | .check = test__checkevent_symbolic_alias, | 1214 | .check = test__checkevent_symbolic_alias, |
1215 | .id = 6, | ||
1208 | }, | 1216 | }, |
1209 | [7] = { | 1217 | { |
1210 | .name = "L1-dcache-load-miss", | 1218 | .name = "L1-dcache-load-miss", |
1211 | .check = test__checkevent_genhw, | 1219 | .check = test__checkevent_genhw, |
1220 | .id = 7, | ||
1212 | }, | 1221 | }, |
1213 | [8] = { | 1222 | { |
1214 | .name = "mem:0", | 1223 | .name = "mem:0", |
1215 | .check = test__checkevent_breakpoint, | 1224 | .check = test__checkevent_breakpoint, |
1225 | .id = 8, | ||
1216 | }, | 1226 | }, |
1217 | [9] = { | 1227 | { |
1218 | .name = "mem:0:x", | 1228 | .name = "mem:0:x", |
1219 | .check = test__checkevent_breakpoint_x, | 1229 | .check = test__checkevent_breakpoint_x, |
1230 | .id = 9, | ||
1220 | }, | 1231 | }, |
1221 | [10] = { | 1232 | { |
1222 | .name = "mem:0:r", | 1233 | .name = "mem:0:r", |
1223 | .check = test__checkevent_breakpoint_r, | 1234 | .check = test__checkevent_breakpoint_r, |
1235 | .id = 10, | ||
1224 | }, | 1236 | }, |
1225 | [11] = { | 1237 | { |
1226 | .name = "mem:0:w", | 1238 | .name = "mem:0:w", |
1227 | .check = test__checkevent_breakpoint_w, | 1239 | .check = test__checkevent_breakpoint_w, |
1240 | .id = 11, | ||
1228 | }, | 1241 | }, |
1229 | [12] = { | 1242 | { |
1230 | .name = "syscalls:sys_enter_open:k", | 1243 | .name = "syscalls:sys_enter_open:k", |
1231 | .check = test__checkevent_tracepoint_modifier, | 1244 | .check = test__checkevent_tracepoint_modifier, |
1245 | .id = 12, | ||
1232 | }, | 1246 | }, |
1233 | [13] = { | 1247 | { |
1234 | .name = "syscalls:*:u", | 1248 | .name = "syscalls:*:u", |
1235 | .check = test__checkevent_tracepoint_multi_modifier, | 1249 | .check = test__checkevent_tracepoint_multi_modifier, |
1250 | .id = 13, | ||
1236 | }, | 1251 | }, |
1237 | [14] = { | 1252 | { |
1238 | .name = "r1a:kp", | 1253 | .name = "r1a:kp", |
1239 | .check = test__checkevent_raw_modifier, | 1254 | .check = test__checkevent_raw_modifier, |
1255 | .id = 14, | ||
1240 | }, | 1256 | }, |
1241 | [15] = { | 1257 | { |
1242 | .name = "1:1:hp", | 1258 | .name = "1:1:hp", |
1243 | .check = test__checkevent_numeric_modifier, | 1259 | .check = test__checkevent_numeric_modifier, |
1260 | .id = 15, | ||
1244 | }, | 1261 | }, |
1245 | [16] = { | 1262 | { |
1246 | .name = "instructions:h", | 1263 | .name = "instructions:h", |
1247 | .check = test__checkevent_symbolic_name_modifier, | 1264 | .check = test__checkevent_symbolic_name_modifier, |
1265 | .id = 16, | ||
1248 | }, | 1266 | }, |
1249 | [17] = { | 1267 | { |
1250 | .name = "faults:u", | 1268 | .name = "faults:u", |
1251 | .check = test__checkevent_symbolic_alias_modifier, | 1269 | .check = test__checkevent_symbolic_alias_modifier, |
1270 | .id = 17, | ||
1252 | }, | 1271 | }, |
1253 | [18] = { | 1272 | { |
1254 | .name = "L1-dcache-load-miss:kp", | 1273 | .name = "L1-dcache-load-miss:kp", |
1255 | .check = test__checkevent_genhw_modifier, | 1274 | .check = test__checkevent_genhw_modifier, |
1275 | .id = 18, | ||
1256 | }, | 1276 | }, |
1257 | [19] = { | 1277 | { |
1258 | .name = "mem:0:u", | 1278 | .name = "mem:0:u", |
1259 | .check = test__checkevent_breakpoint_modifier, | 1279 | .check = test__checkevent_breakpoint_modifier, |
1280 | .id = 19, | ||
1260 | }, | 1281 | }, |
1261 | [20] = { | 1282 | { |
1262 | .name = "mem:0:x:k", | 1283 | .name = "mem:0:x:k", |
1263 | .check = test__checkevent_breakpoint_x_modifier, | 1284 | .check = test__checkevent_breakpoint_x_modifier, |
1285 | .id = 20, | ||
1264 | }, | 1286 | }, |
1265 | [21] = { | 1287 | { |
1266 | .name = "mem:0:r:hp", | 1288 | .name = "mem:0:r:hp", |
1267 | .check = test__checkevent_breakpoint_r_modifier, | 1289 | .check = test__checkevent_breakpoint_r_modifier, |
1290 | .id = 21, | ||
1268 | }, | 1291 | }, |
1269 | [22] = { | 1292 | { |
1270 | .name = "mem:0:w:up", | 1293 | .name = "mem:0:w:up", |
1271 | .check = test__checkevent_breakpoint_w_modifier, | 1294 | .check = test__checkevent_breakpoint_w_modifier, |
1295 | .id = 22, | ||
1272 | }, | 1296 | }, |
1273 | [23] = { | 1297 | { |
1274 | .name = "r1,syscalls:sys_enter_open:k,1:1:hp", | 1298 | .name = "r1,syscalls:sys_enter_open:k,1:1:hp", |
1275 | .check = test__checkevent_list, | 1299 | .check = test__checkevent_list, |
1300 | .id = 23, | ||
1276 | }, | 1301 | }, |
1277 | [24] = { | 1302 | { |
1278 | .name = "instructions:G", | 1303 | .name = "instructions:G", |
1279 | .check = test__checkevent_exclude_host_modifier, | 1304 | .check = test__checkevent_exclude_host_modifier, |
1305 | .id = 24, | ||
1280 | }, | 1306 | }, |
1281 | [25] = { | 1307 | { |
1282 | .name = "instructions:H", | 1308 | .name = "instructions:H", |
1283 | .check = test__checkevent_exclude_guest_modifier, | 1309 | .check = test__checkevent_exclude_guest_modifier, |
1310 | .id = 25, | ||
1284 | }, | 1311 | }, |
1285 | [26] = { | 1312 | { |
1286 | .name = "mem:0:rw", | 1313 | .name = "mem:0:rw", |
1287 | .check = test__checkevent_breakpoint_rw, | 1314 | .check = test__checkevent_breakpoint_rw, |
1315 | .id = 26, | ||
1288 | }, | 1316 | }, |
1289 | [27] = { | 1317 | { |
1290 | .name = "mem:0:rw:kp", | 1318 | .name = "mem:0:rw:kp", |
1291 | .check = test__checkevent_breakpoint_rw_modifier, | 1319 | .check = test__checkevent_breakpoint_rw_modifier, |
1320 | .id = 27, | ||
1292 | }, | 1321 | }, |
1293 | [28] = { | 1322 | { |
1294 | .name = "{instructions:k,cycles:upp}", | 1323 | .name = "{instructions:k,cycles:upp}", |
1295 | .check = test__group1, | 1324 | .check = test__group1, |
1325 | .id = 28, | ||
1296 | }, | 1326 | }, |
1297 | [29] = { | 1327 | { |
1298 | .name = "{faults:k,cache-references}:u,cycles:k", | 1328 | .name = "{faults:k,cache-references}:u,cycles:k", |
1299 | .check = test__group2, | 1329 | .check = test__group2, |
1330 | .id = 29, | ||
1300 | }, | 1331 | }, |
1301 | [30] = { | 1332 | { |
1302 | .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u", | 1333 | .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u", |
1303 | .check = test__group3, | 1334 | .check = test__group3, |
1335 | .id = 30, | ||
1304 | }, | 1336 | }, |
1305 | [31] = { | 1337 | { |
1306 | .name = "{cycles:u,instructions:kp}:p", | 1338 | .name = "{cycles:u,instructions:kp}:p", |
1307 | .check = test__group4, | 1339 | .check = test__group4, |
1340 | .id = 31, | ||
1308 | }, | 1341 | }, |
1309 | [32] = { | 1342 | { |
1310 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", | 1343 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", |
1311 | .check = test__group5, | 1344 | .check = test__group5, |
1345 | .id = 32, | ||
1312 | }, | 1346 | }, |
1313 | [33] = { | 1347 | { |
1314 | .name = "*:*", | 1348 | .name = "*:*", |
1315 | .check = test__all_tracepoints, | 1349 | .check = test__all_tracepoints, |
1350 | .id = 33, | ||
1316 | }, | 1351 | }, |
1317 | [34] = { | 1352 | { |
1318 | .name = "{cycles,cache-misses:G}:H", | 1353 | .name = "{cycles,cache-misses:G}:H", |
1319 | .check = test__group_gh1, | 1354 | .check = test__group_gh1, |
1355 | .id = 34, | ||
1320 | }, | 1356 | }, |
1321 | [35] = { | 1357 | { |
1322 | .name = "{cycles,cache-misses:H}:G", | 1358 | .name = "{cycles,cache-misses:H}:G", |
1323 | .check = test__group_gh2, | 1359 | .check = test__group_gh2, |
1360 | .id = 35, | ||
1324 | }, | 1361 | }, |
1325 | [36] = { | 1362 | { |
1326 | .name = "{cycles:G,cache-misses:H}:u", | 1363 | .name = "{cycles:G,cache-misses:H}:u", |
1327 | .check = test__group_gh3, | 1364 | .check = test__group_gh3, |
1365 | .id = 36, | ||
1328 | }, | 1366 | }, |
1329 | [37] = { | 1367 | { |
1330 | .name = "{cycles:G,cache-misses:H}:uG", | 1368 | .name = "{cycles:G,cache-misses:H}:uG", |
1331 | .check = test__group_gh4, | 1369 | .check = test__group_gh4, |
1370 | .id = 37, | ||
1332 | }, | 1371 | }, |
1333 | [38] = { | 1372 | { |
1334 | .name = "{cycles,cache-misses,branch-misses}:S", | 1373 | .name = "{cycles,cache-misses,branch-misses}:S", |
1335 | .check = test__leader_sample1, | 1374 | .check = test__leader_sample1, |
1375 | .id = 38, | ||
1336 | }, | 1376 | }, |
1337 | [39] = { | 1377 | { |
1338 | .name = "{instructions,branch-misses}:Su", | 1378 | .name = "{instructions,branch-misses}:Su", |
1339 | .check = test__leader_sample2, | 1379 | .check = test__leader_sample2, |
1380 | .id = 39, | ||
1340 | }, | 1381 | }, |
1341 | [40] = { | 1382 | { |
1342 | .name = "instructions:uDp", | 1383 | .name = "instructions:uDp", |
1343 | .check = test__checkevent_pinned_modifier, | 1384 | .check = test__checkevent_pinned_modifier, |
1385 | .id = 40, | ||
1344 | }, | 1386 | }, |
1345 | [41] = { | 1387 | { |
1346 | .name = "{cycles,cache-misses,branch-misses}:D", | 1388 | .name = "{cycles,cache-misses,branch-misses}:D", |
1347 | .check = test__pinned_group, | 1389 | .check = test__pinned_group, |
1390 | .id = 41, | ||
1391 | }, | ||
1392 | #if defined(__s390x__) | ||
1393 | { | ||
1394 | .name = "kvm-s390:kvm_s390_create_vm", | ||
1395 | .check = test__checkevent_tracepoint, | ||
1396 | .id = 100, | ||
1348 | }, | 1397 | }, |
1398 | #endif | ||
1349 | }; | 1399 | }; |
1350 | 1400 | ||
1351 | static struct evlist_test test__events_pmu[] = { | 1401 | static struct evlist_test test__events_pmu[] = { |
1352 | [0] = { | 1402 | { |
1353 | .name = "cpu/config=10,config1,config2=3,period=1000/u", | 1403 | .name = "cpu/config=10,config1,config2=3,period=1000/u", |
1354 | .check = test__checkevent_pmu, | 1404 | .check = test__checkevent_pmu, |
1405 | .id = 0, | ||
1355 | }, | 1406 | }, |
1356 | [1] = { | 1407 | { |
1357 | .name = "cpu/config=1,name=krava/u,cpu/config=2/u", | 1408 | .name = "cpu/config=1,name=krava/u,cpu/config=2/u", |
1358 | .check = test__checkevent_pmu_name, | 1409 | .check = test__checkevent_pmu_name, |
1410 | .id = 1, | ||
1359 | }, | 1411 | }, |
1360 | }; | 1412 | }; |
1361 | 1413 | ||
@@ -1402,7 +1454,7 @@ static int test_events(struct evlist_test *events, unsigned cnt) | |||
1402 | for (i = 0; i < cnt; i++) { | 1454 | for (i = 0; i < cnt; i++) { |
1403 | struct evlist_test *e = &events[i]; | 1455 | struct evlist_test *e = &events[i]; |
1404 | 1456 | ||
1405 | pr_debug("running test %d '%s'\n", i, e->name); | 1457 | pr_debug("running test %d '%s'\n", e->id, e->name); |
1406 | ret1 = test_event(e); | 1458 | ret1 = test_event(e); |
1407 | if (ret1) | 1459 | if (ret1) |
1408 | ret2 = ret1; | 1460 | ret2 = ret1; |
diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index e117b6c6a248..905019f9b740 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c | |||
@@ -1,4 +1,4 @@ | |||
1 | #include <sys/types.h> | 1 | #include <linux/types.h> |
2 | #include <stddef.h> | 2 | #include <stddef.h> |
3 | 3 | ||
4 | #include "tests.h" | 4 | #include "tests.h" |
diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index 47146d388dbf..3b7cd4d32dcb 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c | |||
@@ -1,7 +1,6 @@ | |||
1 | #include <stdio.h> | 1 | #include <stdio.h> |
2 | #include <sys/types.h> | ||
3 | #include <unistd.h> | 2 | #include <unistd.h> |
4 | #include <inttypes.h> | 3 | #include <linux/types.h> |
5 | #include <sys/prctl.h> | 4 | #include <sys/prctl.h> |
6 | 5 | ||
7 | #include "parse-events.h" | 6 | #include "parse-events.h" |
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c index 46649c25fa5e..e59143fd9e71 100644 --- a/tools/perf/tests/rdpmc.c +++ b/tools/perf/tests/rdpmc.c | |||
@@ -2,7 +2,7 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <signal.h> | 3 | #include <signal.h> |
4 | #include <sys/mman.h> | 4 | #include <sys/mman.h> |
5 | #include "types.h" | 5 | #include <linux/types.h> |
6 | #include "perf.h" | 6 | #include "perf.h" |
7 | #include "debug.h" | 7 | #include "debug.h" |
8 | #include "tests.h" | 8 | #include "tests.h" |
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 0014d3c8c21c..7ae8d17db3d9 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include <stdbool.h> | 1 | #include <stdbool.h> |
2 | #include <inttypes.h> | 2 | #include <linux/types.h> |
3 | 3 | ||
4 | #include "util.h" | 4 | #include "util.h" |
5 | #include "event.h" | 5 | #include "event.h" |
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a24795ca002d..d76c0e2e6635 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -41,8 +41,12 @@ int test__sample_parsing(void); | |||
41 | int test__keep_tracking(void); | 41 | int test__keep_tracking(void); |
42 | int test__parse_no_sample_id_all(void); | 42 | int test__parse_no_sample_id_all(void); |
43 | int test__dwarf_unwind(void); | 43 | int test__dwarf_unwind(void); |
44 | int test__hists_filter(void); | ||
45 | int test__mmap_thread_lookup(void); | ||
46 | int test__thread_mg_share(void); | ||
47 | int test__hists_output(void); | ||
44 | 48 | ||
45 | #if defined(__x86_64__) || defined(__i386__) | 49 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) |
46 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 50 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
47 | struct thread; | 51 | struct thread; |
48 | struct perf_sample; | 52 | struct perf_sample; |
diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c new file mode 100644 index 000000000000..2b2e0dbe114f --- /dev/null +++ b/tools/perf/tests/thread-mg-share.c | |||
@@ -0,0 +1,90 @@ | |||
1 | #include "tests.h" | ||
2 | #include "machine.h" | ||
3 | #include "thread.h" | ||
4 | #include "map.h" | ||
5 | |||
6 | int test__thread_mg_share(void) | ||
7 | { | ||
8 | struct machines machines; | ||
9 | struct machine *machine; | ||
10 | |||
11 | /* thread group */ | ||
12 | struct thread *leader; | ||
13 | struct thread *t1, *t2, *t3; | ||
14 | struct map_groups *mg; | ||
15 | |||
16 | /* other process */ | ||
17 | struct thread *other, *other_leader; | ||
18 | struct map_groups *other_mg; | ||
19 | |||
20 | /* | ||
21 | * This test create 2 processes abstractions (struct thread) | ||
22 | * with several threads and checks they properly share and | ||
23 | * maintain map groups info (struct map_groups). | ||
24 | * | ||
25 | * thread group (pid: 0, tids: 0, 1, 2, 3) | ||
26 | * other group (pid: 4, tids: 4, 5) | ||
27 | */ | ||
28 | |||
29 | machines__init(&machines); | ||
30 | machine = &machines.host; | ||
31 | |||
32 | /* create process with 4 threads */ | ||
33 | leader = machine__findnew_thread(machine, 0, 0); | ||
34 | t1 = machine__findnew_thread(machine, 0, 1); | ||
35 | t2 = machine__findnew_thread(machine, 0, 2); | ||
36 | t3 = machine__findnew_thread(machine, 0, 3); | ||
37 | |||
38 | /* and create 1 separated process, without thread leader */ | ||
39 | other = machine__findnew_thread(machine, 4, 5); | ||
40 | |||
41 | TEST_ASSERT_VAL("failed to create threads", | ||
42 | leader && t1 && t2 && t3 && other); | ||
43 | |||
44 | mg = leader->mg; | ||
45 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 4); | ||
46 | |||
47 | /* test the map groups pointer is shared */ | ||
48 | TEST_ASSERT_VAL("map groups don't match", mg == t1->mg); | ||
49 | TEST_ASSERT_VAL("map groups don't match", mg == t2->mg); | ||
50 | TEST_ASSERT_VAL("map groups don't match", mg == t3->mg); | ||
51 | |||
52 | /* | ||
53 | * Verify the other leader was created by previous call. | ||
54 | * It should have shared map groups with no change in | ||
55 | * refcnt. | ||
56 | */ | ||
57 | other_leader = machine__find_thread(machine, 4, 4); | ||
58 | TEST_ASSERT_VAL("failed to find other leader", other_leader); | ||
59 | |||
60 | other_mg = other->mg; | ||
61 | TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 2); | ||
62 | |||
63 | TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg); | ||
64 | |||
65 | /* release thread group */ | ||
66 | thread__delete(leader); | ||
67 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 3); | ||
68 | |||
69 | thread__delete(t1); | ||
70 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 2); | ||
71 | |||
72 | thread__delete(t2); | ||
73 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 1); | ||
74 | |||
75 | thread__delete(t3); | ||
76 | |||
77 | /* release other group */ | ||
78 | thread__delete(other_leader); | ||
79 | TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 1); | ||
80 | |||
81 | thread__delete(other); | ||
82 | |||
83 | /* | ||
84 | * Cannot call machine__delete_threads(machine) now, | ||
85 | * because we've already released all the threads. | ||
86 | */ | ||
87 | |||
88 | machines__exit(&machines); | ||
89 | return 0; | ||
90 | } | ||
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index 118cca29dd26..03d4d6295f10 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h | |||
@@ -1,9 +1,7 @@ | |||
1 | #ifndef _PERF_UI_BROWSER_H_ | 1 | #ifndef _PERF_UI_BROWSER_H_ |
2 | #define _PERF_UI_BROWSER_H_ 1 | 2 | #define _PERF_UI_BROWSER_H_ 1 |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <linux/types.h> |
5 | #include <sys/types.h> | ||
6 | #include "../types.h" | ||
7 | 5 | ||
8 | #define HE_COLORSET_TOP 50 | 6 | #define HE_COLORSET_TOP 50 |
9 | #define HE_COLORSET_MEDIUM 51 | 7 | #define HE_COLORSET_MEDIUM 51 |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 7ec871af3f6f..1c331b934ffc 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -26,13 +26,36 @@ struct hist_browser { | |||
26 | int print_seq; | 26 | int print_seq; |
27 | bool show_dso; | 27 | bool show_dso; |
28 | float min_pcnt; | 28 | float min_pcnt; |
29 | u64 nr_pcnt_entries; | 29 | u64 nr_non_filtered_entries; |
30 | u64 nr_callchain_rows; | ||
30 | }; | 31 | }; |
31 | 32 | ||
32 | extern void hist_browser__init_hpp(void); | 33 | extern void hist_browser__init_hpp(void); |
33 | 34 | ||
34 | static int hists__browser_title(struct hists *hists, char *bf, size_t size, | 35 | static int hists__browser_title(struct hists *hists, char *bf, size_t size, |
35 | const char *ev_name); | 36 | const char *ev_name); |
37 | static void hist_browser__update_nr_entries(struct hist_browser *hb); | ||
38 | |||
39 | static struct rb_node *hists__filter_entries(struct rb_node *nd, | ||
40 | struct hists *hists, | ||
41 | float min_pcnt); | ||
42 | |||
43 | static bool hist_browser__has_filter(struct hist_browser *hb) | ||
44 | { | ||
45 | return hists__has_filter(hb->hists) || hb->min_pcnt; | ||
46 | } | ||
47 | |||
48 | static u32 hist_browser__nr_entries(struct hist_browser *hb) | ||
49 | { | ||
50 | u32 nr_entries; | ||
51 | |||
52 | if (hist_browser__has_filter(hb)) | ||
53 | nr_entries = hb->nr_non_filtered_entries; | ||
54 | else | ||
55 | nr_entries = hb->hists->nr_entries; | ||
56 | |||
57 | return nr_entries + hb->nr_callchain_rows; | ||
58 | } | ||
36 | 59 | ||
37 | static void hist_browser__refresh_dimensions(struct hist_browser *browser) | 60 | static void hist_browser__refresh_dimensions(struct hist_browser *browser) |
38 | { | 61 | { |
@@ -43,7 +66,14 @@ static void hist_browser__refresh_dimensions(struct hist_browser *browser) | |||
43 | 66 | ||
44 | static void hist_browser__reset(struct hist_browser *browser) | 67 | static void hist_browser__reset(struct hist_browser *browser) |
45 | { | 68 | { |
46 | browser->b.nr_entries = browser->hists->nr_entries; | 69 | /* |
70 | * The hists__remove_entry_filter() already folds non-filtered | ||
71 | * entries so we can assume it has 0 callchain rows. | ||
72 | */ | ||
73 | browser->nr_callchain_rows = 0; | ||
74 | |||
75 | hist_browser__update_nr_entries(browser); | ||
76 | browser->b.nr_entries = hist_browser__nr_entries(browser); | ||
47 | hist_browser__refresh_dimensions(browser); | 77 | hist_browser__refresh_dimensions(browser); |
48 | ui_browser__reset_index(&browser->b); | 78 | ui_browser__reset_index(&browser->b); |
49 | } | 79 | } |
@@ -198,14 +228,16 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser) | |||
198 | struct hist_entry *he = browser->he_selection; | 228 | struct hist_entry *he = browser->he_selection; |
199 | 229 | ||
200 | hist_entry__init_have_children(he); | 230 | hist_entry__init_have_children(he); |
201 | browser->hists->nr_entries -= he->nr_rows; | 231 | browser->b.nr_entries -= he->nr_rows; |
232 | browser->nr_callchain_rows -= he->nr_rows; | ||
202 | 233 | ||
203 | if (he->ms.unfolded) | 234 | if (he->ms.unfolded) |
204 | he->nr_rows = callchain__count_rows(&he->sorted_chain); | 235 | he->nr_rows = callchain__count_rows(&he->sorted_chain); |
205 | else | 236 | else |
206 | he->nr_rows = 0; | 237 | he->nr_rows = 0; |
207 | browser->hists->nr_entries += he->nr_rows; | 238 | |
208 | browser->b.nr_entries = browser->hists->nr_entries; | 239 | browser->b.nr_entries += he->nr_rows; |
240 | browser->nr_callchain_rows += he->nr_rows; | ||
209 | 241 | ||
210 | return true; | 242 | return true; |
211 | } | 243 | } |
@@ -280,23 +312,27 @@ static void hist_entry__set_folding(struct hist_entry *he, bool unfold) | |||
280 | he->nr_rows = 0; | 312 | he->nr_rows = 0; |
281 | } | 313 | } |
282 | 314 | ||
283 | static void hists__set_folding(struct hists *hists, bool unfold) | 315 | static void |
316 | __hist_browser__set_folding(struct hist_browser *browser, bool unfold) | ||
284 | { | 317 | { |
285 | struct rb_node *nd; | 318 | struct rb_node *nd; |
319 | struct hists *hists = browser->hists; | ||
286 | 320 | ||
287 | hists->nr_entries = 0; | 321 | for (nd = rb_first(&hists->entries); |
288 | 322 | (nd = hists__filter_entries(nd, hists, browser->min_pcnt)) != NULL; | |
289 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 323 | nd = rb_next(nd)) { |
290 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | 324 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
291 | hist_entry__set_folding(he, unfold); | 325 | hist_entry__set_folding(he, unfold); |
292 | hists->nr_entries += 1 + he->nr_rows; | 326 | browser->nr_callchain_rows += he->nr_rows; |
293 | } | 327 | } |
294 | } | 328 | } |
295 | 329 | ||
296 | static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) | 330 | static void hist_browser__set_folding(struct hist_browser *browser, bool unfold) |
297 | { | 331 | { |
298 | hists__set_folding(browser->hists, unfold); | 332 | browser->nr_callchain_rows = 0; |
299 | browser->b.nr_entries = browser->hists->nr_entries; | 333 | __hist_browser__set_folding(browser, unfold); |
334 | |||
335 | browser->b.nr_entries = hist_browser__nr_entries(browser); | ||
300 | /* Go to the start, we may be way after valid entries after a collapse */ | 336 | /* Go to the start, we may be way after valid entries after a collapse */ |
301 | ui_browser__reset_index(&browser->b); | 337 | ui_browser__reset_index(&browser->b); |
302 | } | 338 | } |
@@ -310,8 +346,6 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) | |||
310 | "Or reduce the sampling frequency."); | 346 | "Or reduce the sampling frequency."); |
311 | } | 347 | } |
312 | 348 | ||
313 | static void hist_browser__update_pcnt_entries(struct hist_browser *hb); | ||
314 | |||
315 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | 349 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, |
316 | struct hist_browser_timer *hbt) | 350 | struct hist_browser_timer *hbt) |
317 | { | 351 | { |
@@ -320,9 +354,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
320 | int delay_secs = hbt ? hbt->refresh : 0; | 354 | int delay_secs = hbt ? hbt->refresh : 0; |
321 | 355 | ||
322 | browser->b.entries = &browser->hists->entries; | 356 | browser->b.entries = &browser->hists->entries; |
323 | browser->b.nr_entries = browser->hists->nr_entries; | 357 | browser->b.nr_entries = hist_browser__nr_entries(browser); |
324 | if (browser->min_pcnt) | ||
325 | browser->b.nr_entries = browser->nr_pcnt_entries; | ||
326 | 358 | ||
327 | hist_browser__refresh_dimensions(browser); | 359 | hist_browser__refresh_dimensions(browser); |
328 | hists__browser_title(browser->hists, title, sizeof(title), ev_name); | 360 | hists__browser_title(browser->hists, title, sizeof(title), ev_name); |
@@ -339,13 +371,10 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
339 | u64 nr_entries; | 371 | u64 nr_entries; |
340 | hbt->timer(hbt->arg); | 372 | hbt->timer(hbt->arg); |
341 | 373 | ||
342 | if (browser->min_pcnt) { | 374 | if (hist_browser__has_filter(browser)) |
343 | hist_browser__update_pcnt_entries(browser); | 375 | hist_browser__update_nr_entries(browser); |
344 | nr_entries = browser->nr_pcnt_entries; | ||
345 | } else { | ||
346 | nr_entries = browser->hists->nr_entries; | ||
347 | } | ||
348 | 376 | ||
377 | nr_entries = hist_browser__nr_entries(browser); | ||
349 | ui_browser__update_nr_entries(&browser->b, nr_entries); | 378 | ui_browser__update_nr_entries(&browser->b, nr_entries); |
350 | 379 | ||
351 | if (browser->hists->stats.nr_lost_warned != | 380 | if (browser->hists->stats.nr_lost_warned != |
@@ -587,35 +616,6 @@ struct hpp_arg { | |||
587 | bool current_entry; | 616 | bool current_entry; |
588 | }; | 617 | }; |
589 | 618 | ||
590 | static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front) | ||
591 | { | ||
592 | struct hpp_arg *arg = hpp->ptr; | ||
593 | |||
594 | if (arg->current_entry && arg->b->navkeypressed) | ||
595 | ui_browser__set_color(arg->b, HE_COLORSET_SELECTED); | ||
596 | else | ||
597 | ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); | ||
598 | |||
599 | if (front) { | ||
600 | if (!symbol_conf.use_callchain) | ||
601 | return 0; | ||
602 | |||
603 | slsmg_printf("%c ", arg->folded_sign); | ||
604 | return 2; | ||
605 | } | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused) | ||
611 | { | ||
612 | struct hpp_arg *arg = hpp->ptr; | ||
613 | |||
614 | if (!arg->current_entry || !arg->b->navkeypressed) | ||
615 | ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) | 619 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) |
620 | { | 620 | { |
621 | struct hpp_arg *arg = hpp->ptr; | 621 | struct hpp_arg *arg = hpp->ptr; |
@@ -636,7 +636,7 @@ static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) | |||
636 | return ret; | 636 | return ret; |
637 | } | 637 | } |
638 | 638 | ||
639 | #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ | 639 | #define __HPP_COLOR_PERCENT_FN(_type, _field) \ |
640 | static u64 __hpp_get_##_field(struct hist_entry *he) \ | 640 | static u64 __hpp_get_##_field(struct hist_entry *he) \ |
641 | { \ | 641 | { \ |
642 | return he->stat._field; \ | 642 | return he->stat._field; \ |
@@ -647,22 +647,20 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | |||
647 | struct perf_hpp *hpp, \ | 647 | struct perf_hpp *hpp, \ |
648 | struct hist_entry *he) \ | 648 | struct hist_entry *he) \ |
649 | { \ | 649 | { \ |
650 | return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \ | 650 | return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \ |
651 | __hpp__slsmg_color_printf, true); \ | 651 | __hpp__slsmg_color_printf, true); \ |
652 | } | 652 | } |
653 | 653 | ||
654 | __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback) | 654 | __HPP_COLOR_PERCENT_FN(overhead, period) |
655 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback) | 655 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) |
656 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback) | 656 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us) |
657 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback) | 657 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) |
658 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback) | 658 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) |
659 | 659 | ||
660 | #undef __HPP_COLOR_PERCENT_FN | 660 | #undef __HPP_COLOR_PERCENT_FN |
661 | 661 | ||
662 | void hist_browser__init_hpp(void) | 662 | void hist_browser__init_hpp(void) |
663 | { | 663 | { |
664 | perf_hpp__init(); | ||
665 | |||
666 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 664 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
667 | hist_browser__hpp_color_overhead; | 665 | hist_browser__hpp_color_overhead; |
668 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = | 666 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = |
@@ -700,7 +698,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
700 | 698 | ||
701 | if (row_offset == 0) { | 699 | if (row_offset == 0) { |
702 | struct hpp_arg arg = { | 700 | struct hpp_arg arg = { |
703 | .b = &browser->b, | 701 | .b = &browser->b, |
704 | .folded_sign = folded_sign, | 702 | .folded_sign = folded_sign, |
705 | .current_entry = current_entry, | 703 | .current_entry = current_entry, |
706 | }; | 704 | }; |
@@ -713,11 +711,27 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
713 | ui_browser__gotorc(&browser->b, row, 0); | 711 | ui_browser__gotorc(&browser->b, row, 0); |
714 | 712 | ||
715 | perf_hpp__for_each_format(fmt) { | 713 | perf_hpp__for_each_format(fmt) { |
716 | if (!first) { | 714 | if (perf_hpp__should_skip(fmt)) |
715 | continue; | ||
716 | |||
717 | if (current_entry && browser->b.navkeypressed) { | ||
718 | ui_browser__set_color(&browser->b, | ||
719 | HE_COLORSET_SELECTED); | ||
720 | } else { | ||
721 | ui_browser__set_color(&browser->b, | ||
722 | HE_COLORSET_NORMAL); | ||
723 | } | ||
724 | |||
725 | if (first) { | ||
726 | if (symbol_conf.use_callchain) { | ||
727 | slsmg_printf("%c ", folded_sign); | ||
728 | width -= 2; | ||
729 | } | ||
730 | first = false; | ||
731 | } else { | ||
717 | slsmg_printf(" "); | 732 | slsmg_printf(" "); |
718 | width -= 2; | 733 | width -= 2; |
719 | } | 734 | } |
720 | first = false; | ||
721 | 735 | ||
722 | if (fmt->color) { | 736 | if (fmt->color) { |
723 | width -= fmt->color(fmt, &hpp, entry); | 737 | width -= fmt->color(fmt, &hpp, entry); |
@@ -731,8 +745,8 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
731 | if (!browser->b.navkeypressed) | 745 | if (!browser->b.navkeypressed) |
732 | width += 1; | 746 | width += 1; |
733 | 747 | ||
734 | hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists); | 748 | slsmg_write_nstring("", width); |
735 | slsmg_write_nstring(s, width); | 749 | |
736 | ++row; | 750 | ++row; |
737 | ++printed; | 751 | ++printed; |
738 | } else | 752 | } else |
@@ -769,12 +783,15 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) | |||
769 | 783 | ||
770 | for (nd = browser->top; nd; nd = rb_next(nd)) { | 784 | for (nd = browser->top; nd; nd = rb_next(nd)) { |
771 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 785 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
772 | float percent = h->stat.period * 100.0 / | 786 | u64 total = hists__total_period(h->hists); |
773 | hb->hists->stats.total_period; | 787 | float percent = 0.0; |
774 | 788 | ||
775 | if (h->filtered) | 789 | if (h->filtered) |
776 | continue; | 790 | continue; |
777 | 791 | ||
792 | if (total) | ||
793 | percent = h->stat.period * 100.0 / total; | ||
794 | |||
778 | if (percent < hb->min_pcnt) | 795 | if (percent < hb->min_pcnt) |
779 | continue; | 796 | continue; |
780 | 797 | ||
@@ -792,13 +809,13 @@ static struct rb_node *hists__filter_entries(struct rb_node *nd, | |||
792 | { | 809 | { |
793 | while (nd != NULL) { | 810 | while (nd != NULL) { |
794 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 811 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
795 | float percent = h->stat.period * 100.0 / | 812 | u64 total = hists__total_period(hists); |
796 | hists->stats.total_period; | 813 | float percent = 0.0; |
797 | 814 | ||
798 | if (percent < min_pcnt) | 815 | if (total) |
799 | return NULL; | 816 | percent = h->stat.period * 100.0 / total; |
800 | 817 | ||
801 | if (!h->filtered) | 818 | if (!h->filtered && percent >= min_pcnt) |
802 | return nd; | 819 | return nd; |
803 | 820 | ||
804 | nd = rb_next(nd); | 821 | nd = rb_next(nd); |
@@ -813,8 +830,11 @@ static struct rb_node *hists__filter_prev_entries(struct rb_node *nd, | |||
813 | { | 830 | { |
814 | while (nd != NULL) { | 831 | while (nd != NULL) { |
815 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 832 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
816 | float percent = h->stat.period * 100.0 / | 833 | u64 total = hists__total_period(hists); |
817 | hists->stats.total_period; | 834 | float percent = 0.0; |
835 | |||
836 | if (total) | ||
837 | percent = h->stat.period * 100.0 / total; | ||
818 | 838 | ||
819 | if (!h->filtered && percent >= min_pcnt) | 839 | if (!h->filtered && percent >= min_pcnt) |
820 | return nd; | 840 | return nd; |
@@ -1066,27 +1086,35 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
1066 | struct hist_entry *he, FILE *fp) | 1086 | struct hist_entry *he, FILE *fp) |
1067 | { | 1087 | { |
1068 | char s[8192]; | 1088 | char s[8192]; |
1069 | double percent; | ||
1070 | int printed = 0; | 1089 | int printed = 0; |
1071 | char folded_sign = ' '; | 1090 | char folded_sign = ' '; |
1091 | struct perf_hpp hpp = { | ||
1092 | .buf = s, | ||
1093 | .size = sizeof(s), | ||
1094 | }; | ||
1095 | struct perf_hpp_fmt *fmt; | ||
1096 | bool first = true; | ||
1097 | int ret; | ||
1072 | 1098 | ||
1073 | if (symbol_conf.use_callchain) | 1099 | if (symbol_conf.use_callchain) |
1074 | folded_sign = hist_entry__folded(he); | 1100 | folded_sign = hist_entry__folded(he); |
1075 | 1101 | ||
1076 | hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists); | ||
1077 | percent = (he->stat.period * 100.0) / browser->hists->stats.total_period; | ||
1078 | |||
1079 | if (symbol_conf.use_callchain) | 1102 | if (symbol_conf.use_callchain) |
1080 | printed += fprintf(fp, "%c ", folded_sign); | 1103 | printed += fprintf(fp, "%c ", folded_sign); |
1081 | 1104 | ||
1082 | printed += fprintf(fp, " %5.2f%%", percent); | 1105 | perf_hpp__for_each_format(fmt) { |
1083 | 1106 | if (perf_hpp__should_skip(fmt)) | |
1084 | if (symbol_conf.show_nr_samples) | 1107 | continue; |
1085 | printed += fprintf(fp, " %11u", he->stat.nr_events); | ||
1086 | 1108 | ||
1087 | if (symbol_conf.show_total_period) | 1109 | if (!first) { |
1088 | printed += fprintf(fp, " %12" PRIu64, he->stat.period); | 1110 | ret = scnprintf(hpp.buf, hpp.size, " "); |
1111 | advance_hpp(&hpp, ret); | ||
1112 | } else | ||
1113 | first = false; | ||
1089 | 1114 | ||
1115 | ret = fmt->entry(fmt, &hpp, he); | ||
1116 | advance_hpp(&hpp, ret); | ||
1117 | } | ||
1090 | printed += fprintf(fp, "%s\n", rtrim(s)); | 1118 | printed += fprintf(fp, "%s\n", rtrim(s)); |
1091 | 1119 | ||
1092 | if (folded_sign == '-') | 1120 | if (folded_sign == '-') |
@@ -1189,6 +1217,11 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1189 | char buf[512]; | 1217 | char buf[512]; |
1190 | size_t buflen = sizeof(buf); | 1218 | size_t buflen = sizeof(buf); |
1191 | 1219 | ||
1220 | if (symbol_conf.filter_relative) { | ||
1221 | nr_samples = hists->stats.nr_non_filtered_samples; | ||
1222 | nr_events = hists->stats.total_non_filtered_period; | ||
1223 | } | ||
1224 | |||
1192 | if (perf_evsel__is_group_event(evsel)) { | 1225 | if (perf_evsel__is_group_event(evsel)) { |
1193 | struct perf_evsel *pos; | 1226 | struct perf_evsel *pos; |
1194 | 1227 | ||
@@ -1196,8 +1229,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1196 | ev_name = buf; | 1229 | ev_name = buf; |
1197 | 1230 | ||
1198 | for_each_group_member(pos, evsel) { | 1231 | for_each_group_member(pos, evsel) { |
1199 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1232 | if (symbol_conf.filter_relative) { |
1200 | nr_events += pos->hists.stats.total_period; | 1233 | nr_samples += pos->hists.stats.nr_non_filtered_samples; |
1234 | nr_events += pos->hists.stats.total_non_filtered_period; | ||
1235 | } else { | ||
1236 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1237 | nr_events += pos->hists.stats.total_period; | ||
1238 | } | ||
1201 | } | 1239 | } |
1202 | } | 1240 | } |
1203 | 1241 | ||
@@ -1324,18 +1362,23 @@ close_file_and_continue: | |||
1324 | return ret; | 1362 | return ret; |
1325 | } | 1363 | } |
1326 | 1364 | ||
1327 | static void hist_browser__update_pcnt_entries(struct hist_browser *hb) | 1365 | static void hist_browser__update_nr_entries(struct hist_browser *hb) |
1328 | { | 1366 | { |
1329 | u64 nr_entries = 0; | 1367 | u64 nr_entries = 0; |
1330 | struct rb_node *nd = rb_first(&hb->hists->entries); | 1368 | struct rb_node *nd = rb_first(&hb->hists->entries); |
1331 | 1369 | ||
1332 | while (nd) { | 1370 | if (hb->min_pcnt == 0) { |
1371 | hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries; | ||
1372 | return; | ||
1373 | } | ||
1374 | |||
1375 | while ((nd = hists__filter_entries(nd, hb->hists, | ||
1376 | hb->min_pcnt)) != NULL) { | ||
1333 | nr_entries++; | 1377 | nr_entries++; |
1334 | nd = hists__filter_entries(rb_next(nd), hb->hists, | 1378 | nd = rb_next(nd); |
1335 | hb->min_pcnt); | ||
1336 | } | 1379 | } |
1337 | 1380 | ||
1338 | hb->nr_pcnt_entries = nr_entries; | 1381 | hb->nr_non_filtered_entries = nr_entries; |
1339 | } | 1382 | } |
1340 | 1383 | ||
1341 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 1384 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
@@ -1370,6 +1413,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1370 | "C Collapse all callchains\n" \ | 1413 | "C Collapse all callchains\n" \ |
1371 | "d Zoom into current DSO\n" \ | 1414 | "d Zoom into current DSO\n" \ |
1372 | "E Expand all callchains\n" \ | 1415 | "E Expand all callchains\n" \ |
1416 | "F Toggle percentage of filtered entries\n" \ | ||
1373 | 1417 | ||
1374 | /* help messages are sorted by lexical order of the hotkey */ | 1418 | /* help messages are sorted by lexical order of the hotkey */ |
1375 | const char report_help[] = HIST_BROWSER_HELP_COMMON | 1419 | const char report_help[] = HIST_BROWSER_HELP_COMMON |
@@ -1391,7 +1435,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1391 | 1435 | ||
1392 | if (min_pcnt) { | 1436 | if (min_pcnt) { |
1393 | browser->min_pcnt = min_pcnt; | 1437 | browser->min_pcnt = min_pcnt; |
1394 | hist_browser__update_pcnt_entries(browser); | 1438 | hist_browser__update_nr_entries(browser); |
1395 | } | 1439 | } |
1396 | 1440 | ||
1397 | fstack = pstack__new(2); | 1441 | fstack = pstack__new(2); |
@@ -1475,6 +1519,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1475 | if (env->arch) | 1519 | if (env->arch) |
1476 | tui__header_window(env); | 1520 | tui__header_window(env); |
1477 | continue; | 1521 | continue; |
1522 | case 'F': | ||
1523 | symbol_conf.filter_relative ^= 1; | ||
1524 | continue; | ||
1478 | case K_F1: | 1525 | case K_F1: |
1479 | case 'h': | 1526 | case 'h': |
1480 | case '?': | 1527 | case '?': |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index e395ef9b0ae0..9d90683914d4 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -43,7 +43,7 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, | |||
43 | struct perf_hpp *hpp, \ | 43 | struct perf_hpp *hpp, \ |
44 | struct hist_entry *he) \ | 44 | struct hist_entry *he) \ |
45 | { \ | 45 | { \ |
46 | return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ | 46 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ |
47 | __percent_color_snprintf, true); \ | 47 | __percent_color_snprintf, true); \ |
48 | } | 48 | } |
49 | 49 | ||
@@ -58,8 +58,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) | |||
58 | 58 | ||
59 | void perf_gtk__init_hpp(void) | 59 | void perf_gtk__init_hpp(void) |
60 | { | 60 | { |
61 | perf_hpp__init(); | ||
62 | |||
63 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 61 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
64 | perf_gtk__hpp_color_overhead; | 62 | perf_gtk__hpp_color_overhead; |
65 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = | 63 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = |
@@ -153,7 +151,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
153 | struct perf_hpp_fmt *fmt; | 151 | struct perf_hpp_fmt *fmt; |
154 | GType col_types[MAX_COLUMNS]; | 152 | GType col_types[MAX_COLUMNS]; |
155 | GtkCellRenderer *renderer; | 153 | GtkCellRenderer *renderer; |
156 | struct sort_entry *se; | ||
157 | GtkTreeStore *store; | 154 | GtkTreeStore *store; |
158 | struct rb_node *nd; | 155 | struct rb_node *nd; |
159 | GtkWidget *view; | 156 | GtkWidget *view; |
@@ -172,16 +169,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
172 | perf_hpp__for_each_format(fmt) | 169 | perf_hpp__for_each_format(fmt) |
173 | col_types[nr_cols++] = G_TYPE_STRING; | 170 | col_types[nr_cols++] = G_TYPE_STRING; |
174 | 171 | ||
175 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
176 | if (se->elide) | ||
177 | continue; | ||
178 | |||
179 | if (se == &sort_sym) | ||
180 | sym_col = nr_cols; | ||
181 | |||
182 | col_types[nr_cols++] = G_TYPE_STRING; | ||
183 | } | ||
184 | |||
185 | store = gtk_tree_store_newv(nr_cols, col_types); | 172 | store = gtk_tree_store_newv(nr_cols, col_types); |
186 | 173 | ||
187 | view = gtk_tree_view_new(); | 174 | view = gtk_tree_view_new(); |
@@ -191,6 +178,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
191 | col_idx = 0; | 178 | col_idx = 0; |
192 | 179 | ||
193 | perf_hpp__for_each_format(fmt) { | 180 | perf_hpp__for_each_format(fmt) { |
181 | if (perf_hpp__should_skip(fmt)) | ||
182 | continue; | ||
183 | |||
194 | fmt->header(fmt, &hpp, hists_to_evsel(hists)); | 184 | fmt->header(fmt, &hpp, hists_to_evsel(hists)); |
195 | 185 | ||
196 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | 186 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), |
@@ -199,16 +189,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
199 | col_idx++, NULL); | 189 | col_idx++, NULL); |
200 | } | 190 | } |
201 | 191 | ||
202 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
203 | if (se->elide) | ||
204 | continue; | ||
205 | |||
206 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
207 | -1, se->se_header, | ||
208 | renderer, "text", | ||
209 | col_idx++, NULL); | ||
210 | } | ||
211 | |||
212 | for (col_idx = 0; col_idx < nr_cols; col_idx++) { | 192 | for (col_idx = 0; col_idx < nr_cols; col_idx++) { |
213 | GtkTreeViewColumn *column; | 193 | GtkTreeViewColumn *column; |
214 | 194 | ||
@@ -228,12 +208,15 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
228 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 208 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
229 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 209 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
230 | GtkTreeIter iter; | 210 | GtkTreeIter iter; |
231 | float percent = h->stat.period * 100.0 / | 211 | u64 total = hists__total_period(h->hists); |
232 | hists->stats.total_period; | 212 | float percent = 0.0; |
233 | 213 | ||
234 | if (h->filtered) | 214 | if (h->filtered) |
235 | continue; | 215 | continue; |
236 | 216 | ||
217 | if (total) | ||
218 | percent = h->stat.period * 100.0 / total; | ||
219 | |||
237 | if (percent < min_pcnt) | 220 | if (percent < min_pcnt) |
238 | continue; | 221 | continue; |
239 | 222 | ||
@@ -242,6 +225,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
242 | col_idx = 0; | 225 | col_idx = 0; |
243 | 226 | ||
244 | perf_hpp__for_each_format(fmt) { | 227 | perf_hpp__for_each_format(fmt) { |
228 | if (perf_hpp__should_skip(fmt)) | ||
229 | continue; | ||
230 | |||
245 | if (fmt->color) | 231 | if (fmt->color) |
246 | fmt->color(fmt, &hpp, h); | 232 | fmt->color(fmt, &hpp, h); |
247 | else | 233 | else |
@@ -250,23 +236,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
250 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); | 236 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); |
251 | } | 237 | } |
252 | 238 | ||
253 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
254 | if (se->elide) | ||
255 | continue; | ||
256 | |||
257 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
258 | hists__col_len(hists, se->se_width_idx)); | ||
259 | |||
260 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); | ||
261 | } | ||
262 | |||
263 | if (symbol_conf.use_callchain && sort__has_sym) { | 239 | if (symbol_conf.use_callchain && sort__has_sym) { |
264 | u64 total; | ||
265 | |||
266 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 240 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
267 | total = h->stat.period; | 241 | total = h->stat.period; |
268 | else | ||
269 | total = hists->stats.total_period; | ||
270 | 242 | ||
271 | perf_gtk__add_callchain(&h->sorted_chain, store, &iter, | 243 | perf_gtk__add_callchain(&h->sorted_chain, store, &iter, |
272 | sym_col, total); | 244 | sym_col, total); |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 0f403b83e9d1..4484f5bd1b14 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -16,30 +16,25 @@ | |||
16 | }) | 16 | }) |
17 | 17 | ||
18 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 18 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, |
19 | hpp_field_fn get_field, hpp_callback_fn callback, | 19 | hpp_field_fn get_field, const char *fmt, |
20 | const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent) | 20 | hpp_snprint_fn print_fn, bool fmt_percent) |
21 | { | 21 | { |
22 | int ret = 0; | 22 | int ret; |
23 | struct hists *hists = he->hists; | 23 | struct hists *hists = he->hists; |
24 | struct perf_evsel *evsel = hists_to_evsel(hists); | 24 | struct perf_evsel *evsel = hists_to_evsel(hists); |
25 | char *buf = hpp->buf; | 25 | char *buf = hpp->buf; |
26 | size_t size = hpp->size; | 26 | size_t size = hpp->size; |
27 | 27 | ||
28 | if (callback) { | ||
29 | ret = callback(hpp, true); | ||
30 | advance_hpp(hpp, ret); | ||
31 | } | ||
32 | |||
33 | if (fmt_percent) { | 28 | if (fmt_percent) { |
34 | double percent = 0.0; | 29 | double percent = 0.0; |
30 | u64 total = hists__total_period(hists); | ||
35 | 31 | ||
36 | if (hists->stats.total_period) | 32 | if (total) |
37 | percent = 100.0 * get_field(he) / | 33 | percent = 100.0 * get_field(he) / total; |
38 | hists->stats.total_period; | ||
39 | 34 | ||
40 | ret += hpp__call_print_fn(hpp, print_fn, fmt, percent); | 35 | ret = hpp__call_print_fn(hpp, print_fn, fmt, percent); |
41 | } else | 36 | } else |
42 | ret += hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); | 37 | ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); |
43 | 38 | ||
44 | if (perf_evsel__is_group_event(evsel)) { | 39 | if (perf_evsel__is_group_event(evsel)) { |
45 | int prev_idx, idx_delta; | 40 | int prev_idx, idx_delta; |
@@ -50,7 +45,7 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
50 | 45 | ||
51 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | 46 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { |
52 | u64 period = get_field(pair); | 47 | u64 period = get_field(pair); |
53 | u64 total = pair->hists->stats.total_period; | 48 | u64 total = hists__total_period(pair->hists); |
54 | 49 | ||
55 | if (!total) | 50 | if (!total) |
56 | continue; | 51 | continue; |
@@ -99,13 +94,6 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
99 | } | 94 | } |
100 | } | 95 | } |
101 | 96 | ||
102 | if (callback) { | ||
103 | int __ret = callback(hpp, false); | ||
104 | |||
105 | advance_hpp(hpp, __ret); | ||
106 | ret += __ret; | ||
107 | } | ||
108 | |||
109 | /* | 97 | /* |
110 | * Restore original buf and size as it's where caller expects | 98 | * Restore original buf and size as it's where caller expects |
111 | * the result will be saved. | 99 | * the result will be saved. |
@@ -116,6 +104,62 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
116 | return ret; | 104 | return ret; |
117 | } | 105 | } |
118 | 106 | ||
107 | static int field_cmp(u64 field_a, u64 field_b) | ||
108 | { | ||
109 | if (field_a > field_b) | ||
110 | return 1; | ||
111 | if (field_a < field_b) | ||
112 | return -1; | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | static int __hpp__sort(struct hist_entry *a, struct hist_entry *b, | ||
117 | hpp_field_fn get_field) | ||
118 | { | ||
119 | s64 ret; | ||
120 | int i, nr_members; | ||
121 | struct perf_evsel *evsel; | ||
122 | struct hist_entry *pair; | ||
123 | u64 *fields_a, *fields_b; | ||
124 | |||
125 | ret = field_cmp(get_field(a), get_field(b)); | ||
126 | if (ret || !symbol_conf.event_group) | ||
127 | return ret; | ||
128 | |||
129 | evsel = hists_to_evsel(a->hists); | ||
130 | if (!perf_evsel__is_group_event(evsel)) | ||
131 | return ret; | ||
132 | |||
133 | nr_members = evsel->nr_members; | ||
134 | fields_a = calloc(sizeof(*fields_a), nr_members); | ||
135 | fields_b = calloc(sizeof(*fields_b), nr_members); | ||
136 | |||
137 | if (!fields_a || !fields_b) | ||
138 | goto out; | ||
139 | |||
140 | list_for_each_entry(pair, &a->pairs.head, pairs.node) { | ||
141 | evsel = hists_to_evsel(pair->hists); | ||
142 | fields_a[perf_evsel__group_idx(evsel)] = get_field(pair); | ||
143 | } | ||
144 | |||
145 | list_for_each_entry(pair, &b->pairs.head, pairs.node) { | ||
146 | evsel = hists_to_evsel(pair->hists); | ||
147 | fields_b[perf_evsel__group_idx(evsel)] = get_field(pair); | ||
148 | } | ||
149 | |||
150 | for (i = 1; i < nr_members; i++) { | ||
151 | ret = field_cmp(fields_a[i], fields_b[i]); | ||
152 | if (ret) | ||
153 | break; | ||
154 | } | ||
155 | |||
156 | out: | ||
157 | free(fields_a); | ||
158 | free(fields_b); | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
119 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | 163 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ |
120 | static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 164 | static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
121 | struct perf_hpp *hpp, \ | 165 | struct perf_hpp *hpp, \ |
@@ -179,7 +223,7 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
179 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 223 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
180 | struct perf_hpp *hpp, struct hist_entry *he) \ | 224 | struct perf_hpp *hpp, struct hist_entry *he) \ |
181 | { \ | 225 | { \ |
182 | return __hpp__fmt(hpp, he, he_get_##_field, NULL, " %6.2f%%", \ | 226 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ |
183 | hpp_color_scnprintf, true); \ | 227 | hpp_color_scnprintf, true); \ |
184 | } | 228 | } |
185 | 229 | ||
@@ -188,10 +232,16 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | |||
188 | struct perf_hpp *hpp, struct hist_entry *he) \ | 232 | struct perf_hpp *hpp, struct hist_entry *he) \ |
189 | { \ | 233 | { \ |
190 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | 234 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ |
191 | return __hpp__fmt(hpp, he, he_get_##_field, NULL, fmt, \ | 235 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ |
192 | hpp_entry_scnprintf, true); \ | 236 | hpp_entry_scnprintf, true); \ |
193 | } | 237 | } |
194 | 238 | ||
239 | #define __HPP_SORT_FN(_type, _field) \ | ||
240 | static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | ||
241 | { \ | ||
242 | return __hpp__sort(a, b, he_get_##_field); \ | ||
243 | } | ||
244 | |||
195 | #define __HPP_ENTRY_RAW_FN(_type, _field) \ | 245 | #define __HPP_ENTRY_RAW_FN(_type, _field) \ |
196 | static u64 he_get_raw_##_field(struct hist_entry *he) \ | 246 | static u64 he_get_raw_##_field(struct hist_entry *he) \ |
197 | { \ | 247 | { \ |
@@ -202,20 +252,29 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | |||
202 | struct perf_hpp *hpp, struct hist_entry *he) \ | 252 | struct perf_hpp *hpp, struct hist_entry *he) \ |
203 | { \ | 253 | { \ |
204 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ | 254 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ |
205 | return __hpp__fmt(hpp, he, he_get_raw_##_field, NULL, fmt, \ | 255 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \ |
206 | hpp_entry_scnprintf, false); \ | 256 | hpp_entry_scnprintf, false); \ |
207 | } | 257 | } |
208 | 258 | ||
259 | #define __HPP_SORT_RAW_FN(_type, _field) \ | ||
260 | static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | ||
261 | { \ | ||
262 | return __hpp__sort(a, b, he_get_raw_##_field); \ | ||
263 | } | ||
264 | |||
265 | |||
209 | #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ | 266 | #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ |
210 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | 267 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ |
211 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | 268 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ |
212 | __HPP_COLOR_PERCENT_FN(_type, _field) \ | 269 | __HPP_COLOR_PERCENT_FN(_type, _field) \ |
213 | __HPP_ENTRY_PERCENT_FN(_type, _field) | 270 | __HPP_ENTRY_PERCENT_FN(_type, _field) \ |
271 | __HPP_SORT_FN(_type, _field) | ||
214 | 272 | ||
215 | #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ | 273 | #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ |
216 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | 274 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ |
217 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | 275 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ |
218 | __HPP_ENTRY_RAW_FN(_type, _field) | 276 | __HPP_ENTRY_RAW_FN(_type, _field) \ |
277 | __HPP_SORT_RAW_FN(_type, _field) | ||
219 | 278 | ||
220 | 279 | ||
221 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) | 280 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) |
@@ -227,19 +286,31 @@ HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | |||
227 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | 286 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) |
228 | HPP_RAW_FNS(period, "Period", period, 12, 12) | 287 | HPP_RAW_FNS(period, "Period", period, 12, 12) |
229 | 288 | ||
289 | static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, | ||
290 | struct hist_entry *b __maybe_unused) | ||
291 | { | ||
292 | return 0; | ||
293 | } | ||
294 | |||
230 | #define HPP__COLOR_PRINT_FNS(_name) \ | 295 | #define HPP__COLOR_PRINT_FNS(_name) \ |
231 | { \ | 296 | { \ |
232 | .header = hpp__header_ ## _name, \ | 297 | .header = hpp__header_ ## _name, \ |
233 | .width = hpp__width_ ## _name, \ | 298 | .width = hpp__width_ ## _name, \ |
234 | .color = hpp__color_ ## _name, \ | 299 | .color = hpp__color_ ## _name, \ |
235 | .entry = hpp__entry_ ## _name \ | 300 | .entry = hpp__entry_ ## _name, \ |
301 | .cmp = hpp__nop_cmp, \ | ||
302 | .collapse = hpp__nop_cmp, \ | ||
303 | .sort = hpp__sort_ ## _name, \ | ||
236 | } | 304 | } |
237 | 305 | ||
238 | #define HPP__PRINT_FNS(_name) \ | 306 | #define HPP__PRINT_FNS(_name) \ |
239 | { \ | 307 | { \ |
240 | .header = hpp__header_ ## _name, \ | 308 | .header = hpp__header_ ## _name, \ |
241 | .width = hpp__width_ ## _name, \ | 309 | .width = hpp__width_ ## _name, \ |
242 | .entry = hpp__entry_ ## _name \ | 310 | .entry = hpp__entry_ ## _name, \ |
311 | .cmp = hpp__nop_cmp, \ | ||
312 | .collapse = hpp__nop_cmp, \ | ||
313 | .sort = hpp__sort_ ## _name, \ | ||
243 | } | 314 | } |
244 | 315 | ||
245 | struct perf_hpp_fmt perf_hpp__format[] = { | 316 | struct perf_hpp_fmt perf_hpp__format[] = { |
@@ -253,6 +324,7 @@ struct perf_hpp_fmt perf_hpp__format[] = { | |||
253 | }; | 324 | }; |
254 | 325 | ||
255 | LIST_HEAD(perf_hpp__list); | 326 | LIST_HEAD(perf_hpp__list); |
327 | LIST_HEAD(perf_hpp__sort_list); | ||
256 | 328 | ||
257 | 329 | ||
258 | #undef HPP__COLOR_PRINT_FNS | 330 | #undef HPP__COLOR_PRINT_FNS |
@@ -270,6 +342,25 @@ LIST_HEAD(perf_hpp__list); | |||
270 | 342 | ||
271 | void perf_hpp__init(void) | 343 | void perf_hpp__init(void) |
272 | { | 344 | { |
345 | struct list_head *list; | ||
346 | int i; | ||
347 | |||
348 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
349 | struct perf_hpp_fmt *fmt = &perf_hpp__format[i]; | ||
350 | |||
351 | INIT_LIST_HEAD(&fmt->list); | ||
352 | |||
353 | /* sort_list may be linked by setup_sorting() */ | ||
354 | if (fmt->sort_list.next == NULL) | ||
355 | INIT_LIST_HEAD(&fmt->sort_list); | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * If user specified field order, no need to setup default fields. | ||
360 | */ | ||
361 | if (field_order) | ||
362 | return; | ||
363 | |||
273 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | 364 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); |
274 | 365 | ||
275 | if (symbol_conf.show_cpu_utilization) { | 366 | if (symbol_conf.show_cpu_utilization) { |
@@ -287,6 +378,11 @@ void perf_hpp__init(void) | |||
287 | 378 | ||
288 | if (symbol_conf.show_total_period) | 379 | if (symbol_conf.show_total_period) |
289 | perf_hpp__column_enable(PERF_HPP__PERIOD); | 380 | perf_hpp__column_enable(PERF_HPP__PERIOD); |
381 | |||
382 | /* prepend overhead field for backward compatiblity. */ | ||
383 | list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; | ||
384 | if (list_empty(list)) | ||
385 | list_add(list, &perf_hpp__sort_list); | ||
290 | } | 386 | } |
291 | 387 | ||
292 | void perf_hpp__column_register(struct perf_hpp_fmt *format) | 388 | void perf_hpp__column_register(struct perf_hpp_fmt *format) |
@@ -294,29 +390,90 @@ void perf_hpp__column_register(struct perf_hpp_fmt *format) | |||
294 | list_add_tail(&format->list, &perf_hpp__list); | 390 | list_add_tail(&format->list, &perf_hpp__list); |
295 | } | 391 | } |
296 | 392 | ||
393 | void perf_hpp__register_sort_field(struct perf_hpp_fmt *format) | ||
394 | { | ||
395 | list_add_tail(&format->sort_list, &perf_hpp__sort_list); | ||
396 | } | ||
397 | |||
297 | void perf_hpp__column_enable(unsigned col) | 398 | void perf_hpp__column_enable(unsigned col) |
298 | { | 399 | { |
299 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | 400 | BUG_ON(col >= PERF_HPP__MAX_INDEX); |
300 | perf_hpp__column_register(&perf_hpp__format[col]); | 401 | perf_hpp__column_register(&perf_hpp__format[col]); |
301 | } | 402 | } |
302 | 403 | ||
303 | int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, | 404 | void perf_hpp__setup_output_field(void) |
304 | struct hists *hists) | ||
305 | { | 405 | { |
306 | const char *sep = symbol_conf.field_sep; | 406 | struct perf_hpp_fmt *fmt; |
307 | struct sort_entry *se; | ||
308 | int ret = 0; | ||
309 | 407 | ||
310 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 408 | /* append sort keys to output field */ |
311 | if (se->elide) | 409 | perf_hpp__for_each_sort_list(fmt) { |
410 | if (!list_empty(&fmt->list)) | ||
312 | continue; | 411 | continue; |
313 | 412 | ||
314 | ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); | 413 | /* |
315 | ret += se->se_snprintf(he, s + ret, size - ret, | 414 | * sort entry fields are dynamically created, |
316 | hists__col_len(hists, se->se_width_idx)); | 415 | * so they can share a same sort key even though |
416 | * the list is empty. | ||
417 | */ | ||
418 | if (perf_hpp__is_sort_entry(fmt)) { | ||
419 | struct perf_hpp_fmt *pos; | ||
420 | |||
421 | perf_hpp__for_each_format(pos) { | ||
422 | if (perf_hpp__same_sort_entry(pos, fmt)) | ||
423 | goto next; | ||
424 | } | ||
425 | } | ||
426 | |||
427 | perf_hpp__column_register(fmt); | ||
428 | next: | ||
429 | continue; | ||
317 | } | 430 | } |
431 | } | ||
318 | 432 | ||
319 | return ret; | 433 | void perf_hpp__append_sort_keys(void) |
434 | { | ||
435 | struct perf_hpp_fmt *fmt; | ||
436 | |||
437 | /* append output fields to sort keys */ | ||
438 | perf_hpp__for_each_format(fmt) { | ||
439 | if (!list_empty(&fmt->sort_list)) | ||
440 | continue; | ||
441 | |||
442 | /* | ||
443 | * sort entry fields are dynamically created, | ||
444 | * so they can share a same sort key even though | ||
445 | * the list is empty. | ||
446 | */ | ||
447 | if (perf_hpp__is_sort_entry(fmt)) { | ||
448 | struct perf_hpp_fmt *pos; | ||
449 | |||
450 | perf_hpp__for_each_sort_list(pos) { | ||
451 | if (perf_hpp__same_sort_entry(pos, fmt)) | ||
452 | goto next; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | perf_hpp__register_sort_field(fmt); | ||
457 | next: | ||
458 | continue; | ||
459 | } | ||
460 | } | ||
461 | |||
462 | void perf_hpp__reset_output_field(void) | ||
463 | { | ||
464 | struct perf_hpp_fmt *fmt, *tmp; | ||
465 | |||
466 | /* reset output fields */ | ||
467 | perf_hpp__for_each_format_safe(fmt, tmp) { | ||
468 | list_del_init(&fmt->list); | ||
469 | list_del_init(&fmt->sort_list); | ||
470 | } | ||
471 | |||
472 | /* reset sort keys */ | ||
473 | perf_hpp__for_each_sort_list_safe(fmt, tmp) { | ||
474 | list_del_init(&fmt->list); | ||
475 | list_del_init(&fmt->sort_list); | ||
476 | } | ||
320 | } | 477 | } |
321 | 478 | ||
322 | /* | 479 | /* |
@@ -325,22 +482,23 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, | |||
325 | unsigned int hists__sort_list_width(struct hists *hists) | 482 | unsigned int hists__sort_list_width(struct hists *hists) |
326 | { | 483 | { |
327 | struct perf_hpp_fmt *fmt; | 484 | struct perf_hpp_fmt *fmt; |
328 | struct sort_entry *se; | 485 | int ret = 0; |
329 | int i = 0, ret = 0; | 486 | bool first = true; |
330 | struct perf_hpp dummy_hpp; | 487 | struct perf_hpp dummy_hpp; |
331 | 488 | ||
332 | perf_hpp__for_each_format(fmt) { | 489 | perf_hpp__for_each_format(fmt) { |
333 | if (i) | 490 | if (perf_hpp__should_skip(fmt)) |
491 | continue; | ||
492 | |||
493 | if (first) | ||
494 | first = false; | ||
495 | else | ||
334 | ret += 2; | 496 | ret += 2; |
335 | 497 | ||
336 | ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); | 498 | ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); |
337 | } | 499 | } |
338 | 500 | ||
339 | list_for_each_entry(se, &hist_entry__sort_list, list) | 501 | if (verbose && sort__has_sym) /* Addr + origin */ |
340 | if (!se->elide) | ||
341 | ret += 2 + hists__col_len(hists, se->se_width_idx); | ||
342 | |||
343 | if (verbose) /* Addr + origin */ | ||
344 | ret += 3 + BITS_PER_LONG / 4; | 502 | ret += 3 + BITS_PER_LONG / 4; |
345 | 503 | ||
346 | return ret; | 504 | return ret; |
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index 29ec8efffefb..f34f89eb607c 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef _PERF_UI_PROGRESS_H_ | 1 | #ifndef _PERF_UI_PROGRESS_H_ |
2 | #define _PERF_UI_PROGRESS_H_ 1 | 2 | #define _PERF_UI_PROGRESS_H_ 1 |
3 | 3 | ||
4 | #include <../types.h> | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | void ui_progress__finish(void); | 6 | void ui_progress__finish(void); |
7 | 7 | ||
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index 5df5140a9f29..ba51fa8a1176 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c | |||
@@ -86,8 +86,6 @@ void setup_browser(bool fallback_to_pager) | |||
86 | use_browser = 0; | 86 | use_browser = 0; |
87 | if (fallback_to_pager) | 87 | if (fallback_to_pager) |
88 | setup_pager(); | 88 | setup_pager(); |
89 | |||
90 | perf_hpp__init(); | ||
91 | break; | 89 | break; |
92 | } | 90 | } |
93 | } | 91 | } |
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index d59893edf031..9f57991025a9 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -183,7 +183,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, | |||
183 | * the symbol. No need to print it otherwise it appears as | 183 | * the symbol. No need to print it otherwise it appears as |
184 | * displayed twice. | 184 | * displayed twice. |
185 | */ | 185 | */ |
186 | if (!i++ && sort__first_dimension == SORT_SYM) | 186 | if (!i++ && field_order == NULL && |
187 | sort_order && !prefixcmp(sort_order, "sym")) | ||
187 | continue; | 188 | continue; |
188 | if (!printed) { | 189 | if (!printed) { |
189 | ret += callchain__fprintf_left_margin(fp, left_margin); | 190 | ret += callchain__fprintf_left_margin(fp, left_margin); |
@@ -296,18 +297,24 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he, | |||
296 | int left_margin = 0; | 297 | int left_margin = 0; |
297 | u64 total_period = hists->stats.total_period; | 298 | u64 total_period = hists->stats.total_period; |
298 | 299 | ||
299 | if (sort__first_dimension == SORT_COMM) { | 300 | if (field_order == NULL && (sort_order == NULL || |
300 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | 301 | !prefixcmp(sort_order, "comm"))) { |
301 | typeof(*se), list); | 302 | struct perf_hpp_fmt *fmt; |
302 | left_margin = hists__col_len(hists, se->se_width_idx); | 303 | |
303 | left_margin -= thread__comm_len(he->thread); | 304 | perf_hpp__for_each_format(fmt) { |
304 | } | 305 | if (!perf_hpp__is_sort_entry(fmt)) |
306 | continue; | ||
305 | 307 | ||
308 | /* must be 'comm' sort entry */ | ||
309 | left_margin = fmt->width(fmt, NULL, hists_to_evsel(hists)); | ||
310 | left_margin -= thread__comm_len(he->thread); | ||
311 | break; | ||
312 | } | ||
313 | } | ||
306 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); | 314 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); |
307 | } | 315 | } |
308 | 316 | ||
309 | static int hist_entry__period_snprintf(struct perf_hpp *hpp, | 317 | static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) |
310 | struct hist_entry *he) | ||
311 | { | 318 | { |
312 | const char *sep = symbol_conf.field_sep; | 319 | const char *sep = symbol_conf.field_sep; |
313 | struct perf_hpp_fmt *fmt; | 320 | struct perf_hpp_fmt *fmt; |
@@ -319,6 +326,9 @@ static int hist_entry__period_snprintf(struct perf_hpp *hpp, | |||
319 | return 0; | 326 | return 0; |
320 | 327 | ||
321 | perf_hpp__for_each_format(fmt) { | 328 | perf_hpp__for_each_format(fmt) { |
329 | if (perf_hpp__should_skip(fmt)) | ||
330 | continue; | ||
331 | |||
322 | /* | 332 | /* |
323 | * If there's no field_sep, we still need | 333 | * If there's no field_sep, we still need |
324 | * to display initial ' '. | 334 | * to display initial ' '. |
@@ -353,8 +363,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, | |||
353 | if (size == 0 || size > bfsz) | 363 | if (size == 0 || size > bfsz) |
354 | size = hpp.size = bfsz; | 364 | size = hpp.size = bfsz; |
355 | 365 | ||
356 | ret = hist_entry__period_snprintf(&hpp, he); | 366 | hist_entry__snprintf(he, &hpp); |
357 | hist_entry__sort_snprintf(he, bf + ret, size - ret, hists); | ||
358 | 367 | ||
359 | ret = fprintf(fp, "%s\n", bf); | 368 | ret = fprintf(fp, "%s\n", bf); |
360 | 369 | ||
@@ -368,12 +377,10 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
368 | int max_cols, float min_pcnt, FILE *fp) | 377 | int max_cols, float min_pcnt, FILE *fp) |
369 | { | 378 | { |
370 | struct perf_hpp_fmt *fmt; | 379 | struct perf_hpp_fmt *fmt; |
371 | struct sort_entry *se; | ||
372 | struct rb_node *nd; | 380 | struct rb_node *nd; |
373 | size_t ret = 0; | 381 | size_t ret = 0; |
374 | unsigned int width; | 382 | unsigned int width; |
375 | const char *sep = symbol_conf.field_sep; | 383 | const char *sep = symbol_conf.field_sep; |
376 | const char *col_width = symbol_conf.col_width_list_str; | ||
377 | int nr_rows = 0; | 384 | int nr_rows = 0; |
378 | char bf[96]; | 385 | char bf[96]; |
379 | struct perf_hpp dummy_hpp = { | 386 | struct perf_hpp dummy_hpp = { |
@@ -386,12 +393,19 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
386 | 393 | ||
387 | init_rem_hits(); | 394 | init_rem_hits(); |
388 | 395 | ||
396 | |||
397 | perf_hpp__for_each_format(fmt) | ||
398 | perf_hpp__reset_width(fmt, hists); | ||
399 | |||
389 | if (!show_header) | 400 | if (!show_header) |
390 | goto print_entries; | 401 | goto print_entries; |
391 | 402 | ||
392 | fprintf(fp, "# "); | 403 | fprintf(fp, "# "); |
393 | 404 | ||
394 | perf_hpp__for_each_format(fmt) { | 405 | perf_hpp__for_each_format(fmt) { |
406 | if (perf_hpp__should_skip(fmt)) | ||
407 | continue; | ||
408 | |||
395 | if (!first) | 409 | if (!first) |
396 | fprintf(fp, "%s", sep ?: " "); | 410 | fprintf(fp, "%s", sep ?: " "); |
397 | else | 411 | else |
@@ -401,28 +415,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
401 | fprintf(fp, "%s", bf); | 415 | fprintf(fp, "%s", bf); |
402 | } | 416 | } |
403 | 417 | ||
404 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
405 | if (se->elide) | ||
406 | continue; | ||
407 | if (sep) { | ||
408 | fprintf(fp, "%c%s", *sep, se->se_header); | ||
409 | continue; | ||
410 | } | ||
411 | width = strlen(se->se_header); | ||
412 | if (symbol_conf.col_width_list_str) { | ||
413 | if (col_width) { | ||
414 | hists__set_col_len(hists, se->se_width_idx, | ||
415 | atoi(col_width)); | ||
416 | col_width = strchr(col_width, ','); | ||
417 | if (col_width) | ||
418 | ++col_width; | ||
419 | } | ||
420 | } | ||
421 | if (!hists__new_col_len(hists, se->se_width_idx, width)) | ||
422 | width = hists__col_len(hists, se->se_width_idx); | ||
423 | fprintf(fp, " %*s", width, se->se_header); | ||
424 | } | ||
425 | |||
426 | fprintf(fp, "\n"); | 418 | fprintf(fp, "\n"); |
427 | if (max_rows && ++nr_rows >= max_rows) | 419 | if (max_rows && ++nr_rows >= max_rows) |
428 | goto out; | 420 | goto out; |
@@ -437,6 +429,9 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
437 | perf_hpp__for_each_format(fmt) { | 429 | perf_hpp__for_each_format(fmt) { |
438 | unsigned int i; | 430 | unsigned int i; |
439 | 431 | ||
432 | if (perf_hpp__should_skip(fmt)) | ||
433 | continue; | ||
434 | |||
440 | if (!first) | 435 | if (!first) |
441 | fprintf(fp, "%s", sep ?: " "); | 436 | fprintf(fp, "%s", sep ?: " "); |
442 | else | 437 | else |
@@ -447,20 +442,6 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
447 | fprintf(fp, "."); | 442 | fprintf(fp, "."); |
448 | } | 443 | } |
449 | 444 | ||
450 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
451 | unsigned int i; | ||
452 | |||
453 | if (se->elide) | ||
454 | continue; | ||
455 | |||
456 | fprintf(fp, " "); | ||
457 | width = hists__col_len(hists, se->se_width_idx); | ||
458 | if (width == 0) | ||
459 | width = strlen(se->se_header); | ||
460 | for (i = 0; i < width; i++) | ||
461 | fprintf(fp, "."); | ||
462 | } | ||
463 | |||
464 | fprintf(fp, "\n"); | 445 | fprintf(fp, "\n"); |
465 | if (max_rows && ++nr_rows >= max_rows) | 446 | if (max_rows && ++nr_rows >= max_rows) |
466 | goto out; | 447 | goto out; |
@@ -495,7 +476,7 @@ print_entries: | |||
495 | break; | 476 | break; |
496 | 477 | ||
497 | if (h->ms.map == NULL && verbose > 1) { | 478 | if (h->ms.map == NULL && verbose > 1) { |
498 | __map_groups__fprintf_maps(&h->thread->mg, | 479 | __map_groups__fprintf_maps(h->thread->mg, |
499 | MAP__FUNCTION, verbose, fp); | 480 | MAP__FUNCTION, verbose, fp); |
500 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 481 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
501 | } | 482 | } |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 56ad4f5287de..112d6e268150 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <stdint.h> | 5 | #include <stdint.h> |
6 | #include "types.h" | 6 | #include <linux/types.h> |
7 | #include "symbol.h" | 7 | #include "symbol.h" |
8 | #include "hist.h" | 8 | #include "hist.h" |
9 | #include "sort.h" | 9 | #include "sort.h" |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 6baabe63182b..a904a4cfe7d3 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -25,7 +25,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
25 | struct addr_location al; | 25 | struct addr_location al; |
26 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 26 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
27 | struct thread *thread = machine__findnew_thread(machine, sample->pid, | 27 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
28 | sample->pid); | 28 | sample->tid); |
29 | 29 | ||
30 | if (thread == NULL) { | 30 | if (thread == NULL) { |
31 | pr_err("problem processing %d event, skipping it.\n", | 31 | pr_err("problem processing %d event, skipping it.\n", |
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 845ef865eced..ae392561470b 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #define BUILD_ID_SIZE 20 | 4 | #define BUILD_ID_SIZE 20 |
5 | 5 | ||
6 | #include "tool.h" | 6 | #include "tool.h" |
7 | #include "types.h" | 7 | #include <linux/types.h> |
8 | 8 | ||
9 | extern struct perf_tool build_id__mark_dso_hit_ops; | 9 | extern struct perf_tool build_id__mark_dso_hit_ops; |
10 | struct dso; | 10 | struct dso; |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 8d9db454f1a9..9a42382b3921 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -25,6 +25,84 @@ | |||
25 | 25 | ||
26 | __thread struct callchain_cursor callchain_cursor; | 26 | __thread struct callchain_cursor callchain_cursor; |
27 | 27 | ||
28 | int | ||
29 | parse_callchain_report_opt(const char *arg) | ||
30 | { | ||
31 | char *tok, *tok2; | ||
32 | char *endptr; | ||
33 | |||
34 | symbol_conf.use_callchain = true; | ||
35 | |||
36 | if (!arg) | ||
37 | return 0; | ||
38 | |||
39 | tok = strtok((char *)arg, ","); | ||
40 | if (!tok) | ||
41 | return -1; | ||
42 | |||
43 | /* get the output mode */ | ||
44 | if (!strncmp(tok, "graph", strlen(arg))) { | ||
45 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
46 | |||
47 | } else if (!strncmp(tok, "flat", strlen(arg))) { | ||
48 | callchain_param.mode = CHAIN_FLAT; | ||
49 | } else if (!strncmp(tok, "fractal", strlen(arg))) { | ||
50 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
51 | } else if (!strncmp(tok, "none", strlen(arg))) { | ||
52 | callchain_param.mode = CHAIN_NONE; | ||
53 | symbol_conf.use_callchain = false; | ||
54 | return 0; | ||
55 | } else { | ||
56 | return -1; | ||
57 | } | ||
58 | |||
59 | /* get the min percentage */ | ||
60 | tok = strtok(NULL, ","); | ||
61 | if (!tok) | ||
62 | goto setup; | ||
63 | |||
64 | callchain_param.min_percent = strtod(tok, &endptr); | ||
65 | if (tok == endptr) | ||
66 | return -1; | ||
67 | |||
68 | /* get the print limit */ | ||
69 | tok2 = strtok(NULL, ","); | ||
70 | if (!tok2) | ||
71 | goto setup; | ||
72 | |||
73 | if (tok2[0] != 'c') { | ||
74 | callchain_param.print_limit = strtoul(tok2, &endptr, 0); | ||
75 | tok2 = strtok(NULL, ","); | ||
76 | if (!tok2) | ||
77 | goto setup; | ||
78 | } | ||
79 | |||
80 | /* get the call chain order */ | ||
81 | if (!strncmp(tok2, "caller", strlen("caller"))) | ||
82 | callchain_param.order = ORDER_CALLER; | ||
83 | else if (!strncmp(tok2, "callee", strlen("callee"))) | ||
84 | callchain_param.order = ORDER_CALLEE; | ||
85 | else | ||
86 | return -1; | ||
87 | |||
88 | /* Get the sort key */ | ||
89 | tok2 = strtok(NULL, ","); | ||
90 | if (!tok2) | ||
91 | goto setup; | ||
92 | if (!strncmp(tok2, "function", strlen("function"))) | ||
93 | callchain_param.key = CCKEY_FUNCTION; | ||
94 | else if (!strncmp(tok2, "address", strlen("address"))) | ||
95 | callchain_param.key = CCKEY_ADDRESS; | ||
96 | else | ||
97 | return -1; | ||
98 | setup: | ||
99 | if (callchain_register_param(&callchain_param) < 0) { | ||
100 | pr_err("Can't register callchain params\n"); | ||
101 | return -1; | ||
102 | } | ||
103 | return 0; | ||
104 | } | ||
105 | |||
28 | static void | 106 | static void |
29 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 107 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
30 | enum chain_mode mode) | 108 | enum chain_mode mode) |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 8ad97e9b119f..bde2b0cc24cf 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -7,6 +7,13 @@ | |||
7 | #include "event.h" | 7 | #include "event.h" |
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | 9 | ||
10 | enum perf_call_graph_mode { | ||
11 | CALLCHAIN_NONE, | ||
12 | CALLCHAIN_FP, | ||
13 | CALLCHAIN_DWARF, | ||
14 | CALLCHAIN_MAX | ||
15 | }; | ||
16 | |||
10 | enum chain_mode { | 17 | enum chain_mode { |
11 | CHAIN_NONE, | 18 | CHAIN_NONE, |
12 | CHAIN_FLAT, | 19 | CHAIN_FLAT, |
@@ -157,4 +164,5 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent | |||
157 | int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); | 164 | int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); |
158 | 165 | ||
159 | extern const char record_callchain_help[]; | 166 | extern const char record_callchain_help[]; |
167 | int parse_callchain_report_opt(const char *arg); | ||
160 | #endif /* __PERF_CALLCHAIN_H */ | 168 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 3e0fdd369ccb..24519e14ac56 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include "util.h" | 11 | #include "util.h" |
12 | #include "cache.h" | 12 | #include "cache.h" |
13 | #include "exec_cmd.h" | 13 | #include "exec_cmd.h" |
14 | #include "util/hist.h" /* perf_hist_config */ | ||
14 | 15 | ||
15 | #define MAXNAME (256) | 16 | #define MAXNAME (256) |
16 | 17 | ||
@@ -355,6 +356,9 @@ int perf_default_config(const char *var, const char *value, | |||
355 | if (!prefixcmp(var, "core.")) | 356 | if (!prefixcmp(var, "core.")) |
356 | return perf_default_core_config(var, value); | 357 | return perf_default_core_config(var, value); |
357 | 358 | ||
359 | if (!prefixcmp(var, "hist.")) | ||
360 | return perf_hist_config(var, value); | ||
361 | |||
358 | /* Add other config variables here. */ | 362 | /* Add other config variables here. */ |
359 | return 0; | 363 | return 0; |
360 | } | 364 | } |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 7fe4994eeb63..c4e55b71010c 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -317,3 +317,163 @@ int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep) | |||
317 | { | 317 | { |
318 | return cpu_map__build_map(cpus, corep, cpu_map__get_core); | 318 | return cpu_map__build_map(cpus, corep, cpu_map__get_core); |
319 | } | 319 | } |
320 | |||
321 | /* setup simple routines to easily access node numbers given a cpu number */ | ||
322 | static int get_max_num(char *path, int *max) | ||
323 | { | ||
324 | size_t num; | ||
325 | char *buf; | ||
326 | int err = 0; | ||
327 | |||
328 | if (filename__read_str(path, &buf, &num)) | ||
329 | return -1; | ||
330 | |||
331 | buf[num] = '\0'; | ||
332 | |||
333 | /* start on the right, to find highest node num */ | ||
334 | while (--num) { | ||
335 | if ((buf[num] == ',') || (buf[num] == '-')) { | ||
336 | num++; | ||
337 | break; | ||
338 | } | ||
339 | } | ||
340 | if (sscanf(&buf[num], "%d", max) < 1) { | ||
341 | err = -1; | ||
342 | goto out; | ||
343 | } | ||
344 | |||
345 | /* convert from 0-based to 1-based */ | ||
346 | (*max)++; | ||
347 | |||
348 | out: | ||
349 | free(buf); | ||
350 | return err; | ||
351 | } | ||
352 | |||
353 | /* Determine highest possible cpu in the system for sparse allocation */ | ||
354 | static void set_max_cpu_num(void) | ||
355 | { | ||
356 | const char *mnt; | ||
357 | char path[PATH_MAX]; | ||
358 | int ret = -1; | ||
359 | |||
360 | /* set up default */ | ||
361 | max_cpu_num = 4096; | ||
362 | |||
363 | mnt = sysfs__mountpoint(); | ||
364 | if (!mnt) | ||
365 | goto out; | ||
366 | |||
367 | /* get the highest possible cpu number for a sparse allocation */ | ||
368 | ret = snprintf(path, PATH_MAX, "%s/devices/system/cpu/possible", mnt); | ||
369 | if (ret == PATH_MAX) { | ||
370 | pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); | ||
371 | goto out; | ||
372 | } | ||
373 | |||
374 | ret = get_max_num(path, &max_cpu_num); | ||
375 | |||
376 | out: | ||
377 | if (ret) | ||
378 | pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num); | ||
379 | } | ||
380 | |||
381 | /* Determine highest possible node in the system for sparse allocation */ | ||
382 | static void set_max_node_num(void) | ||
383 | { | ||
384 | const char *mnt; | ||
385 | char path[PATH_MAX]; | ||
386 | int ret = -1; | ||
387 | |||
388 | /* set up default */ | ||
389 | max_node_num = 8; | ||
390 | |||
391 | mnt = sysfs__mountpoint(); | ||
392 | if (!mnt) | ||
393 | goto out; | ||
394 | |||
395 | /* get the highest possible cpu number for a sparse allocation */ | ||
396 | ret = snprintf(path, PATH_MAX, "%s/devices/system/node/possible", mnt); | ||
397 | if (ret == PATH_MAX) { | ||
398 | pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); | ||
399 | goto out; | ||
400 | } | ||
401 | |||
402 | ret = get_max_num(path, &max_node_num); | ||
403 | |||
404 | out: | ||
405 | if (ret) | ||
406 | pr_err("Failed to read max nodes, using default of %d\n", max_node_num); | ||
407 | } | ||
408 | |||
409 | static int init_cpunode_map(void) | ||
410 | { | ||
411 | int i; | ||
412 | |||
413 | set_max_cpu_num(); | ||
414 | set_max_node_num(); | ||
415 | |||
416 | cpunode_map = calloc(max_cpu_num, sizeof(int)); | ||
417 | if (!cpunode_map) { | ||
418 | pr_err("%s: calloc failed\n", __func__); | ||
419 | return -1; | ||
420 | } | ||
421 | |||
422 | for (i = 0; i < max_cpu_num; i++) | ||
423 | cpunode_map[i] = -1; | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | int cpu__setup_cpunode_map(void) | ||
429 | { | ||
430 | struct dirent *dent1, *dent2; | ||
431 | DIR *dir1, *dir2; | ||
432 | unsigned int cpu, mem; | ||
433 | char buf[PATH_MAX]; | ||
434 | char path[PATH_MAX]; | ||
435 | const char *mnt; | ||
436 | int n; | ||
437 | |||
438 | /* initialize globals */ | ||
439 | if (init_cpunode_map()) | ||
440 | return -1; | ||
441 | |||
442 | mnt = sysfs__mountpoint(); | ||
443 | if (!mnt) | ||
444 | return 0; | ||
445 | |||
446 | n = snprintf(path, PATH_MAX, "%s/devices/system/node", mnt); | ||
447 | if (n == PATH_MAX) { | ||
448 | pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); | ||
449 | return -1; | ||
450 | } | ||
451 | |||
452 | dir1 = opendir(path); | ||
453 | if (!dir1) | ||
454 | return 0; | ||
455 | |||
456 | /* walk tree and setup map */ | ||
457 | while ((dent1 = readdir(dir1)) != NULL) { | ||
458 | if (dent1->d_type != DT_DIR || sscanf(dent1->d_name, "node%u", &mem) < 1) | ||
459 | continue; | ||
460 | |||
461 | n = snprintf(buf, PATH_MAX, "%s/%s", path, dent1->d_name); | ||
462 | if (n == PATH_MAX) { | ||
463 | pr_err("sysfs path crossed PATH_MAX(%d) size\n", PATH_MAX); | ||
464 | continue; | ||
465 | } | ||
466 | |||
467 | dir2 = opendir(buf); | ||
468 | if (!dir2) | ||
469 | continue; | ||
470 | while ((dent2 = readdir(dir2)) != NULL) { | ||
471 | if (dent2->d_type != DT_LNK || sscanf(dent2->d_name, "cpu%u", &cpu) < 1) | ||
472 | continue; | ||
473 | cpunode_map[cpu] = mem; | ||
474 | } | ||
475 | closedir(dir2); | ||
476 | } | ||
477 | closedir(dir1); | ||
478 | return 0; | ||
479 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index b123bb9d6f55..61a654849002 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -4,6 +4,9 @@ | |||
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | 6 | ||
7 | #include "perf.h" | ||
8 | #include "util/debug.h" | ||
9 | |||
7 | struct cpu_map { | 10 | struct cpu_map { |
8 | int nr; | 11 | int nr; |
9 | int map[]; | 12 | int map[]; |
@@ -46,4 +49,36 @@ static inline bool cpu_map__empty(const struct cpu_map *map) | |||
46 | return map ? map->map[0] == -1 : true; | 49 | return map ? map->map[0] == -1 : true; |
47 | } | 50 | } |
48 | 51 | ||
52 | int max_cpu_num; | ||
53 | int max_node_num; | ||
54 | int *cpunode_map; | ||
55 | |||
56 | int cpu__setup_cpunode_map(void); | ||
57 | |||
58 | static inline int cpu__max_node(void) | ||
59 | { | ||
60 | if (unlikely(!max_node_num)) | ||
61 | pr_debug("cpu_map not initialized\n"); | ||
62 | |||
63 | return max_node_num; | ||
64 | } | ||
65 | |||
66 | static inline int cpu__max_cpu(void) | ||
67 | { | ||
68 | if (unlikely(!max_cpu_num)) | ||
69 | pr_debug("cpu_map not initialized\n"); | ||
70 | |||
71 | return max_cpu_num; | ||
72 | } | ||
73 | |||
74 | static inline int cpu__get_node(int cpu) | ||
75 | { | ||
76 | if (unlikely(cpunode_map == NULL)) { | ||
77 | pr_debug("cpu_map not initialized\n"); | ||
78 | return -1; | ||
79 | } | ||
80 | |||
81 | return cpunode_map[cpu]; | ||
82 | } | ||
83 | |||
49 | #endif /* __PERF_CPUMAP_H */ | 84 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ab06f1c03655..38efe95a7fdd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -4,7 +4,7 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include <linux/types.h> |
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | 9 | #include "build-id.h" |
10 | 10 | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9d12aa6dd485..65795b835b39 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -699,7 +699,7 @@ void thread__find_addr_map(struct thread *thread, | |||
699 | enum map_type type, u64 addr, | 699 | enum map_type type, u64 addr, |
700 | struct addr_location *al) | 700 | struct addr_location *al) |
701 | { | 701 | { |
702 | struct map_groups *mg = &thread->mg; | 702 | struct map_groups *mg = thread->mg; |
703 | bool load_map = false; | 703 | bool load_map = false; |
704 | 704 | ||
705 | al->machine = machine; | 705 | al->machine = machine; |
@@ -788,7 +788,7 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
788 | { | 788 | { |
789 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 789 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
790 | struct thread *thread = machine__findnew_thread(machine, sample->pid, | 790 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
791 | sample->pid); | 791 | sample->tid); |
792 | 792 | ||
793 | if (thread == NULL) | 793 | if (thread == NULL) |
794 | return -1; | 794 | return -1; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 38457d447a13..d970232cb270 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -112,6 +112,30 @@ struct sample_read { | |||
112 | }; | 112 | }; |
113 | }; | 113 | }; |
114 | 114 | ||
115 | struct ip_callchain { | ||
116 | u64 nr; | ||
117 | u64 ips[0]; | ||
118 | }; | ||
119 | |||
120 | struct branch_flags { | ||
121 | u64 mispred:1; | ||
122 | u64 predicted:1; | ||
123 | u64 in_tx:1; | ||
124 | u64 abort:1; | ||
125 | u64 reserved:60; | ||
126 | }; | ||
127 | |||
128 | struct branch_entry { | ||
129 | u64 from; | ||
130 | u64 to; | ||
131 | struct branch_flags flags; | ||
132 | }; | ||
133 | |||
134 | struct branch_stack { | ||
135 | u64 nr; | ||
136 | struct branch_entry entries[0]; | ||
137 | }; | ||
138 | |||
115 | struct perf_sample { | 139 | struct perf_sample { |
116 | u64 ip; | 140 | u64 ip; |
117 | u32 pid, tid; | 141 | u32 pid, tid; |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0c9926cfb292..a52e9a5bb2d0 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -5,12 +5,12 @@ | |||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stddef.h> | 6 | #include <stddef.h> |
7 | #include <linux/perf_event.h> | 7 | #include <linux/perf_event.h> |
8 | #include "types.h" | 8 | #include <linux/types.h> |
9 | #include "xyarray.h" | 9 | #include "xyarray.h" |
10 | #include "cgroup.h" | 10 | #include "cgroup.h" |
11 | #include "hist.h" | 11 | #include "hist.h" |
12 | #include "symbol.h" | 12 | #include "symbol.h" |
13 | 13 | ||
14 | struct perf_counts_values { | 14 | struct perf_counts_values { |
15 | union { | 15 | union { |
16 | struct { | 16 | struct { |
@@ -91,6 +91,11 @@ struct perf_evsel { | |||
91 | char *group_name; | 91 | char *group_name; |
92 | }; | 92 | }; |
93 | 93 | ||
94 | union u64_swap { | ||
95 | u64 val64; | ||
96 | u32 val32[2]; | ||
97 | }; | ||
98 | |||
94 | #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) | 99 | #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) |
95 | 100 | ||
96 | struct cpu_map; | 101 | struct cpu_map; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a2d047bdf4ef..d08cfe499404 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -4,10 +4,10 @@ | |||
4 | #include <linux/perf_event.h> | 4 | #include <linux/perf_event.h> |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include <linux/bitmap.h> |
8 | #include <linux/types.h> | ||
8 | #include "event.h" | 9 | #include "event.h" |
9 | 10 | ||
10 | #include <linux/bitmap.h> | ||
11 | 11 | ||
12 | enum { | 12 | enum { |
13 | HEADER_RESERVED = 0, /* always cleared */ | 13 | HEADER_RESERVED = 0, /* always cleared */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f38590d7561b..b262b44b7a65 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -225,14 +225,18 @@ static void he_stat__decay(struct he_stat *he_stat) | |||
225 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 225 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
226 | { | 226 | { |
227 | u64 prev_period = he->stat.period; | 227 | u64 prev_period = he->stat.period; |
228 | u64 diff; | ||
228 | 229 | ||
229 | if (prev_period == 0) | 230 | if (prev_period == 0) |
230 | return true; | 231 | return true; |
231 | 232 | ||
232 | he_stat__decay(&he->stat); | 233 | he_stat__decay(&he->stat); |
233 | 234 | ||
235 | diff = prev_period - he->stat.period; | ||
236 | |||
237 | hists->stats.total_period -= diff; | ||
234 | if (!he->filtered) | 238 | if (!he->filtered) |
235 | hists->stats.total_period -= prev_period - he->stat.period; | 239 | hists->stats.total_non_filtered_period -= diff; |
236 | 240 | ||
237 | return he->stat.period == 0; | 241 | return he->stat.period == 0; |
238 | } | 242 | } |
@@ -259,8 +263,11 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
259 | if (sort__need_collapse) | 263 | if (sort__need_collapse) |
260 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | 264 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); |
261 | 265 | ||
262 | hist_entry__free(n); | ||
263 | --hists->nr_entries; | 266 | --hists->nr_entries; |
267 | if (!n->filtered) | ||
268 | --hists->nr_non_filtered_entries; | ||
269 | |||
270 | hist_entry__free(n); | ||
264 | } | 271 | } |
265 | } | 272 | } |
266 | } | 273 | } |
@@ -317,15 +324,6 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
317 | return he; | 324 | return he; |
318 | } | 325 | } |
319 | 326 | ||
320 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) | ||
321 | { | ||
322 | if (!h->filtered) { | ||
323 | hists__calc_col_len(hists, h); | ||
324 | ++hists->nr_entries; | ||
325 | hists->stats.total_period += h->stat.period; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | static u8 symbol__parent_filter(const struct symbol *parent) | 327 | static u8 symbol__parent_filter(const struct symbol *parent) |
330 | { | 328 | { |
331 | if (symbol_conf.exclude_other && parent == NULL) | 329 | if (symbol_conf.exclude_other && parent == NULL) |
@@ -391,7 +389,6 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
391 | if (!he) | 389 | if (!he) |
392 | return NULL; | 390 | return NULL; |
393 | 391 | ||
394 | hists->nr_entries++; | ||
395 | rb_link_node(&he->rb_node_in, parent, p); | 392 | rb_link_node(&he->rb_node_in, parent, p); |
396 | rb_insert_color(&he->rb_node_in, hists->entries_in); | 393 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
397 | out: | 394 | out: |
@@ -435,11 +432,14 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
435 | int64_t | 432 | int64_t |
436 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 433 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
437 | { | 434 | { |
438 | struct sort_entry *se; | 435 | struct perf_hpp_fmt *fmt; |
439 | int64_t cmp = 0; | 436 | int64_t cmp = 0; |
440 | 437 | ||
441 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 438 | perf_hpp__for_each_sort_list(fmt) { |
442 | cmp = se->se_cmp(left, right); | 439 | if (perf_hpp__should_skip(fmt)) |
440 | continue; | ||
441 | |||
442 | cmp = fmt->cmp(left, right); | ||
443 | if (cmp) | 443 | if (cmp) |
444 | break; | 444 | break; |
445 | } | 445 | } |
@@ -450,15 +450,14 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |||
450 | int64_t | 450 | int64_t |
451 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | 451 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) |
452 | { | 452 | { |
453 | struct sort_entry *se; | 453 | struct perf_hpp_fmt *fmt; |
454 | int64_t cmp = 0; | 454 | int64_t cmp = 0; |
455 | 455 | ||
456 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 456 | perf_hpp__for_each_sort_list(fmt) { |
457 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | 457 | if (perf_hpp__should_skip(fmt)) |
458 | 458 | continue; | |
459 | f = se->se_collapse ?: se->se_cmp; | ||
460 | 459 | ||
461 | cmp = f(left, right); | 460 | cmp = fmt->collapse(left, right); |
462 | if (cmp) | 461 | if (cmp) |
463 | break; | 462 | break; |
464 | } | 463 | } |
@@ -571,64 +570,50 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
571 | } | 570 | } |
572 | } | 571 | } |
573 | 572 | ||
574 | /* | 573 | static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) |
575 | * reverse the map, sort on period. | ||
576 | */ | ||
577 | |||
578 | static int period_cmp(u64 period_a, u64 period_b) | ||
579 | { | 574 | { |
580 | if (period_a > period_b) | 575 | struct perf_hpp_fmt *fmt; |
581 | return 1; | 576 | int64_t cmp = 0; |
582 | if (period_a < period_b) | ||
583 | return -1; | ||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | static int hist_entry__sort_on_period(struct hist_entry *a, | ||
588 | struct hist_entry *b) | ||
589 | { | ||
590 | int ret; | ||
591 | int i, nr_members; | ||
592 | struct perf_evsel *evsel; | ||
593 | struct hist_entry *pair; | ||
594 | u64 *periods_a, *periods_b; | ||
595 | 577 | ||
596 | ret = period_cmp(a->stat.period, b->stat.period); | 578 | perf_hpp__for_each_sort_list(fmt) { |
597 | if (ret || !symbol_conf.event_group) | 579 | if (perf_hpp__should_skip(fmt)) |
598 | return ret; | 580 | continue; |
599 | 581 | ||
600 | evsel = hists_to_evsel(a->hists); | 582 | cmp = fmt->sort(a, b); |
601 | nr_members = evsel->nr_members; | 583 | if (cmp) |
602 | if (nr_members <= 1) | 584 | break; |
603 | return ret; | 585 | } |
604 | 586 | ||
605 | periods_a = zalloc(sizeof(periods_a) * nr_members); | 587 | return cmp; |
606 | periods_b = zalloc(sizeof(periods_b) * nr_members); | 588 | } |
607 | 589 | ||
608 | if (!periods_a || !periods_b) | 590 | static void hists__reset_filter_stats(struct hists *hists) |
609 | goto out; | 591 | { |
592 | hists->nr_non_filtered_entries = 0; | ||
593 | hists->stats.total_non_filtered_period = 0; | ||
594 | } | ||
610 | 595 | ||
611 | list_for_each_entry(pair, &a->pairs.head, pairs.node) { | 596 | void hists__reset_stats(struct hists *hists) |
612 | evsel = hists_to_evsel(pair->hists); | 597 | { |
613 | periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; | 598 | hists->nr_entries = 0; |
614 | } | 599 | hists->stats.total_period = 0; |
615 | 600 | ||
616 | list_for_each_entry(pair, &b->pairs.head, pairs.node) { | 601 | hists__reset_filter_stats(hists); |
617 | evsel = hists_to_evsel(pair->hists); | 602 | } |
618 | periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; | ||
619 | } | ||
620 | 603 | ||
621 | for (i = 1; i < nr_members; i++) { | 604 | static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h) |
622 | ret = period_cmp(periods_a[i], periods_b[i]); | 605 | { |
623 | if (ret) | 606 | hists->nr_non_filtered_entries++; |
624 | break; | 607 | hists->stats.total_non_filtered_period += h->stat.period; |
625 | } | 608 | } |
626 | 609 | ||
627 | out: | 610 | void hists__inc_stats(struct hists *hists, struct hist_entry *h) |
628 | free(periods_a); | 611 | { |
629 | free(periods_b); | 612 | if (!h->filtered) |
613 | hists__inc_filter_stats(hists, h); | ||
630 | 614 | ||
631 | return ret; | 615 | hists->nr_entries++; |
616 | hists->stats.total_period += h->stat.period; | ||
632 | } | 617 | } |
633 | 618 | ||
634 | static void __hists__insert_output_entry(struct rb_root *entries, | 619 | static void __hists__insert_output_entry(struct rb_root *entries, |
@@ -647,7 +632,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
647 | parent = *p; | 632 | parent = *p; |
648 | iter = rb_entry(parent, struct hist_entry, rb_node); | 633 | iter = rb_entry(parent, struct hist_entry, rb_node); |
649 | 634 | ||
650 | if (hist_entry__sort_on_period(he, iter) > 0) | 635 | if (hist_entry__sort(he, iter) > 0) |
651 | p = &(*p)->rb_left; | 636 | p = &(*p)->rb_left; |
652 | else | 637 | else |
653 | p = &(*p)->rb_right; | 638 | p = &(*p)->rb_right; |
@@ -674,8 +659,7 @@ void hists__output_resort(struct hists *hists) | |||
674 | next = rb_first(root); | 659 | next = rb_first(root); |
675 | hists->entries = RB_ROOT; | 660 | hists->entries = RB_ROOT; |
676 | 661 | ||
677 | hists->nr_entries = 0; | 662 | hists__reset_stats(hists); |
678 | hists->stats.total_period = 0; | ||
679 | hists__reset_col_len(hists); | 663 | hists__reset_col_len(hists); |
680 | 664 | ||
681 | while (next) { | 665 | while (next) { |
@@ -683,7 +667,10 @@ void hists__output_resort(struct hists *hists) | |||
683 | next = rb_next(&n->rb_node_in); | 667 | next = rb_next(&n->rb_node_in); |
684 | 668 | ||
685 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); | 669 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); |
686 | hists__inc_nr_entries(hists, n); | 670 | hists__inc_stats(hists, n); |
671 | |||
672 | if (!n->filtered) | ||
673 | hists__calc_col_len(hists, n); | ||
687 | } | 674 | } |
688 | } | 675 | } |
689 | 676 | ||
@@ -694,13 +681,13 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h | |||
694 | if (h->filtered) | 681 | if (h->filtered) |
695 | return; | 682 | return; |
696 | 683 | ||
697 | ++hists->nr_entries; | 684 | /* force fold unfiltered entry for simplicity */ |
698 | if (h->ms.unfolded) | 685 | h->ms.unfolded = false; |
699 | hists->nr_entries += h->nr_rows; | ||
700 | h->row_offset = 0; | 686 | h->row_offset = 0; |
701 | hists->stats.total_period += h->stat.period; | ||
702 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; | ||
703 | 687 | ||
688 | hists->stats.nr_non_filtered_samples += h->stat.nr_events; | ||
689 | |||
690 | hists__inc_filter_stats(hists, h); | ||
704 | hists__calc_col_len(hists, h); | 691 | hists__calc_col_len(hists, h); |
705 | } | 692 | } |
706 | 693 | ||
@@ -721,8 +708,9 @@ void hists__filter_by_dso(struct hists *hists) | |||
721 | { | 708 | { |
722 | struct rb_node *nd; | 709 | struct rb_node *nd; |
723 | 710 | ||
724 | hists->nr_entries = hists->stats.total_period = 0; | 711 | hists->stats.nr_non_filtered_samples = 0; |
725 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 712 | |
713 | hists__reset_filter_stats(hists); | ||
726 | hists__reset_col_len(hists); | 714 | hists__reset_col_len(hists); |
727 | 715 | ||
728 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 716 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
@@ -754,8 +742,9 @@ void hists__filter_by_thread(struct hists *hists) | |||
754 | { | 742 | { |
755 | struct rb_node *nd; | 743 | struct rb_node *nd; |
756 | 744 | ||
757 | hists->nr_entries = hists->stats.total_period = 0; | 745 | hists->stats.nr_non_filtered_samples = 0; |
758 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 746 | |
747 | hists__reset_filter_stats(hists); | ||
759 | hists__reset_col_len(hists); | 748 | hists__reset_col_len(hists); |
760 | 749 | ||
761 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 750 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
@@ -785,8 +774,9 @@ void hists__filter_by_symbol(struct hists *hists) | |||
785 | { | 774 | { |
786 | struct rb_node *nd; | 775 | struct rb_node *nd; |
787 | 776 | ||
788 | hists->nr_entries = hists->stats.total_period = 0; | 777 | hists->stats.nr_non_filtered_samples = 0; |
789 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 778 | |
779 | hists__reset_filter_stats(hists); | ||
790 | hists__reset_col_len(hists); | 780 | hists__reset_col_len(hists); |
791 | 781 | ||
792 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 782 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
@@ -847,7 +837,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | |||
847 | he->hists = hists; | 837 | he->hists = hists; |
848 | rb_link_node(&he->rb_node_in, parent, p); | 838 | rb_link_node(&he->rb_node_in, parent, p); |
849 | rb_insert_color(&he->rb_node_in, root); | 839 | rb_insert_color(&he->rb_node_in, root); |
850 | hists__inc_nr_entries(hists, he); | 840 | hists__inc_stats(hists, he); |
851 | he->dummy = true; | 841 | he->dummy = true; |
852 | } | 842 | } |
853 | out: | 843 | out: |
@@ -931,3 +921,30 @@ int hists__link(struct hists *leader, struct hists *other) | |||
931 | 921 | ||
932 | return 0; | 922 | return 0; |
933 | } | 923 | } |
924 | |||
925 | u64 hists__total_period(struct hists *hists) | ||
926 | { | ||
927 | return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : | ||
928 | hists->stats.total_period; | ||
929 | } | ||
930 | |||
931 | int parse_filter_percentage(const struct option *opt __maybe_unused, | ||
932 | const char *arg, int unset __maybe_unused) | ||
933 | { | ||
934 | if (!strcmp(arg, "relative")) | ||
935 | symbol_conf.filter_relative = true; | ||
936 | else if (!strcmp(arg, "absolute")) | ||
937 | symbol_conf.filter_relative = false; | ||
938 | else | ||
939 | return -1; | ||
940 | |||
941 | return 0; | ||
942 | } | ||
943 | |||
944 | int perf_hist_config(const char *var, const char *value) | ||
945 | { | ||
946 | if (!strcmp(var, "hist.percentage")) | ||
947 | return parse_filter_percentage(NULL, value, 0); | ||
948 | |||
949 | return 0; | ||
950 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 1f1f513dfe7f..a8418d19808d 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -37,9 +37,11 @@ enum hist_filter { | |||
37 | */ | 37 | */ |
38 | struct events_stats { | 38 | struct events_stats { |
39 | u64 total_period; | 39 | u64 total_period; |
40 | u64 total_non_filtered_period; | ||
40 | u64 total_lost; | 41 | u64 total_lost; |
41 | u64 total_invalid_chains; | 42 | u64 total_invalid_chains; |
42 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | 43 | u32 nr_events[PERF_RECORD_HEADER_MAX]; |
44 | u32 nr_non_filtered_samples; | ||
43 | u32 nr_lost_warned; | 45 | u32 nr_lost_warned; |
44 | u32 nr_unknown_events; | 46 | u32 nr_unknown_events; |
45 | u32 nr_invalid_chains; | 47 | u32 nr_invalid_chains; |
@@ -83,6 +85,7 @@ struct hists { | |||
83 | struct rb_root entries; | 85 | struct rb_root entries; |
84 | struct rb_root entries_collapsed; | 86 | struct rb_root entries_collapsed; |
85 | u64 nr_entries; | 87 | u64 nr_entries; |
88 | u64 nr_non_filtered_entries; | ||
86 | const struct thread *thread_filter; | 89 | const struct thread *thread_filter; |
87 | const struct dso *dso_filter; | 90 | const struct dso *dso_filter; |
88 | const char *uid_filter_str; | 91 | const char *uid_filter_str; |
@@ -112,7 +115,9 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | |||
112 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 115 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
113 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 116 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
114 | 117 | ||
115 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); | 118 | u64 hists__total_period(struct hists *hists); |
119 | void hists__reset_stats(struct hists *hists); | ||
120 | void hists__inc_stats(struct hists *hists, struct hist_entry *h); | ||
116 | void hists__inc_nr_events(struct hists *hists, u32 type); | 121 | void hists__inc_nr_events(struct hists *hists, u32 type); |
117 | void events_stats__inc(struct events_stats *stats, u32 type); | 122 | void events_stats__inc(struct events_stats *stats, u32 type); |
118 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); | 123 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); |
@@ -124,6 +129,12 @@ void hists__filter_by_dso(struct hists *hists); | |||
124 | void hists__filter_by_thread(struct hists *hists); | 129 | void hists__filter_by_thread(struct hists *hists); |
125 | void hists__filter_by_symbol(struct hists *hists); | 130 | void hists__filter_by_symbol(struct hists *hists); |
126 | 131 | ||
132 | static inline bool hists__has_filter(struct hists *hists) | ||
133 | { | ||
134 | return hists->thread_filter || hists->dso_filter || | ||
135 | hists->symbol_filter_str; | ||
136 | } | ||
137 | |||
127 | u16 hists__col_len(struct hists *hists, enum hist_column col); | 138 | u16 hists__col_len(struct hists *hists, enum hist_column col); |
128 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); | 139 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len); |
129 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); | 140 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len); |
@@ -149,15 +160,29 @@ struct perf_hpp_fmt { | |||
149 | struct hist_entry *he); | 160 | struct hist_entry *he); |
150 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 161 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
151 | struct hist_entry *he); | 162 | struct hist_entry *he); |
163 | int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b); | ||
164 | int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b); | ||
165 | int64_t (*sort)(struct hist_entry *a, struct hist_entry *b); | ||
152 | 166 | ||
153 | struct list_head list; | 167 | struct list_head list; |
168 | struct list_head sort_list; | ||
154 | }; | 169 | }; |
155 | 170 | ||
156 | extern struct list_head perf_hpp__list; | 171 | extern struct list_head perf_hpp__list; |
172 | extern struct list_head perf_hpp__sort_list; | ||
157 | 173 | ||
158 | #define perf_hpp__for_each_format(format) \ | 174 | #define perf_hpp__for_each_format(format) \ |
159 | list_for_each_entry(format, &perf_hpp__list, list) | 175 | list_for_each_entry(format, &perf_hpp__list, list) |
160 | 176 | ||
177 | #define perf_hpp__for_each_format_safe(format, tmp) \ | ||
178 | list_for_each_entry_safe(format, tmp, &perf_hpp__list, list) | ||
179 | |||
180 | #define perf_hpp__for_each_sort_list(format) \ | ||
181 | list_for_each_entry(format, &perf_hpp__sort_list, sort_list) | ||
182 | |||
183 | #define perf_hpp__for_each_sort_list_safe(format, tmp) \ | ||
184 | list_for_each_entry_safe(format, tmp, &perf_hpp__sort_list, sort_list) | ||
185 | |||
161 | extern struct perf_hpp_fmt perf_hpp__format[]; | 186 | extern struct perf_hpp_fmt perf_hpp__format[]; |
162 | 187 | ||
163 | enum { | 188 | enum { |
@@ -176,14 +201,23 @@ enum { | |||
176 | void perf_hpp__init(void); | 201 | void perf_hpp__init(void); |
177 | void perf_hpp__column_register(struct perf_hpp_fmt *format); | 202 | void perf_hpp__column_register(struct perf_hpp_fmt *format); |
178 | void perf_hpp__column_enable(unsigned col); | 203 | void perf_hpp__column_enable(unsigned col); |
204 | void perf_hpp__register_sort_field(struct perf_hpp_fmt *format); | ||
205 | void perf_hpp__setup_output_field(void); | ||
206 | void perf_hpp__reset_output_field(void); | ||
207 | void perf_hpp__append_sort_keys(void); | ||
208 | |||
209 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); | ||
210 | bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); | ||
211 | bool perf_hpp__should_skip(struct perf_hpp_fmt *format); | ||
212 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); | ||
179 | 213 | ||
180 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); | 214 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); |
181 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); | 215 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); |
182 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); | 216 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); |
183 | 217 | ||
184 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 218 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, |
185 | hpp_field_fn get_field, hpp_callback_fn callback, | 219 | hpp_field_fn get_field, const char *fmt, |
186 | const char *fmt, hpp_snprint_fn print_fn, bool fmt_percent); | 220 | hpp_snprint_fn print_fn, bool fmt_percent); |
187 | 221 | ||
188 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 222 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
189 | { | 223 | { |
@@ -250,4 +284,10 @@ static inline int script_browse(const char *script_opt __maybe_unused) | |||
250 | #endif | 284 | #endif |
251 | 285 | ||
252 | unsigned int hists__sort_list_width(struct hists *hists); | 286 | unsigned int hists__sort_list_width(struct hists *hists); |
287 | |||
288 | struct option; | ||
289 | int parse_filter_percentage(const struct option *opt __maybe_unused, | ||
290 | const char *arg, int unset __maybe_unused); | ||
291 | int perf_hist_config(const char *var, const char *value); | ||
292 | |||
253 | #endif /* __PERF_HIST_H */ | 293 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index bb162e40c76c..01ffd12dc791 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -4,6 +4,9 @@ | |||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <linux/bitops.h> | 5 | #include <linux/bitops.h> |
6 | 6 | ||
7 | #define DECLARE_BITMAP(name,bits) \ | ||
8 | unsigned long name[BITS_TO_LONGS(bits)] | ||
9 | |||
7 | int __bitmap_weight(const unsigned long *bitmap, int bits); | 10 | int __bitmap_weight(const unsigned long *bitmap, int bits); |
8 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | 11 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, |
9 | const unsigned long *bitmap2, int bits); | 12 | const unsigned long *bitmap2, int bits); |
diff --git a/tools/perf/util/include/linux/export.h b/tools/perf/util/include/linux/export.h deleted file mode 100644 index b43e2dc21e04..000000000000 --- a/tools/perf/util/include/linux/export.h +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | #ifndef PERF_LINUX_MODULE_H | ||
2 | #define PERF_LINUX_MODULE_H | ||
3 | |||
4 | #define EXPORT_SYMBOL(name) | ||
5 | |||
6 | #endif | ||
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index bfe0a2afd0d2..76ddbc726343 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | #include <linux/types.h> | ||
2 | 3 | ||
3 | #include "../../../../include/linux/list.h" | 4 | #include "../../../../include/linux/list.h" |
4 | 5 | ||
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h deleted file mode 100644 index eb464786c084..000000000000 --- a/tools/perf/util/include/linux/types.h +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | #ifndef _PERF_LINUX_TYPES_H_ | ||
2 | #define _PERF_LINUX_TYPES_H_ | ||
3 | |||
4 | #include <asm/types.h> | ||
5 | |||
6 | #ifndef __bitwise | ||
7 | #define __bitwise | ||
8 | #endif | ||
9 | |||
10 | #ifndef __le32 | ||
11 | typedef __u32 __bitwise __le32; | ||
12 | #endif | ||
13 | |||
14 | #define DECLARE_BITMAP(name,bits) \ | ||
15 | unsigned long name[BITS_TO_LONGS(bits)] | ||
16 | |||
17 | struct list_head { | ||
18 | struct list_head *next, *prev; | ||
19 | }; | ||
20 | |||
21 | struct hlist_head { | ||
22 | struct hlist_node *first; | ||
23 | }; | ||
24 | |||
25 | struct hlist_node { | ||
26 | struct hlist_node *next, **pprev; | ||
27 | }; | ||
28 | |||
29 | #endif | ||
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 27c2a5efe450..7409ac8de51c 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -316,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine, | |||
316 | rb_link_node(&th->rb_node, parent, p); | 316 | rb_link_node(&th->rb_node, parent, p); |
317 | rb_insert_color(&th->rb_node, &machine->threads); | 317 | rb_insert_color(&th->rb_node, &machine->threads); |
318 | machine->last_match = th; | 318 | machine->last_match = th; |
319 | |||
320 | /* | ||
321 | * We have to initialize map_groups separately | ||
322 | * after rb tree is updated. | ||
323 | * | ||
324 | * The reason is that we call machine__findnew_thread | ||
325 | * within thread__init_map_groups to find the thread | ||
326 | * leader and that would screwed the rb tree. | ||
327 | */ | ||
328 | if (thread__init_map_groups(th, machine)) | ||
329 | return NULL; | ||
319 | } | 330 | } |
320 | 331 | ||
321 | return th; | 332 | return th; |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 39cd2d0faff6..8ccbb32eda25 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -32,6 +32,93 @@ static inline int is_no_dso_memory(const char *filename) | |||
32 | !strcmp(filename, "[heap]"); | 32 | !strcmp(filename, "[heap]"); |
33 | } | 33 | } |
34 | 34 | ||
35 | static inline int is_android_lib(const char *filename) | ||
36 | { | ||
37 | return !strncmp(filename, "/data/app-lib", 13) || | ||
38 | !strncmp(filename, "/system/lib", 11); | ||
39 | } | ||
40 | |||
41 | static inline bool replace_android_lib(const char *filename, char *newfilename) | ||
42 | { | ||
43 | const char *libname; | ||
44 | char *app_abi; | ||
45 | size_t app_abi_length, new_length; | ||
46 | size_t lib_length = 0; | ||
47 | |||
48 | libname = strrchr(filename, '/'); | ||
49 | if (libname) | ||
50 | lib_length = strlen(libname); | ||
51 | |||
52 | app_abi = getenv("APP_ABI"); | ||
53 | if (!app_abi) | ||
54 | return false; | ||
55 | |||
56 | app_abi_length = strlen(app_abi); | ||
57 | |||
58 | if (!strncmp(filename, "/data/app-lib", 13)) { | ||
59 | char *apk_path; | ||
60 | |||
61 | if (!app_abi_length) | ||
62 | return false; | ||
63 | |||
64 | new_length = 7 + app_abi_length + lib_length; | ||
65 | |||
66 | apk_path = getenv("APK_PATH"); | ||
67 | if (apk_path) { | ||
68 | new_length += strlen(apk_path) + 1; | ||
69 | if (new_length > PATH_MAX) | ||
70 | return false; | ||
71 | snprintf(newfilename, new_length, | ||
72 | "%s/libs/%s/%s", apk_path, app_abi, libname); | ||
73 | } else { | ||
74 | if (new_length > PATH_MAX) | ||
75 | return false; | ||
76 | snprintf(newfilename, new_length, | ||
77 | "libs/%s/%s", app_abi, libname); | ||
78 | } | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | if (!strncmp(filename, "/system/lib/", 11)) { | ||
83 | char *ndk, *app; | ||
84 | const char *arch; | ||
85 | size_t ndk_length; | ||
86 | size_t app_length; | ||
87 | |||
88 | ndk = getenv("NDK_ROOT"); | ||
89 | app = getenv("APP_PLATFORM"); | ||
90 | |||
91 | if (!(ndk && app)) | ||
92 | return false; | ||
93 | |||
94 | ndk_length = strlen(ndk); | ||
95 | app_length = strlen(app); | ||
96 | |||
97 | if (!(ndk_length && app_length && app_abi_length)) | ||
98 | return false; | ||
99 | |||
100 | arch = !strncmp(app_abi, "arm", 3) ? "arm" : | ||
101 | !strncmp(app_abi, "mips", 4) ? "mips" : | ||
102 | !strncmp(app_abi, "x86", 3) ? "x86" : NULL; | ||
103 | |||
104 | if (!arch) | ||
105 | return false; | ||
106 | |||
107 | new_length = 27 + ndk_length + | ||
108 | app_length + lib_length | ||
109 | + strlen(arch); | ||
110 | |||
111 | if (new_length > PATH_MAX) | ||
112 | return false; | ||
113 | snprintf(newfilename, new_length, | ||
114 | "%s/platforms/%s/arch-%s/usr/lib/%s", | ||
115 | ndk, app, arch, libname); | ||
116 | |||
117 | return true; | ||
118 | } | ||
119 | return false; | ||
120 | } | ||
121 | |||
35 | void map__init(struct map *map, enum map_type type, | 122 | void map__init(struct map *map, enum map_type type, |
36 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 123 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
37 | { | 124 | { |
@@ -59,8 +146,9 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
59 | if (map != NULL) { | 146 | if (map != NULL) { |
60 | char newfilename[PATH_MAX]; | 147 | char newfilename[PATH_MAX]; |
61 | struct dso *dso; | 148 | struct dso *dso; |
62 | int anon, no_dso, vdso; | 149 | int anon, no_dso, vdso, android; |
63 | 150 | ||
151 | android = is_android_lib(filename); | ||
64 | anon = is_anon_memory(filename); | 152 | anon = is_anon_memory(filename); |
65 | vdso = is_vdso_map(filename); | 153 | vdso = is_vdso_map(filename); |
66 | no_dso = is_no_dso_memory(filename); | 154 | no_dso = is_no_dso_memory(filename); |
@@ -75,6 +163,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
75 | filename = newfilename; | 163 | filename = newfilename; |
76 | } | 164 | } |
77 | 165 | ||
166 | if (android) { | ||
167 | if (replace_android_lib(filename, newfilename)) | ||
168 | filename = newfilename; | ||
169 | } | ||
170 | |||
78 | if (vdso) { | 171 | if (vdso) { |
79 | pgoff = 0; | 172 | pgoff = 0; |
80 | dso = vdso__dso_findnew(dsos__list); | 173 | dso = vdso__dso_findnew(dsos__list); |
@@ -323,6 +416,7 @@ void map_groups__init(struct map_groups *mg) | |||
323 | INIT_LIST_HEAD(&mg->removed_maps[i]); | 416 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
324 | } | 417 | } |
325 | mg->machine = NULL; | 418 | mg->machine = NULL; |
419 | mg->refcnt = 1; | ||
326 | } | 420 | } |
327 | 421 | ||
328 | static void maps__delete(struct rb_root *maps) | 422 | static void maps__delete(struct rb_root *maps) |
@@ -358,6 +452,28 @@ void map_groups__exit(struct map_groups *mg) | |||
358 | } | 452 | } |
359 | } | 453 | } |
360 | 454 | ||
455 | struct map_groups *map_groups__new(void) | ||
456 | { | ||
457 | struct map_groups *mg = malloc(sizeof(*mg)); | ||
458 | |||
459 | if (mg != NULL) | ||
460 | map_groups__init(mg); | ||
461 | |||
462 | return mg; | ||
463 | } | ||
464 | |||
465 | void map_groups__delete(struct map_groups *mg) | ||
466 | { | ||
467 | map_groups__exit(mg); | ||
468 | free(mg); | ||
469 | } | ||
470 | |||
471 | void map_groups__put(struct map_groups *mg) | ||
472 | { | ||
473 | if (--mg->refcnt == 0) | ||
474 | map_groups__delete(mg); | ||
475 | } | ||
476 | |||
361 | void map_groups__flush(struct map_groups *mg) | 477 | void map_groups__flush(struct map_groups *mg) |
362 | { | 478 | { |
363 | int type; | 479 | int type; |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f00f058afb3b..ae2d45110588 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -6,7 +6,7 @@ | |||
6 | #include <linux/rbtree.h> | 6 | #include <linux/rbtree.h> |
7 | #include <stdio.h> | 7 | #include <stdio.h> |
8 | #include <stdbool.h> | 8 | #include <stdbool.h> |
9 | #include "types.h" | 9 | #include <linux/types.h> |
10 | 10 | ||
11 | enum map_type { | 11 | enum map_type { |
12 | MAP__FUNCTION = 0, | 12 | MAP__FUNCTION = 0, |
@@ -59,8 +59,20 @@ struct map_groups { | |||
59 | struct rb_root maps[MAP__NR_TYPES]; | 59 | struct rb_root maps[MAP__NR_TYPES]; |
60 | struct list_head removed_maps[MAP__NR_TYPES]; | 60 | struct list_head removed_maps[MAP__NR_TYPES]; |
61 | struct machine *machine; | 61 | struct machine *machine; |
62 | int refcnt; | ||
62 | }; | 63 | }; |
63 | 64 | ||
65 | struct map_groups *map_groups__new(void); | ||
66 | void map_groups__delete(struct map_groups *mg); | ||
67 | |||
68 | static inline struct map_groups *map_groups__get(struct map_groups *mg) | ||
69 | { | ||
70 | ++mg->refcnt; | ||
71 | return mg; | ||
72 | } | ||
73 | |||
74 | void map_groups__put(struct map_groups *mg); | ||
75 | |||
64 | static inline struct kmap *map__kmap(struct map *map) | 76 | static inline struct kmap *map__kmap(struct map *map) |
65 | { | 77 | { |
66 | return (struct kmap *)(map + 1); | 78 | return (struct kmap *)(map + 1); |
diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 3322b8446e89..31ee02d4e988 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c | |||
@@ -57,13 +57,13 @@ void setup_pager(void) | |||
57 | } | 57 | } |
58 | if (!pager) | 58 | if (!pager) |
59 | pager = getenv("PAGER"); | 59 | pager = getenv("PAGER"); |
60 | if (!pager) { | 60 | if (!(pager || access("/usr/bin/pager", X_OK))) |
61 | if (!access("/usr/bin/pager", X_OK)) | 61 | pager = "/usr/bin/pager"; |
62 | pager = "/usr/bin/pager"; | 62 | if (!(pager || access("/usr/bin/less", X_OK))) |
63 | } | 63 | pager = "/usr/bin/less"; |
64 | if (!pager) | 64 | if (!pager) |
65 | pager = "less"; | 65 | pager = "cat"; |
66 | else if (!*pager || !strcmp(pager, "cat")) | 66 | if (!*pager || !strcmp(pager, "cat")) |
67 | return; | 67 | return; |
68 | 68 | ||
69 | spawned_pager = 1; /* means we are emitting to terminal */ | 69 | spawned_pager = 1; /* means we are emitting to terminal */ |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index f1cb4c4b3c70..df094b4ed5ed 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -6,9 +6,8 @@ | |||
6 | 6 | ||
7 | #include <linux/list.h> | 7 | #include <linux/list.h> |
8 | #include <stdbool.h> | 8 | #include <stdbool.h> |
9 | #include "types.h" | 9 | #include <linux/types.h> |
10 | #include <linux/perf_event.h> | 10 | #include <linux/perf_event.h> |
11 | #include "types.h" | ||
12 | 11 | ||
13 | struct list_head; | 12 | struct list_head; |
14 | struct perf_evsel; | 13 | struct perf_evsel; |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 4eb67ec333f1..0bc87ba46bf3 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -9,7 +9,7 @@ | |||
9 | 9 | ||
10 | #include <linux/compiler.h> | 10 | #include <linux/compiler.h> |
11 | #include <linux/list.h> | 11 | #include <linux/list.h> |
12 | #include "types.h" | 12 | #include <linux/types.h> |
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "parse-events.h" | 14 | #include "parse-events.h" |
15 | #include "parse-events-bison.h" | 15 | #include "parse-events-bison.h" |
@@ -299,6 +299,18 @@ PE_PREFIX_MEM PE_VALUE sep_dc | |||
299 | } | 299 | } |
300 | 300 | ||
301 | event_legacy_tracepoint: | 301 | event_legacy_tracepoint: |
302 | PE_NAME '-' PE_NAME ':' PE_NAME | ||
303 | { | ||
304 | struct parse_events_evlist *data = _data; | ||
305 | struct list_head *list; | ||
306 | char sys_name[128]; | ||
307 | snprintf(&sys_name, 128, "%s-%s", $1, $3); | ||
308 | |||
309 | ALLOC_LIST(list); | ||
310 | ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5)); | ||
311 | $$ = list; | ||
312 | } | ||
313 | | | ||
302 | PE_NAME ':' PE_NAME | 314 | PE_NAME ':' PE_NAME |
303 | { | 315 | { |
304 | struct parse_events_evlist *data = _data; | 316 | struct parse_events_evlist *data = _data; |
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index d6e8b6a8d7f3..79c78f74e0cf 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_REGS_H | 1 | #ifndef __PERF_REGS_H |
2 | #define __PERF_REGS_H | 2 | #define __PERF_REGS_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include <linux/types.h> |
5 | #include "event.h" | 5 | #include "event.h" |
6 | 6 | ||
7 | #ifdef HAVE_PERF_REGS_SUPPORT | 7 | #ifdef HAVE_PERF_REGS_SUPPORT |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 00a7dcb2f55c..7a811eb61f75 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -284,17 +284,17 @@ static int pmu_aliases(const char *name, struct list_head *head) | |||
284 | static int pmu_alias_terms(struct perf_pmu_alias *alias, | 284 | static int pmu_alias_terms(struct perf_pmu_alias *alias, |
285 | struct list_head *terms) | 285 | struct list_head *terms) |
286 | { | 286 | { |
287 | struct parse_events_term *term, *clone; | 287 | struct parse_events_term *term, *cloned; |
288 | LIST_HEAD(list); | 288 | LIST_HEAD(list); |
289 | int ret; | 289 | int ret; |
290 | 290 | ||
291 | list_for_each_entry(term, &alias->terms, list) { | 291 | list_for_each_entry(term, &alias->terms, list) { |
292 | ret = parse_events_term__clone(&clone, term); | 292 | ret = parse_events_term__clone(&cloned, term); |
293 | if (ret) { | 293 | if (ret) { |
294 | parse_events__free_terms(&list); | 294 | parse_events__free_terms(&list); |
295 | return ret; | 295 | return ret; |
296 | } | 296 | } |
297 | list_add_tail(&clone->list, &list); | 297 | list_add_tail(&cloned->list, &list); |
298 | } | 298 | } |
299 | list_splice(&list, terms); | 299 | list_splice(&list, terms); |
300 | return 0; | 300 | return 0; |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 8b64125a9281..c14a543ce1f3 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PMU_H | 1 | #ifndef __PMU_H |
2 | #define __PMU_H | 2 | #define __PMU_H |
3 | 3 | ||
4 | #include <linux/bitops.h> | 4 | #include <linux/bitmap.h> |
5 | #include <linux/perf_event.h> | 5 | #include <linux/perf_event.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | 7 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 55960f22233c..64a186edc7be 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1625,13 +1625,14 @@ out_delete_map: | |||
1625 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | 1625 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, |
1626 | bool full) | 1626 | bool full) |
1627 | { | 1627 | { |
1628 | int fd = perf_data_file__fd(session->file); | ||
1629 | struct stat st; | 1628 | struct stat st; |
1630 | int ret; | 1629 | int fd, ret; |
1631 | 1630 | ||
1632 | if (session == NULL || fp == NULL) | 1631 | if (session == NULL || fp == NULL) |
1633 | return; | 1632 | return; |
1634 | 1633 | ||
1634 | fd = perf_data_file__fd(session->file); | ||
1635 | |||
1635 | ret = fstat(fd, &st); | 1636 | ret = fstat(fd, &st); |
1636 | if (ret == -1) | 1637 | if (ret == -1) |
1637 | return; | 1638 | return; |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 635cd8f8b22e..901b9bece2ee 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -2,12 +2,18 @@ | |||
2 | #include "hist.h" | 2 | #include "hist.h" |
3 | #include "comm.h" | 3 | #include "comm.h" |
4 | #include "symbol.h" | 4 | #include "symbol.h" |
5 | #include "evsel.h" | ||
5 | 6 | ||
6 | regex_t parent_regex; | 7 | regex_t parent_regex; |
7 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; | 8 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; |
8 | const char *parent_pattern = default_parent_pattern; | 9 | const char *parent_pattern = default_parent_pattern; |
9 | const char default_sort_order[] = "comm,dso,symbol"; | 10 | const char default_sort_order[] = "comm,dso,symbol"; |
10 | const char *sort_order = default_sort_order; | 11 | const char default_branch_sort_order[] = "comm,dso_from,symbol_from,dso_to,symbol_to"; |
12 | const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; | ||
13 | const char default_top_sort_order[] = "dso,symbol"; | ||
14 | const char default_diff_sort_order[] = "dso,symbol"; | ||
15 | const char *sort_order; | ||
16 | const char *field_order; | ||
11 | regex_t ignore_callees_regex; | 17 | regex_t ignore_callees_regex; |
12 | int have_ignore_callees = 0; | 18 | int have_ignore_callees = 0; |
13 | int sort__need_collapse = 0; | 19 | int sort__need_collapse = 0; |
@@ -16,9 +22,6 @@ int sort__has_sym = 0; | |||
16 | int sort__has_dso = 0; | 22 | int sort__has_dso = 0; |
17 | enum sort_mode sort__mode = SORT_MODE__NORMAL; | 23 | enum sort_mode sort__mode = SORT_MODE__NORMAL; |
18 | 24 | ||
19 | enum sort_type sort__first_dimension; | ||
20 | |||
21 | LIST_HEAD(hist_entry__sort_list); | ||
22 | 25 | ||
23 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | 26 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
24 | { | 27 | { |
@@ -93,6 +96,12 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
93 | return comm__str(right->comm) - comm__str(left->comm); | 96 | return comm__str(right->comm) - comm__str(left->comm); |
94 | } | 97 | } |
95 | 98 | ||
99 | static int64_t | ||
100 | sort__comm_sort(struct hist_entry *left, struct hist_entry *right) | ||
101 | { | ||
102 | return strcmp(comm__str(right->comm), comm__str(left->comm)); | ||
103 | } | ||
104 | |||
96 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, | 105 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, |
97 | size_t size, unsigned int width) | 106 | size_t size, unsigned int width) |
98 | { | 107 | { |
@@ -103,6 +112,7 @@ struct sort_entry sort_comm = { | |||
103 | .se_header = "Command", | 112 | .se_header = "Command", |
104 | .se_cmp = sort__comm_cmp, | 113 | .se_cmp = sort__comm_cmp, |
105 | .se_collapse = sort__comm_collapse, | 114 | .se_collapse = sort__comm_collapse, |
115 | .se_sort = sort__comm_sort, | ||
106 | .se_snprintf = hist_entry__comm_snprintf, | 116 | .se_snprintf = hist_entry__comm_snprintf, |
107 | .se_width_idx = HISTC_COMM, | 117 | .se_width_idx = HISTC_COMM, |
108 | }; | 118 | }; |
@@ -116,7 +126,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | |||
116 | const char *dso_name_l, *dso_name_r; | 126 | const char *dso_name_l, *dso_name_r; |
117 | 127 | ||
118 | if (!dso_l || !dso_r) | 128 | if (!dso_l || !dso_r) |
119 | return cmp_null(dso_l, dso_r); | 129 | return cmp_null(dso_r, dso_l); |
120 | 130 | ||
121 | if (verbose) { | 131 | if (verbose) { |
122 | dso_name_l = dso_l->long_name; | 132 | dso_name_l = dso_l->long_name; |
@@ -132,7 +142,7 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | |||
132 | static int64_t | 142 | static int64_t |
133 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 143 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
134 | { | 144 | { |
135 | return _sort__dso_cmp(left->ms.map, right->ms.map); | 145 | return _sort__dso_cmp(right->ms.map, left->ms.map); |
136 | } | 146 | } |
137 | 147 | ||
138 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, | 148 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, |
@@ -204,6 +214,15 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
204 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); | 214 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); |
205 | } | 215 | } |
206 | 216 | ||
217 | static int64_t | ||
218 | sort__sym_sort(struct hist_entry *left, struct hist_entry *right) | ||
219 | { | ||
220 | if (!left->ms.sym || !right->ms.sym) | ||
221 | return cmp_null(left->ms.sym, right->ms.sym); | ||
222 | |||
223 | return strcmp(right->ms.sym->name, left->ms.sym->name); | ||
224 | } | ||
225 | |||
207 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | 226 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
208 | u64 ip, char level, char *bf, size_t size, | 227 | u64 ip, char level, char *bf, size_t size, |
209 | unsigned int width) | 228 | unsigned int width) |
@@ -250,6 +269,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | |||
250 | struct sort_entry sort_sym = { | 269 | struct sort_entry sort_sym = { |
251 | .se_header = "Symbol", | 270 | .se_header = "Symbol", |
252 | .se_cmp = sort__sym_cmp, | 271 | .se_cmp = sort__sym_cmp, |
272 | .se_sort = sort__sym_sort, | ||
253 | .se_snprintf = hist_entry__sym_snprintf, | 273 | .se_snprintf = hist_entry__sym_snprintf, |
254 | .se_width_idx = HISTC_SYMBOL, | 274 | .se_width_idx = HISTC_SYMBOL, |
255 | }; | 275 | }; |
@@ -277,7 +297,7 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
277 | map__rip_2objdump(map, right->ip)); | 297 | map__rip_2objdump(map, right->ip)); |
278 | } | 298 | } |
279 | } | 299 | } |
280 | return strcmp(left->srcline, right->srcline); | 300 | return strcmp(right->srcline, left->srcline); |
281 | } | 301 | } |
282 | 302 | ||
283 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, | 303 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
@@ -305,7 +325,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
305 | if (!sym_l || !sym_r) | 325 | if (!sym_l || !sym_r) |
306 | return cmp_null(sym_l, sym_r); | 326 | return cmp_null(sym_l, sym_r); |
307 | 327 | ||
308 | return strcmp(sym_l->name, sym_r->name); | 328 | return strcmp(sym_r->name, sym_l->name); |
309 | } | 329 | } |
310 | 330 | ||
311 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, | 331 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, |
@@ -1027,19 +1047,192 @@ static struct sort_dimension memory_sort_dimensions[] = { | |||
1027 | 1047 | ||
1028 | #undef DIM | 1048 | #undef DIM |
1029 | 1049 | ||
1030 | static void __sort_dimension__add(struct sort_dimension *sd, enum sort_type idx) | 1050 | struct hpp_dimension { |
1051 | const char *name; | ||
1052 | struct perf_hpp_fmt *fmt; | ||
1053 | int taken; | ||
1054 | }; | ||
1055 | |||
1056 | #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } | ||
1057 | |||
1058 | static struct hpp_dimension hpp_sort_dimensions[] = { | ||
1059 | DIM(PERF_HPP__OVERHEAD, "overhead"), | ||
1060 | DIM(PERF_HPP__OVERHEAD_SYS, "overhead_sys"), | ||
1061 | DIM(PERF_HPP__OVERHEAD_US, "overhead_us"), | ||
1062 | DIM(PERF_HPP__OVERHEAD_GUEST_SYS, "overhead_guest_sys"), | ||
1063 | DIM(PERF_HPP__OVERHEAD_GUEST_US, "overhead_guest_us"), | ||
1064 | DIM(PERF_HPP__SAMPLES, "sample"), | ||
1065 | DIM(PERF_HPP__PERIOD, "period"), | ||
1066 | }; | ||
1067 | |||
1068 | #undef DIM | ||
1069 | |||
1070 | struct hpp_sort_entry { | ||
1071 | struct perf_hpp_fmt hpp; | ||
1072 | struct sort_entry *se; | ||
1073 | }; | ||
1074 | |||
1075 | bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | ||
1031 | { | 1076 | { |
1032 | if (sd->taken) | 1077 | struct hpp_sort_entry *hse_a; |
1078 | struct hpp_sort_entry *hse_b; | ||
1079 | |||
1080 | if (!perf_hpp__is_sort_entry(a) || !perf_hpp__is_sort_entry(b)) | ||
1081 | return false; | ||
1082 | |||
1083 | hse_a = container_of(a, struct hpp_sort_entry, hpp); | ||
1084 | hse_b = container_of(b, struct hpp_sort_entry, hpp); | ||
1085 | |||
1086 | return hse_a->se == hse_b->se; | ||
1087 | } | ||
1088 | |||
1089 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | ||
1090 | { | ||
1091 | struct hpp_sort_entry *hse; | ||
1092 | |||
1093 | if (!perf_hpp__is_sort_entry(fmt)) | ||
1033 | return; | 1094 | return; |
1034 | 1095 | ||
1096 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1097 | hists__new_col_len(hists, hse->se->se_width_idx, | ||
1098 | strlen(hse->se->se_header)); | ||
1099 | } | ||
1100 | |||
1101 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
1102 | struct perf_evsel *evsel) | ||
1103 | { | ||
1104 | struct hpp_sort_entry *hse; | ||
1105 | size_t len; | ||
1106 | |||
1107 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1108 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
1109 | |||
1110 | return scnprintf(hpp->buf, hpp->size, "%*s", len, hse->se->se_header); | ||
1111 | } | ||
1112 | |||
1113 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | ||
1114 | struct perf_hpp *hpp __maybe_unused, | ||
1115 | struct perf_evsel *evsel) | ||
1116 | { | ||
1117 | struct hpp_sort_entry *hse; | ||
1118 | |||
1119 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1120 | |||
1121 | return hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
1122 | } | ||
1123 | |||
1124 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
1125 | struct hist_entry *he) | ||
1126 | { | ||
1127 | struct hpp_sort_entry *hse; | ||
1128 | size_t len; | ||
1129 | |||
1130 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1131 | len = hists__col_len(he->hists, hse->se->se_width_idx); | ||
1132 | |||
1133 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); | ||
1134 | } | ||
1135 | |||
1136 | static struct hpp_sort_entry * | ||
1137 | __sort_dimension__alloc_hpp(struct sort_dimension *sd) | ||
1138 | { | ||
1139 | struct hpp_sort_entry *hse; | ||
1140 | |||
1141 | hse = malloc(sizeof(*hse)); | ||
1142 | if (hse == NULL) { | ||
1143 | pr_err("Memory allocation failed\n"); | ||
1144 | return NULL; | ||
1145 | } | ||
1146 | |||
1147 | hse->se = sd->entry; | ||
1148 | hse->hpp.header = __sort__hpp_header; | ||
1149 | hse->hpp.width = __sort__hpp_width; | ||
1150 | hse->hpp.entry = __sort__hpp_entry; | ||
1151 | hse->hpp.color = NULL; | ||
1152 | |||
1153 | hse->hpp.cmp = sd->entry->se_cmp; | ||
1154 | hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; | ||
1155 | hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; | ||
1156 | |||
1157 | INIT_LIST_HEAD(&hse->hpp.list); | ||
1158 | INIT_LIST_HEAD(&hse->hpp.sort_list); | ||
1159 | |||
1160 | return hse; | ||
1161 | } | ||
1162 | |||
1163 | bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format) | ||
1164 | { | ||
1165 | return format->header == __sort__hpp_header; | ||
1166 | } | ||
1167 | |||
1168 | static int __sort_dimension__add_hpp_sort(struct sort_dimension *sd) | ||
1169 | { | ||
1170 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); | ||
1171 | |||
1172 | if (hse == NULL) | ||
1173 | return -1; | ||
1174 | |||
1175 | perf_hpp__register_sort_field(&hse->hpp); | ||
1176 | return 0; | ||
1177 | } | ||
1178 | |||
1179 | static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) | ||
1180 | { | ||
1181 | struct hpp_sort_entry *hse = __sort_dimension__alloc_hpp(sd); | ||
1182 | |||
1183 | if (hse == NULL) | ||
1184 | return -1; | ||
1185 | |||
1186 | perf_hpp__column_register(&hse->hpp); | ||
1187 | return 0; | ||
1188 | } | ||
1189 | |||
1190 | static int __sort_dimension__add(struct sort_dimension *sd) | ||
1191 | { | ||
1192 | if (sd->taken) | ||
1193 | return 0; | ||
1194 | |||
1195 | if (__sort_dimension__add_hpp_sort(sd) < 0) | ||
1196 | return -1; | ||
1197 | |||
1035 | if (sd->entry->se_collapse) | 1198 | if (sd->entry->se_collapse) |
1036 | sort__need_collapse = 1; | 1199 | sort__need_collapse = 1; |
1037 | 1200 | ||
1038 | if (list_empty(&hist_entry__sort_list)) | 1201 | sd->taken = 1; |
1039 | sort__first_dimension = idx; | 1202 | |
1203 | return 0; | ||
1204 | } | ||
1205 | |||
1206 | static int __hpp_dimension__add(struct hpp_dimension *hd) | ||
1207 | { | ||
1208 | if (!hd->taken) { | ||
1209 | hd->taken = 1; | ||
1210 | |||
1211 | perf_hpp__register_sort_field(hd->fmt); | ||
1212 | } | ||
1213 | return 0; | ||
1214 | } | ||
1215 | |||
1216 | static int __sort_dimension__add_output(struct sort_dimension *sd) | ||
1217 | { | ||
1218 | if (sd->taken) | ||
1219 | return 0; | ||
1220 | |||
1221 | if (__sort_dimension__add_hpp_output(sd) < 0) | ||
1222 | return -1; | ||
1040 | 1223 | ||
1041 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
1042 | sd->taken = 1; | 1224 | sd->taken = 1; |
1225 | return 0; | ||
1226 | } | ||
1227 | |||
1228 | static int __hpp_dimension__add_output(struct hpp_dimension *hd) | ||
1229 | { | ||
1230 | if (!hd->taken) { | ||
1231 | hd->taken = 1; | ||
1232 | |||
1233 | perf_hpp__column_register(hd->fmt); | ||
1234 | } | ||
1235 | return 0; | ||
1043 | } | 1236 | } |
1044 | 1237 | ||
1045 | int sort_dimension__add(const char *tok) | 1238 | int sort_dimension__add(const char *tok) |
@@ -1068,8 +1261,16 @@ int sort_dimension__add(const char *tok) | |||
1068 | sort__has_dso = 1; | 1261 | sort__has_dso = 1; |
1069 | } | 1262 | } |
1070 | 1263 | ||
1071 | __sort_dimension__add(sd, i); | 1264 | return __sort_dimension__add(sd); |
1072 | return 0; | 1265 | } |
1266 | |||
1267 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { | ||
1268 | struct hpp_dimension *hd = &hpp_sort_dimensions[i]; | ||
1269 | |||
1270 | if (strncasecmp(tok, hd->name, strlen(tok))) | ||
1271 | continue; | ||
1272 | |||
1273 | return __hpp_dimension__add(hd); | ||
1073 | } | 1274 | } |
1074 | 1275 | ||
1075 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { | 1276 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { |
@@ -1084,7 +1285,7 @@ int sort_dimension__add(const char *tok) | |||
1084 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) | 1285 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) |
1085 | sort__has_sym = 1; | 1286 | sort__has_sym = 1; |
1086 | 1287 | ||
1087 | __sort_dimension__add(sd, i + __SORT_BRANCH_STACK); | 1288 | __sort_dimension__add(sd); |
1088 | return 0; | 1289 | return 0; |
1089 | } | 1290 | } |
1090 | 1291 | ||
@@ -1100,18 +1301,47 @@ int sort_dimension__add(const char *tok) | |||
1100 | if (sd->entry == &sort_mem_daddr_sym) | 1301 | if (sd->entry == &sort_mem_daddr_sym) |
1101 | sort__has_sym = 1; | 1302 | sort__has_sym = 1; |
1102 | 1303 | ||
1103 | __sort_dimension__add(sd, i + __SORT_MEMORY_MODE); | 1304 | __sort_dimension__add(sd); |
1104 | return 0; | 1305 | return 0; |
1105 | } | 1306 | } |
1106 | 1307 | ||
1107 | return -ESRCH; | 1308 | return -ESRCH; |
1108 | } | 1309 | } |
1109 | 1310 | ||
1110 | int setup_sorting(void) | 1311 | static const char *get_default_sort_order(void) |
1111 | { | 1312 | { |
1112 | char *tmp, *tok, *str = strdup(sort_order); | 1313 | const char *default_sort_orders[] = { |
1314 | default_sort_order, | ||
1315 | default_branch_sort_order, | ||
1316 | default_mem_sort_order, | ||
1317 | default_top_sort_order, | ||
1318 | default_diff_sort_order, | ||
1319 | }; | ||
1320 | |||
1321 | BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); | ||
1322 | |||
1323 | return default_sort_orders[sort__mode]; | ||
1324 | } | ||
1325 | |||
1326 | static int __setup_sorting(void) | ||
1327 | { | ||
1328 | char *tmp, *tok, *str; | ||
1329 | const char *sort_keys = sort_order; | ||
1113 | int ret = 0; | 1330 | int ret = 0; |
1114 | 1331 | ||
1332 | if (sort_keys == NULL) { | ||
1333 | if (field_order) { | ||
1334 | /* | ||
1335 | * If user specified field order but no sort order, | ||
1336 | * we'll honor it and not add default sort orders. | ||
1337 | */ | ||
1338 | return 0; | ||
1339 | } | ||
1340 | |||
1341 | sort_keys = get_default_sort_order(); | ||
1342 | } | ||
1343 | |||
1344 | str = strdup(sort_keys); | ||
1115 | if (str == NULL) { | 1345 | if (str == NULL) { |
1116 | error("Not enough memory to setup sort keys"); | 1346 | error("Not enough memory to setup sort keys"); |
1117 | return -ENOMEM; | 1347 | return -ENOMEM; |
@@ -1133,6 +1363,17 @@ int setup_sorting(void) | |||
1133 | return ret; | 1363 | return ret; |
1134 | } | 1364 | } |
1135 | 1365 | ||
1366 | bool perf_hpp__should_skip(struct perf_hpp_fmt *format) | ||
1367 | { | ||
1368 | if (perf_hpp__is_sort_entry(format)) { | ||
1369 | struct hpp_sort_entry *hse; | ||
1370 | |||
1371 | hse = container_of(format, struct hpp_sort_entry, hpp); | ||
1372 | return hse->se->elide; | ||
1373 | } | ||
1374 | return false; | ||
1375 | } | ||
1376 | |||
1136 | static void sort_entry__setup_elide(struct sort_entry *se, | 1377 | static void sort_entry__setup_elide(struct sort_entry *se, |
1137 | struct strlist *list, | 1378 | struct strlist *list, |
1138 | const char *list_name, FILE *fp) | 1379 | const char *list_name, FILE *fp) |
@@ -1147,7 +1388,8 @@ static void sort_entry__setup_elide(struct sort_entry *se, | |||
1147 | 1388 | ||
1148 | void sort__setup_elide(FILE *output) | 1389 | void sort__setup_elide(FILE *output) |
1149 | { | 1390 | { |
1150 | struct sort_entry *se; | 1391 | struct perf_hpp_fmt *fmt; |
1392 | struct hpp_sort_entry *hse; | ||
1151 | 1393 | ||
1152 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, | 1394 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, |
1153 | "dso", output); | 1395 | "dso", output); |
@@ -1188,11 +1430,157 @@ void sort__setup_elide(FILE *output) | |||
1188 | * It makes no sense to elide all of sort entries. | 1430 | * It makes no sense to elide all of sort entries. |
1189 | * Just revert them to show up again. | 1431 | * Just revert them to show up again. |
1190 | */ | 1432 | */ |
1191 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1433 | perf_hpp__for_each_format(fmt) { |
1192 | if (!se->elide) | 1434 | if (!perf_hpp__is_sort_entry(fmt)) |
1435 | continue; | ||
1436 | |||
1437 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1438 | if (!hse->se->elide) | ||
1193 | return; | 1439 | return; |
1194 | } | 1440 | } |
1195 | 1441 | ||
1196 | list_for_each_entry(se, &hist_entry__sort_list, list) | 1442 | perf_hpp__for_each_format(fmt) { |
1197 | se->elide = false; | 1443 | if (!perf_hpp__is_sort_entry(fmt)) |
1444 | continue; | ||
1445 | |||
1446 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
1447 | hse->se->elide = false; | ||
1448 | } | ||
1449 | } | ||
1450 | |||
1451 | static int output_field_add(char *tok) | ||
1452 | { | ||
1453 | unsigned int i; | ||
1454 | |||
1455 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { | ||
1456 | struct sort_dimension *sd = &common_sort_dimensions[i]; | ||
1457 | |||
1458 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
1459 | continue; | ||
1460 | |||
1461 | return __sort_dimension__add_output(sd); | ||
1462 | } | ||
1463 | |||
1464 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { | ||
1465 | struct hpp_dimension *hd = &hpp_sort_dimensions[i]; | ||
1466 | |||
1467 | if (strncasecmp(tok, hd->name, strlen(tok))) | ||
1468 | continue; | ||
1469 | |||
1470 | return __hpp_dimension__add_output(hd); | ||
1471 | } | ||
1472 | |||
1473 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { | ||
1474 | struct sort_dimension *sd = &bstack_sort_dimensions[i]; | ||
1475 | |||
1476 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
1477 | continue; | ||
1478 | |||
1479 | return __sort_dimension__add_output(sd); | ||
1480 | } | ||
1481 | |||
1482 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) { | ||
1483 | struct sort_dimension *sd = &memory_sort_dimensions[i]; | ||
1484 | |||
1485 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
1486 | continue; | ||
1487 | |||
1488 | return __sort_dimension__add_output(sd); | ||
1489 | } | ||
1490 | |||
1491 | return -ESRCH; | ||
1492 | } | ||
1493 | |||
1494 | static void reset_dimensions(void) | ||
1495 | { | ||
1496 | unsigned int i; | ||
1497 | |||
1498 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) | ||
1499 | common_sort_dimensions[i].taken = 0; | ||
1500 | |||
1501 | for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) | ||
1502 | hpp_sort_dimensions[i].taken = 0; | ||
1503 | |||
1504 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) | ||
1505 | bstack_sort_dimensions[i].taken = 0; | ||
1506 | |||
1507 | for (i = 0; i < ARRAY_SIZE(memory_sort_dimensions); i++) | ||
1508 | memory_sort_dimensions[i].taken = 0; | ||
1509 | } | ||
1510 | |||
1511 | static int __setup_output_field(void) | ||
1512 | { | ||
1513 | char *tmp, *tok, *str; | ||
1514 | int ret = 0; | ||
1515 | |||
1516 | if (field_order == NULL) | ||
1517 | return 0; | ||
1518 | |||
1519 | reset_dimensions(); | ||
1520 | |||
1521 | str = strdup(field_order); | ||
1522 | if (str == NULL) { | ||
1523 | error("Not enough memory to setup output fields"); | ||
1524 | return -ENOMEM; | ||
1525 | } | ||
1526 | |||
1527 | for (tok = strtok_r(str, ", ", &tmp); | ||
1528 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
1529 | ret = output_field_add(tok); | ||
1530 | if (ret == -EINVAL) { | ||
1531 | error("Invalid --fields key: `%s'", tok); | ||
1532 | break; | ||
1533 | } else if (ret == -ESRCH) { | ||
1534 | error("Unknown --fields key: `%s'", tok); | ||
1535 | break; | ||
1536 | } | ||
1537 | } | ||
1538 | |||
1539 | free(str); | ||
1540 | return ret; | ||
1541 | } | ||
1542 | |||
1543 | int setup_sorting(void) | ||
1544 | { | ||
1545 | int err; | ||
1546 | |||
1547 | err = __setup_sorting(); | ||
1548 | if (err < 0) | ||
1549 | return err; | ||
1550 | |||
1551 | if (parent_pattern != default_parent_pattern) { | ||
1552 | err = sort_dimension__add("parent"); | ||
1553 | if (err < 0) | ||
1554 | return err; | ||
1555 | } | ||
1556 | |||
1557 | reset_dimensions(); | ||
1558 | |||
1559 | /* | ||
1560 | * perf diff doesn't use default hpp output fields. | ||
1561 | */ | ||
1562 | if (sort__mode != SORT_MODE__DIFF) | ||
1563 | perf_hpp__init(); | ||
1564 | |||
1565 | err = __setup_output_field(); | ||
1566 | if (err < 0) | ||
1567 | return err; | ||
1568 | |||
1569 | /* copy sort keys to output fields */ | ||
1570 | perf_hpp__setup_output_field(); | ||
1571 | /* and then copy output fields to sort keys */ | ||
1572 | perf_hpp__append_sort_keys(); | ||
1573 | |||
1574 | return 0; | ||
1575 | } | ||
1576 | |||
1577 | void reset_output_field(void) | ||
1578 | { | ||
1579 | sort__need_collapse = 0; | ||
1580 | sort__has_parent = 0; | ||
1581 | sort__has_sym = 0; | ||
1582 | sort__has_dso = 0; | ||
1583 | |||
1584 | reset_dimensions(); | ||
1585 | perf_hpp__reset_output_field(); | ||
1198 | } | 1586 | } |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 43e5ff42a609..5f38d925e92f 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | extern regex_t parent_regex; | 26 | extern regex_t parent_regex; |
27 | extern const char *sort_order; | 27 | extern const char *sort_order; |
28 | extern const char *field_order; | ||
28 | extern const char default_parent_pattern[]; | 29 | extern const char default_parent_pattern[]; |
29 | extern const char *parent_pattern; | 30 | extern const char *parent_pattern; |
30 | extern const char default_sort_order[]; | 31 | extern const char default_sort_order[]; |
@@ -133,6 +134,8 @@ enum sort_mode { | |||
133 | SORT_MODE__NORMAL, | 134 | SORT_MODE__NORMAL, |
134 | SORT_MODE__BRANCH, | 135 | SORT_MODE__BRANCH, |
135 | SORT_MODE__MEMORY, | 136 | SORT_MODE__MEMORY, |
137 | SORT_MODE__TOP, | ||
138 | SORT_MODE__DIFF, | ||
136 | }; | 139 | }; |
137 | 140 | ||
138 | enum sort_type { | 141 | enum sort_type { |
@@ -179,6 +182,7 @@ struct sort_entry { | |||
179 | 182 | ||
180 | int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); | 183 | int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); |
181 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); | 184 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); |
185 | int64_t (*se_sort)(struct hist_entry *, struct hist_entry *); | ||
182 | int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, | 186 | int (*se_snprintf)(struct hist_entry *he, char *bf, size_t size, |
183 | unsigned int width); | 187 | unsigned int width); |
184 | u8 se_width_idx; | 188 | u8 se_width_idx; |
@@ -189,6 +193,8 @@ extern struct sort_entry sort_thread; | |||
189 | extern struct list_head hist_entry__sort_list; | 193 | extern struct list_head hist_entry__sort_list; |
190 | 194 | ||
191 | int setup_sorting(void); | 195 | int setup_sorting(void); |
196 | int setup_output_field(void); | ||
197 | void reset_output_field(void); | ||
192 | extern int sort_dimension__add(const char *); | 198 | extern int sort_dimension__add(const char *); |
193 | void sort__setup_elide(FILE *fp); | 199 | void sort__setup_elide(FILE *fp); |
194 | 200 | ||
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index ae8ccd7227cf..5667fc3e39cf 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_STATS_H | 1 | #ifndef __PERF_STATS_H |
2 | #define __PERF_STATS_H | 2 | #define __PERF_STATS_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | struct stats | 6 | struct stats |
7 | { | 7 | { |
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 43262b83c541..6a0a13d07a28 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c | |||
@@ -17,7 +17,7 @@ | |||
17 | #include <stdlib.h> | 17 | #include <stdlib.h> |
18 | #include <unistd.h> | 18 | #include <unistd.h> |
19 | #include <string.h> | 19 | #include <string.h> |
20 | #include <linux/bitops.h> | 20 | #include <linux/bitmap.h> |
21 | 21 | ||
22 | #include "perf.h" | 22 | #include "perf.h" |
23 | #include "svghelper.h" | 23 | #include "svghelper.h" |
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index f7b4d6e699ea..e3aff5332e30 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_SVGHELPER_H | 1 | #ifndef __PERF_SVGHELPER_H |
2 | #define __PERF_SVGHELPER_H | 2 | #define __PERF_SVGHELPER_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); | 6 | extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); |
7 | extern void svg_box(int Yslot, u64 start, u64 end, const char *type); | 7 | extern void svg_box(int Yslot, u64 start, u64 end, const char *type); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 501e4e722e8e..33ede53fa6b9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <byteswap.h> | 12 | #include <byteswap.h> |
13 | #include <libgen.h> | 13 | #include <libgen.h> |
14 | #include "build-id.h" | 14 | #include "build-id.h" |
15 | #include "event.h" | ||
15 | 16 | ||
16 | #ifdef HAVE_LIBELF_SUPPORT | 17 | #ifdef HAVE_LIBELF_SUPPORT |
17 | #include <libelf.h> | 18 | #include <libelf.h> |
@@ -115,7 +116,8 @@ struct symbol_conf { | |||
115 | annotate_asm_raw, | 116 | annotate_asm_raw, |
116 | annotate_src, | 117 | annotate_src, |
117 | event_group, | 118 | event_group, |
118 | demangle; | 119 | demangle, |
120 | filter_relative; | ||
119 | const char *vmlinux_name, | 121 | const char *vmlinux_name, |
120 | *kallsyms_name, | 122 | *kallsyms_name, |
121 | *source_prefix, | 123 | *source_prefix, |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 3ce0498bdae6..2fde0d5e40b5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -8,6 +8,22 @@ | |||
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | #include "comm.h" | 9 | #include "comm.h" |
10 | 10 | ||
11 | int thread__init_map_groups(struct thread *thread, struct machine *machine) | ||
12 | { | ||
13 | struct thread *leader; | ||
14 | pid_t pid = thread->pid_; | ||
15 | |||
16 | if (pid == thread->tid) { | ||
17 | thread->mg = map_groups__new(); | ||
18 | } else { | ||
19 | leader = machine__findnew_thread(machine, pid, pid); | ||
20 | if (leader) | ||
21 | thread->mg = map_groups__get(leader->mg); | ||
22 | } | ||
23 | |||
24 | return thread->mg ? 0 : -1; | ||
25 | } | ||
26 | |||
11 | struct thread *thread__new(pid_t pid, pid_t tid) | 27 | struct thread *thread__new(pid_t pid, pid_t tid) |
12 | { | 28 | { |
13 | char *comm_str; | 29 | char *comm_str; |
@@ -15,7 +31,6 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
15 | struct thread *thread = zalloc(sizeof(*thread)); | 31 | struct thread *thread = zalloc(sizeof(*thread)); |
16 | 32 | ||
17 | if (thread != NULL) { | 33 | if (thread != NULL) { |
18 | map_groups__init(&thread->mg); | ||
19 | thread->pid_ = pid; | 34 | thread->pid_ = pid; |
20 | thread->tid = tid; | 35 | thread->tid = tid; |
21 | thread->ppid = -1; | 36 | thread->ppid = -1; |
@@ -45,7 +60,8 @@ void thread__delete(struct thread *thread) | |||
45 | { | 60 | { |
46 | struct comm *comm, *tmp; | 61 | struct comm *comm, *tmp; |
47 | 62 | ||
48 | map_groups__exit(&thread->mg); | 63 | map_groups__put(thread->mg); |
64 | thread->mg = NULL; | ||
49 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { | 65 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { |
50 | list_del(&comm->list); | 66 | list_del(&comm->list); |
51 | comm__free(comm); | 67 | comm__free(comm); |
@@ -111,18 +127,35 @@ int thread__comm_len(struct thread *thread) | |||
111 | size_t thread__fprintf(struct thread *thread, FILE *fp) | 127 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
112 | { | 128 | { |
113 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + | 129 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + |
114 | map_groups__fprintf(&thread->mg, verbose, fp); | 130 | map_groups__fprintf(thread->mg, verbose, fp); |
115 | } | 131 | } |
116 | 132 | ||
117 | void thread__insert_map(struct thread *thread, struct map *map) | 133 | void thread__insert_map(struct thread *thread, struct map *map) |
118 | { | 134 | { |
119 | map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); | 135 | map_groups__fixup_overlappings(thread->mg, map, verbose, stderr); |
120 | map_groups__insert(&thread->mg, map); | 136 | map_groups__insert(thread->mg, map); |
137 | } | ||
138 | |||
139 | static int thread__clone_map_groups(struct thread *thread, | ||
140 | struct thread *parent) | ||
141 | { | ||
142 | int i; | ||
143 | |||
144 | /* This is new thread, we share map groups for process. */ | ||
145 | if (thread->pid_ == parent->pid_) | ||
146 | return 0; | ||
147 | |||
148 | /* But this one is new process, copy maps. */ | ||
149 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
150 | if (map_groups__clone(thread->mg, parent->mg, i) < 0) | ||
151 | return -ENOMEM; | ||
152 | |||
153 | return 0; | ||
121 | } | 154 | } |
122 | 155 | ||
123 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | 156 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) |
124 | { | 157 | { |
125 | int i, err; | 158 | int err; |
126 | 159 | ||
127 | if (parent->comm_set) { | 160 | if (parent->comm_set) { |
128 | const char *comm = thread__comm_str(parent); | 161 | const char *comm = thread__comm_str(parent); |
@@ -134,13 +167,8 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | |||
134 | thread->comm_set = true; | 167 | thread->comm_set = true; |
135 | } | 168 | } |
136 | 169 | ||
137 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
138 | if (map_groups__clone(&thread->mg, &parent->mg, i) < 0) | ||
139 | return -ENOMEM; | ||
140 | |||
141 | thread->ppid = parent->tid; | 170 | thread->ppid = parent->tid; |
142 | 171 | return thread__clone_map_groups(thread, parent); | |
143 | return 0; | ||
144 | } | 172 | } |
145 | 173 | ||
146 | void thread__find_cpumode_addr_location(struct thread *thread, | 174 | void thread__find_cpumode_addr_location(struct thread *thread, |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9b29f085aede..3c0c2724f82c 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -13,7 +13,7 @@ struct thread { | |||
13 | struct rb_node rb_node; | 13 | struct rb_node rb_node; |
14 | struct list_head node; | 14 | struct list_head node; |
15 | }; | 15 | }; |
16 | struct map_groups mg; | 16 | struct map_groups *mg; |
17 | pid_t pid_; /* Not all tools update this */ | 17 | pid_t pid_; /* Not all tools update this */ |
18 | pid_t tid; | 18 | pid_t tid; |
19 | pid_t ppid; | 19 | pid_t ppid; |
@@ -30,6 +30,7 @@ struct machine; | |||
30 | struct comm; | 30 | struct comm; |
31 | 31 | ||
32 | struct thread *thread__new(pid_t pid, pid_t tid); | 32 | struct thread *thread__new(pid_t pid, pid_t tid); |
33 | int thread__init_map_groups(struct thread *thread, struct machine *machine); | ||
33 | void thread__delete(struct thread *thread); | 34 | void thread__delete(struct thread *thread); |
34 | static inline void thread__exited(struct thread *thread) | 35 | static inline void thread__exited(struct thread *thread) |
35 | { | 36 | { |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index dab14d0ad3d0..f92c37abb0a8 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #define __PERF_TOP_H 1 | 2 | #define __PERF_TOP_H 1 |
3 | 3 | ||
4 | #include "tool.h" | 4 | #include "tool.h" |
5 | #include "types.h" | 5 | #include <linux/types.h> |
6 | #include <stddef.h> | 6 | #include <stddef.h> |
7 | #include <stdbool.h> | 7 | #include <stdbool.h> |
8 | #include <termios.h> | 8 | #include <termios.h> |
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h deleted file mode 100644 index c51fa6b70a28..000000000000 --- a/tools/perf/util/types.h +++ /dev/null | |||
@@ -1,24 +0,0 @@ | |||
1 | #ifndef __PERF_TYPES_H | ||
2 | #define __PERF_TYPES_H | ||
3 | |||
4 | #include <stdint.h> | ||
5 | |||
6 | /* | ||
7 | * We define u64 as uint64_t for every architecture | ||
8 | * so that we can print it with "%"PRIx64 without getting warnings. | ||
9 | */ | ||
10 | typedef uint64_t u64; | ||
11 | typedef int64_t s64; | ||
12 | typedef unsigned int u32; | ||
13 | typedef signed int s32; | ||
14 | typedef unsigned short u16; | ||
15 | typedef signed short s16; | ||
16 | typedef unsigned char u8; | ||
17 | typedef signed char s8; | ||
18 | |||
19 | union u64_swap { | ||
20 | u64 val64; | ||
21 | u32 val32[2]; | ||
22 | }; | ||
23 | |||
24 | #endif /* __PERF_TYPES_H */ | ||
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 67db73ec3dab..5ec80a575b50 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "unwind-libdw.h" | 7 | #include "unwind-libdw.h" |
8 | #include "machine.h" | 8 | #include "machine.h" |
9 | #include "thread.h" | 9 | #include "thread.h" |
10 | #include "types.h" | 10 | #include <linux/types.h> |
11 | #include "event.h" | 11 | #include "event.h" |
12 | #include "perf_regs.h" | 12 | #include "perf_regs.h" |
13 | 13 | ||
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index b031316f221a..f03061260b4e 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __UNWIND_H | 1 | #ifndef __UNWIND_H |
2 | #define __UNWIND_H | 2 | #define __UNWIND_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include <linux/types.h> |
5 | #include "event.h" | 5 | #include "event.h" |
6 | #include "symbol.h" | 6 | #include "symbol.h" |
7 | 7 | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 9f66549562bd..7fff6be07f07 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -166,6 +166,8 @@ static ssize_t ion(bool is_read, int fd, void *buf, size_t n) | |||
166 | ssize_t ret = is_read ? read(fd, buf, left) : | 166 | ssize_t ret = is_read ? read(fd, buf, left) : |
167 | write(fd, buf, left); | 167 | write(fd, buf, left); |
168 | 168 | ||
169 | if (ret < 0 && errno == EINTR) | ||
170 | continue; | ||
169 | if (ret <= 0) | 171 | if (ret <= 0) |
170 | return ret; | 172 | return ret; |
171 | 173 | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 6995d66f225c..b03da44e94e4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -69,7 +69,7 @@ | |||
69 | #include <sys/ioctl.h> | 69 | #include <sys/ioctl.h> |
70 | #include <inttypes.h> | 70 | #include <inttypes.h> |
71 | #include <linux/magic.h> | 71 | #include <linux/magic.h> |
72 | #include "types.h" | 72 | #include <linux/types.h> |
73 | #include <sys/ttydefaults.h> | 73 | #include <sys/ttydefaults.h> |
74 | #include <api/fs/debugfs.h> | 74 | #include <api/fs/debugfs.h> |
75 | #include <termios.h> | 75 | #include <termios.h> |
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index 2fa967e1a88a..b21a80c6cf8d 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_VALUES_H | 1 | #ifndef __PERF_VALUES_H |
2 | #define __PERF_VALUES_H | 2 | #define __PERF_VALUES_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | struct perf_read_values { | 6 | struct perf_read_values { |
7 | int threads; | 7 | int threads; |
diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile index 3187c62d9814..9325f4693821 100644 --- a/tools/virtio/Makefile +++ b/tools/virtio/Makefile | |||
@@ -3,7 +3,7 @@ test: virtio_test vringh_test | |||
3 | virtio_test: virtio_ring.o virtio_test.o | 3 | virtio_test: virtio_ring.o virtio_test.o |
4 | vringh_test: vringh_test.o vringh.o virtio_ring.o | 4 | vringh_test: vringh_test.o vringh.o virtio_ring.o |
5 | 5 | ||
6 | CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE | 6 | CFLAGS += -g -O2 -Wall -I. -I../include/ -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -fno-strict-aliasing -fno-common -MMD -U_FORTIFY_SOURCE |
7 | vpath %.c ../../drivers/virtio ../../drivers/vhost | 7 | vpath %.c ../../drivers/virtio ../../drivers/vhost |
8 | mod: | 8 | mod: |
9 | ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test | 9 | ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test |
diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h index fba705963968..1e8ce6979c1e 100644 --- a/tools/virtio/linux/kernel.h +++ b/tools/virtio/linux/kernel.h | |||
@@ -38,13 +38,6 @@ struct page { | |||
38 | 38 | ||
39 | #define __printf(a,b) __attribute__((format(printf,a,b))) | 39 | #define __printf(a,b) __attribute__((format(printf,a,b))) |
40 | 40 | ||
41 | typedef enum { | ||
42 | GFP_KERNEL, | ||
43 | GFP_ATOMIC, | ||
44 | __GFP_HIGHMEM, | ||
45 | __GFP_HIGH | ||
46 | } gfp_t; | ||
47 | |||
48 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | 41 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) |
49 | 42 | ||
50 | extern void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; | 43 | extern void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; |
diff --git a/tools/virtio/linux/types.h b/tools/virtio/linux/types.h deleted file mode 100644 index f8ebb9a2b3d6..000000000000 --- a/tools/virtio/linux/types.h +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
1 | #ifndef TYPES_H | ||
2 | #define TYPES_H | ||
3 | #include <stdint.h> | ||
4 | |||
5 | #define __force | ||
6 | #define __user | ||
7 | #define __must_check | ||
8 | #define __cold | ||
9 | |||
10 | typedef uint64_t u64; | ||
11 | typedef int64_t s64; | ||
12 | typedef uint32_t u32; | ||
13 | typedef int32_t s32; | ||
14 | typedef uint16_t u16; | ||
15 | typedef int16_t s16; | ||
16 | typedef uint8_t u8; | ||
17 | typedef int8_t s8; | ||
18 | |||
19 | typedef uint64_t __u64; | ||
20 | typedef int64_t __s64; | ||
21 | typedef uint32_t __u32; | ||
22 | typedef int32_t __s32; | ||
23 | typedef uint16_t __u16; | ||
24 | typedef int16_t __s16; | ||
25 | typedef uint8_t __u8; | ||
26 | typedef int8_t __s8; | ||
27 | |||
28 | #endif /* TYPES_H */ | ||