aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorDeng-Cheng Zhu <dengcheng.zhu@gmail.com>2010-10-12 07:37:23 -0400
committerRalf Baechle <ralf@linux-mips.org>2010-10-29 14:08:49 -0400
commit7e788d967c092343cca773542565c362991c87c0 (patch)
tree88c3ef91aba02a7dae93ccc674f5bed461c16d0d /arch
parent14f7001284185bffeb796a181664906f7160f593 (diff)
MIPS: Perf-events: Add callchain support
Adds callchain support for MIPS Perf-events. For more info on this feature, please refer to tools/perf/Documentation/perf-report.txt and tools/perf/design.txt. Currently userspace callchain data is not recorded, because we do not have a safe way to do this. Signed-off-by: Deng-Cheng Zhu <dengcheng.zhu@gmail.com> Acked-by: David Daney <ddaney@caviumnetworks.com> To: linux-mips@linux-mips.org Cc: a.p.zijlstra@chello.nl Cc: paulus@samba.org Cc: mingo@elte.hu Cc: acme@redhat.com Cc: jamie.iles@picochip.com Cc: matt@console-pimps.org Patchwork: https://patchwork.linux-mips.org/patch/1690/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/kernel/perf_event.c108
1 files changed, 107 insertions, 1 deletions
diff --git a/arch/mips/kernel/perf_event.c b/arch/mips/kernel/perf_event.c
index eefdf60da2d7..95c833e8dc81 100644
--- a/arch/mips/kernel/perf_event.c
+++ b/arch/mips/kernel/perf_event.c
@@ -6,7 +6,8 @@
6 * 6 *
7 * This code is based on the implementation for ARM, which is in turn 7 * This code is based on the implementation for ARM, which is in turn
8 * based on the sparc64 perf event code and the x86 code. Performance 8 * based on the sparc64 perf event code and the x86 code. Performance
9 * counter access is based on the MIPS Oprofile code. 9 * counter access is based on the MIPS Oprofile code. And the callchain
10 * support references the code of MIPS stacktrace.c.
10 * 11 *
11 * This program is free software; you can redistribute it and/or modify 12 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as 13 * it under the terms of the GNU General Public License version 2 as
@@ -486,3 +487,108 @@ handle_associated_event(struct cpu_hw_events *cpuc,
486 if (perf_event_overflow(event, 0, data, regs)) 487 if (perf_event_overflow(event, 0, data, regs))
487 mipspmu->disable_event(idx); 488 mipspmu->disable_event(idx);
488} 489}
490
491/* Callchain handling code. */
492static inline void
493callchain_store(struct perf_callchain_entry *entry,
494 u64 ip)
495{
496 if (entry->nr < PERF_MAX_STACK_DEPTH)
497 entry->ip[entry->nr++] = ip;
498}
499
500/*
501 * Leave userspace callchain empty for now. When we find a way to trace
502 * the user stack callchains, we add here.
503 */
504static void
505perf_callchain_user(struct pt_regs *regs,
506 struct perf_callchain_entry *entry)
507{
508}
509
510static void save_raw_perf_callchain(struct perf_callchain_entry *entry,
511 unsigned long reg29)
512{
513 unsigned long *sp = (unsigned long *)reg29;
514 unsigned long addr;
515
516 while (!kstack_end(sp)) {
517 addr = *sp++;
518 if (__kernel_text_address(addr)) {
519 callchain_store(entry, addr);
520 if (entry->nr >= PERF_MAX_STACK_DEPTH)
521 break;
522 }
523 }
524}
525
526static void
527perf_callchain_kernel(struct pt_regs *regs,
528 struct perf_callchain_entry *entry)
529{
530 unsigned long sp = regs->regs[29];
531#ifdef CONFIG_KALLSYMS
532 unsigned long ra = regs->regs[31];
533 unsigned long pc = regs->cp0_epc;
534
535 callchain_store(entry, PERF_CONTEXT_KERNEL);
536 if (raw_show_trace || !__kernel_text_address(pc)) {
537 unsigned long stack_page =
538 (unsigned long)task_stack_page(current);
539 if (stack_page && sp >= stack_page &&
540 sp <= stack_page + THREAD_SIZE - 32)
541 save_raw_perf_callchain(entry, sp);
542 return;
543 }
544 do {
545 callchain_store(entry, pc);
546 if (entry->nr >= PERF_MAX_STACK_DEPTH)
547 break;
548 pc = unwind_stack(current, &sp, pc, &ra);
549 } while (pc);
550#else
551 callchain_store(entry, PERF_CONTEXT_KERNEL);
552 save_raw_perf_callchain(entry, sp);
553#endif
554}
555
556static void
557perf_do_callchain(struct pt_regs *regs,
558 struct perf_callchain_entry *entry)
559{
560 int is_user;
561
562 if (!regs)
563 return;
564
565 is_user = user_mode(regs);
566
567 if (!current || !current->pid)
568 return;
569
570 if (is_user && current->state != TASK_RUNNING)
571 return;
572
573 if (!is_user) {
574 perf_callchain_kernel(regs, entry);
575 if (current->mm)
576 regs = task_pt_regs(current);
577 else
578 regs = NULL;
579 }
580 if (regs)
581 perf_callchain_user(regs, entry);
582}
583
584static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_irq_entry);
585
586struct perf_callchain_entry *
587perf_callchain(struct pt_regs *regs)
588{
589 struct perf_callchain_entry *entry = &__get_cpu_var(pmc_irq_entry);
590
591 entry->nr = 0;
592 perf_do_callchain(regs, entry);
593 return entry;
594}