diff options
author | Michael Ellerman <mpe@ellerman.id.au> | 2014-06-10 22:23:10 +1000 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2014-06-11 17:03:58 +1000 |
commit | 3752e453f6bafd78e5586cc2b2e33ee4b6e1566d (patch) | |
tree | 2c5d88b6d9f759844249a36ce8b729dcb5c274ad /tools | |
parent | 33b4819f3b93bbcb934e02cbc64ff3c5e9d0149b (diff) |
selftests/powerpc: Add tests of PMU EBBs
The Power8 Performance Monitor Unit (PMU) has a new feature called Event
Based Branches (EBB). This commit adds tests of the kernel API for using
EBBs.
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'tools')
34 files changed, 3913 insertions, 4 deletions
diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile index 7216f009165..b9ff0db42c7 100644 --- a/tools/testing/selftests/powerpc/pmu/Makefile +++ b/tools/testing/selftests/powerpc/pmu/Makefile @@ -4,7 +4,7 @@ noarg: PROGS := count_instructions EXTRA_SOURCES := ../harness.c event.c -all: $(PROGS) +all: $(PROGS) sub_all $(PROGS): $(EXTRA_SOURCES) @@ -12,12 +12,30 @@ $(PROGS): $(EXTRA_SOURCES) count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES) $(CC) $(CFLAGS) -m64 -o $@ $^ -run_tests: all +run_tests: all sub_run_tests @-for PROG in $(PROGS); do \ ./$$PROG; \ done; -clean: +clean: sub_clean rm -f $(PROGS) loop.o -.PHONY: all run_tests clean + +SUB_TARGETS = ebb + +sub_all: + @for TARGET in $(SUB_TARGETS); do \ + $(MAKE) -C $$TARGET all; \ + done; + +sub_run_tests: all + @for TARGET in $(SUB_TARGETS); do \ + $(MAKE) -C $$TARGET run_tests; \ + done; + +sub_clean: + @for TARGET in $(SUB_TARGETS); do \ + $(MAKE) -C $$TARGET clean; \ + done; + +.PHONY: all run_tests clean sub_all sub_run_tests sub_clean diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile new file mode 100644 index 00000000000..edbba2affc2 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile @@ -0,0 +1,32 @@ +noarg: + $(MAKE) -C ../../ + +# The EBB handler is 64-bit code and everything links against it +CFLAGS += -m64 + +PROGS := reg_access_test event_attributes_test cycles_test \ + cycles_with_freeze_test pmc56_overflow_test \ + ebb_vs_cpu_event_test cpu_event_vs_ebb_test \ + cpu_event_pinned_vs_ebb_test task_event_vs_ebb_test \ + task_event_pinned_vs_ebb_test multi_ebb_procs_test \ + multi_counter_test pmae_handling_test \ + close_clears_pmcc_test instruction_count_test \ + fork_cleanup_test ebb_on_child_test \ + ebb_on_willing_child_test back_to_back_ebbs_test \ + lost_exception_test no_handler_test + +all: $(PROGS) + +$(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c + +instruction_count_test: ../loop.S + +lost_exception_test: ../lib.c + +run_tests: all + @-for PROG in $(PROGS); do \ + ./$$PROG; \ + done; + +clean: + rm -f $(PROGS) diff --git a/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c new file mode 100644 index 00000000000..66ea765c0e7 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/back_to_back_ebbs_test.c @@ -0,0 +1,106 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +#include "ebb.h" + + +#define NUMBER_OF_EBBS 50 + +/* + * Test that if we overflow the counter while in the EBB handler, we take + * another EBB on exiting from the handler. + * + * We do this by counting with a stupidly low sample period, causing us to + * overflow the PMU while we're still in the EBB handler, leading to another + * EBB. + * + * We get out of what would otherwise be an infinite loop by leaving the + * counter frozen once we've taken enough EBBs. + */ + +static void ebb_callee(void) +{ + uint64_t siar, val; + + val = mfspr(SPRN_BESCR); + if (!(val & BESCR_PMEO)) { + ebb_state.stats.spurious++; + goto out; + } + + ebb_state.stats.ebb_count++; + trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count); + + /* Resets the PMC */ + count_pmc(1, sample_period); + +out: + if (ebb_state.stats.ebb_count == NUMBER_OF_EBBS) + /* Reset but leave counters frozen */ + reset_ebb_with_clear_mask(MMCR0_PMAO); + else + /* Unfreezes */ + reset_ebb(); + + /* Do some stuff to chew some cycles and pop the counter */ + siar = mfspr(SPRN_SIAR); + trace_log_reg(ebb_state.trace, SPRN_SIAR, siar); + + val = mfspr(SPRN_PMC1); + trace_log_reg(ebb_state.trace, SPRN_PMC1, val); + + val = mfspr(SPRN_MMCR0); + trace_log_reg(ebb_state.trace, SPRN_MMCR0, val); +} + +int back_to_back_ebbs(void) +{ + struct event event; + + event_init_named(&event, 0x1001e, "cycles"); + event_leader_ebb_init(&event); + + event.attr.exclude_kernel = 1; + event.attr.exclude_hv = 1; + event.attr.exclude_idle = 1; + + FAIL_IF(event_open(&event)); + + setup_ebb_handler(ebb_callee); + + FAIL_IF(ebb_event_enable(&event)); + + sample_period = 5; + + ebb_freeze_pmcs(); + mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); + ebb_global_enable(); + ebb_unfreeze_pmcs(); + + while (ebb_state.stats.ebb_count < NUMBER_OF_EBBS) + FAIL_IF(core_busy_loop()); + + ebb_global_disable(); + ebb_freeze_pmcs(); + + count_pmc(1, sample_period); + + dump_ebb_state(); + + event_close(&event); + + FAIL_IF(ebb_state.stats.ebb_count != NUMBER_OF_EBBS); + + return 0; +} + +int main(void) +{ + return test_harness(back_to_back_ebbs, "back_to_back_ebbs"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c new file mode 100644 index 00000000000..0f0423dba18 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/close_clears_pmcc_test.c @@ -0,0 +1,59 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <signal.h> + +#include "ebb.h" + + +/* + * Test that closing the EBB event clears MMCR0_PMCC, preventing further access + * by userspace to the PMU hardware. + */ + +int close_clears_pmcc(void) +{ + struct event event; + + event_init_named(&event, 0x1001e, "cycles"); + event_leader_ebb_init(&event); + + FAIL_IF(event_open(&event)); + + ebb_enable_pmc_counting(1); + setup_ebb_handler(standard_ebb_callee); + ebb_global_enable(); + FAIL_IF(ebb_event_enable(&event)); + + mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); + + while (ebb_state.stats.ebb_count < 1) + FAIL_IF(core_busy_loop()); + + ebb_global_disable(); + event_close(&event); + + FAIL_IF(ebb_state.stats.ebb_count == 0); + + /* The real test is here, do we take a SIGILL when writing PMU regs now + * that we have closed the event. We expect that we will. */ + + FAIL_IF(catch_sigill(write_pmc1)); + + /* We should still be able to read EBB regs though */ + mfspr(SPRN_EBBHR); + mfspr(SPRN_EBBRR); + mfspr(SPRN_BESCR); + + return 0; +} + +int main(void) +{ + return test_harness(close_clears_pmcc, "close_clears_pmcc"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c new file mode 100644 index 00000000000..d3ed64d5d6c --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c @@ -0,0 +1,93 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "ebb.h" + + +/* + * Tests a pinned cpu event vs an EBB - in that order. The pinned cpu event + * should remain and the EBB event should fail to enable. + */ + +static int setup_cpu_event(struct event *event, int cpu) +{ + event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); + + event->attr.pinned = 1; + + event->attr.exclude_kernel = 1; + event->attr.exclude_hv = 1; + event->attr.exclude_idle = 1; + + SKIP_IF(require_paranoia_below(1)); + FAIL_IF(event_open_with_cpu(event, cpu)); + FAIL_IF(event_enable(event)); + + return 0; +} + +int cpu_event_pinned_vs_ebb(void) +{ + union pipe read_pipe, write_pipe; + struct event event; + int cpu, rc; + pid_t pid; + + cpu = pick_online_cpu(); + FAIL_IF(cpu < 0); + FAIL_IF(bind_to_cpu(cpu)); + + FAIL_IF(pipe(read_pipe.fds) == -1); + FAIL_IF(pipe(write_pipe.fds) == -1); + + pid = fork(); + if (pid == 0) { + /* NB order of pipes looks reversed */ + exit(ebb_child(write_pipe, read_pipe)); + } + + /* We setup the cpu event first */ + rc = setup_cpu_event(&event, cpu); + if (rc) { + kill_child_and_wait(pid); + return rc; + } + + /* Signal the child to install its EBB event and wait */ + if (sync_with_child(read_pipe, write_pipe)) + /* If it fails, wait for it to exit */ + goto wait; + + /* Signal the child to run */ + FAIL_IF(sync_with_child(read_pipe, write_pipe)); + +wait: + /* We expect it to fail to read the event */ + FAIL_IF(wait_for_child(pid) != 2); + + FAIL_IF(event_disable(&event)); + FAIL_IF(event_read(&event)); + + event_report(&event); + + /* The cpu event should have run */ + FAIL_IF(event.result.value == 0); + FAIL_IF(event.result.enabled != event.result.running); + + return 0; +} + +int main(void) +{ + return test_harness(cpu_event_pinned_vs_ebb, "cpu_event_pinned_vs_ebb"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c new file mode 100644 index 00000000000..8b972c2aa39 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c @@ -0,0 +1,89 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "ebb.h" + + +/* + * Tests a cpu event vs an EBB - in that order. The EBB should force the cpu + * event off the PMU. + */ + +static int setup_cpu_event(struct event *event, int cpu) +{ + event_init_named(event, 0x400FA, "PM_RUN_INST_CMPL"); + + event->attr.exclude_kernel = 1; + event->attr.exclude_hv = 1; + event->attr.exclude_idle = 1; + + SKIP_IF(require_paranoia_below(1)); + FAIL_IF(event_open_with_cpu(event, cpu)); + FAIL_IF(event_enable(event)); + + return 0; +} + +int cpu_event_vs_ebb(void) +{ + union pipe read_pipe, write_pipe; + struct event event; + int cpu, rc; + pid_t pid; + + cpu = pick_online_cpu(); + FAIL_IF(cpu < 0); + FAIL_IF(bind_to_cpu(cpu)); + + FAIL_IF(pipe(read_pipe.fds) == -1); + FAIL_IF(pipe(write_pipe.fds) == -1); + + pid = fork(); + if (pid == 0) { + /* NB order of pipes looks reversed */ + exit(ebb_child(write_pipe, read_pipe)); + } + + /* We setup the cpu event first */ + rc = setup_cpu_event(&event, cpu); + if (rc) { + kill_child_and_wait(pid); + return rc; + } + + /* Signal the child to install its EBB event and wait */ + if (sync_with_child(read_pipe, write_pipe)) + /* If it fails, wait for it to exit */ + goto wait; + + /* Signal the child to run */ + FAIL_IF(sync_with_child(read_pipe, write_pipe)); + +wait: + /* We expect the child to succeed */ + FAIL_IF(wait_for_child(pid)); + + FAIL_IF(event_disable(&event)); + FAIL_IF(event_read(&event)); + + event_report(&event); + + /* The cpu event may have run */ + + return 0; +} + +int main(void) +{ + return test_harness(cpu_event_vs_ebb, "cpu_event_vs_ebb"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c new file mode 100644 index 00000000000..8590fc1bfc0 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_test.c @@ -0,0 +1,58 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "ebb.h" + + +/* + * Basic test that counts user cycles and takes EBBs. + */ +int cycles(void) +{ + struct event event; + + event_init_named(&event, 0x1001e, "cycles"); + event_leader_ebb_init(&event); + + event.attr.exclude_kernel = 1; + event.attr.exclude_hv = 1; + event.attr.exclude_idle = 1; + + FAIL_IF(event_open(&event)); + + ebb_enable_pmc_counting(1); + setup_ebb_handler(standard_ebb_callee); + ebb_global_enable(); + FAIL_IF(ebb_event_enable(&event)); + + mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); + + while (ebb_state.stats.ebb_count < 10) { + FAIL_IF(core_busy_loop()); + FAIL_IF(ebb_check_mmcr0()); + } + + ebb_global_disable(); + ebb_freeze_pmcs(); + + count_pmc(1, sample_period); + + dump_ebb_state(); + + event_close(&event); + + FAIL_IF(ebb_state.stats.ebb_count == 0); + FAIL_IF(!ebb_check_count(1, sample_period, 100)); + + return 0; +} + +int main(void) +{ + return test_harness(cycles, "cycles"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c new file mode 100644 index 00000000000..754b3f2008d --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_freeze_test.c @@ -0,0 +1,117 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "ebb.h" + + +/* + * Test of counting cycles while using MMCR0_FC (freeze counters) to only count + * parts of the code. This is complicated by the fact that FC is set by the + * hardware when the event overflows. We may take the EBB after we have set FC, + * so we have to be careful about whether we clear FC at the end of the EBB + * handler or not. + */ + +static bool counters_frozen = false; +static int ebbs_while_frozen = 0; + +static void ebb_callee(void) +{ + uint64_t mask, val; + + mask = MMCR0_PMAO | MMCR0_FC; + + val = mfspr(SPRN_BESCR); + if (!(val & BESCR_PMEO)) { + ebb_state.stats.spurious++; + goto out; + } + + ebb_state.stats.ebb_count++; + trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count); + + val = mfspr(SPRN_MMCR0); + trace_log_reg(ebb_state.trace, SPRN_MMCR0, val); + + if (counters_frozen) { + trace_log_string(ebb_state.trace, "frozen"); + ebbs_while_frozen++; + mask &= ~MMCR0_FC; + } + + count_pmc(1, sample_period); +out: + reset_ebb_with_clear_mask(mask); +} + +int cycles_with_freeze(void) +{ + struct event event; + uint64_t val; + bool fc_cleared; + + event_init_named(&event, 0x1001e, "cycles"); + event_leader_ebb_init(&event); + + event.attr.exclude_kernel = 1; + event.attr.exclude_hv = 1; + event.attr.exclude_idle = 1; + + FAIL_IF(event_open(&event)); + + setup_ebb_handler(ebb_callee); + ebb_global_enable(); + FAIL_IF(ebb_event_enable(&event)); + + mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); + + fc_cleared = false; + + /* Make sure we loop until we take at least one EBB */ + while ((ebb_state.stats.ebb_count < 20 && !fc_cleared) || + ebb_state.stats.ebb_count < 1) + { + counters_frozen = false; + mb(); + mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); + + FAIL_IF(core_busy_loop()); + + counters_frozen = true; + mb(); + mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC); + + val = mfspr(SPRN_MMCR0); + if (! (val & MMCR0_FC)) { + printf("Outside of loop, FC NOT set MMCR0 0x%lx\n", val); + fc_cleared = true; + } + } + + ebb_global_disable(); + ebb_freeze_pmcs(); + + count_pmc(1, sample_period); + + dump_ebb_state(); + + printf("EBBs while frozen %d\n", ebbs_while_frozen); + + event_close(&event); + + FAIL_IF(ebb_state.stats.ebb_count == 0); + FAIL_IF(fc_cleared); + + return 0; +} + +int main(void) +{ + return test_harness(cycles_with_freeze, "cycles_with_freeze"); +} diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c new file mode 100644 index 00000000000..1b46be94b64 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c @@ -0,0 +1,727 @@ +/* + * Copyright 2014, Michael Ellerman, IBM Corp. + * Licensed under GPLv2. + */ + +#define _GNU_SOURCE /* For CPU_ZERO etc. */ + +#include <sched.h> +#include <sys/wait.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> + +#include "trace.h" +#include "reg.h" +#include "ebb.h" + + +void (*ebb_user_func)(void); + +void ebb_hook(void) +{ + if (ebb_user_func) + ebb_user_func(); +} + +struct ebb_state ebb_state; + +u64 sample_period = 0x40000000ull; + +void reset_ebb_with_clear_mask(unsigned long mmcr0_clear_mask) +{ + u64 val; + + /* 2) clear MMCR0[PMAO] - docs say BESCR[PMEO] should do this */ + /* 3) set MMCR0[PMAE] - docs say BESCR[PME] should do this */ + val = mfspr(SPRN_MMCR0); + mtspr(SPRN_MMCR0, (val & ~mmcr0_clear_mask) | MMCR0_PMAE); + + /* 4) clear BESCR[PMEO] */ + mtspr(SPRN_BESCRR, BESCR_PMEO); + + /* 5) set BESCR[PME] */ + mtspr(SPRN_BESCRS, BESCR_PME); + + /* 6) rfebb 1 - done in our caller */ +} + +void reset_ebb(void) +{ + reset_ebb_with_clear_mask(MMCR0_PMAO | MMCR0_FC); +} + +/* Called outside of the EBB handler to check MMCR0 is sane */ +int ebb_check_mmcr0(void) +{ + u64 val; + + val = mfspr(SPRN_MMCR0); + if ((val & (MMCR0_FC | MMCR0_PMAO)) == MMCR0_FC) { + /* It's OK if we see FC & PMAO, but not FC by itself */ + printf("Outside of loop, only FC set 0x%llx\n", val); + return 1; + } + + return 0; +} + +bool ebb_check_count(int pmc, u64 sample_period, int fudge) +{ + u64 count, upper, lower; + + count = ebb_state.stats.pmc_count[PMC_INDEX(pmc)]; + + lower = ebb_state.stats.ebb_count * (sample_period - fudge); + + if (count < lower) { + printf("PMC%d count (0x%llx) below lower limit 0x%llx (-0x%llx)\n", + pmc, count, lower, lower - count); + return false; + } + + upper = ebb_state.stats.ebb_count * (sample_period + fudge); + + if (count > upper) { + printf("PMC%d count (0x%llx) above upper limit 0x%llx (+0x%llx)\n", + pmc, count, upper, count - upper); + return false; + } + + printf("PMC%d count (0x%llx) is between 0x%llx and 0x%llx delta +0x%llx/-0x%llx\n", + pmc, count, lower, upper, count - lower, upper - count); + + return true; +} + +void standard_ebb_callee(void) +{ + int found, i; + u64 val; + + val = mfspr(SPRN_BESCR); + if (!(val & BESCR_PMEO)) { + ebb_state.stats.spurious++; + goto out; + } + + ebb_state.stats.ebb_count++; + trace_log_counter(ebb_state.trace, ebb_state.stats.ebb_count); + + val = mfspr(SPRN_MMCR0); + trace_log_reg(ebb_state.trace, SPRN_MMCR0, val); + + found = 0; + for (i = 1; i <= 6; i++) { + if (ebb_state.pmc_enable[PMC_INDEX(i)]) + found += count_pmc(i, sample_period); + } + + if (!found) + ebb_state.stats.no_overflow++; + +out: + reset_ebb(); +} + +extern void ebb_handler(void); + +void setup_ebb_handler(void (*callee)(void)) +{ + u64 entry; + +#if defined(_CALL_ELF) && _CALL_ELF == 2 + entry = (u64)ebb_handler; +#else + struct opd + { + u64 entry; + u64 toc; + } *opd; + + opd = (struct opd *)ebb_handler; + entry = opd->entry; +#endif + printf("EBB Handler is at %#llx\n", entry); + + ebb_user_func = callee; + + /* Ensure ebb_user_func is set before we set the handler */ + mb(); + mtspr(SPRN_EBBHR, entry); + + /* Make sure the handler is set before we return */ + mb(); +} + +void clear_ebb_stats(void) +{ + memset(&ebb_state.stats, 0, sizeof(ebb_state.stats)); +} + +void dump_summary_ebb_state(void) +{ + printf("ebb_state:\n" \ + " ebb_count = %d\n" \ + " spurious = %d\n" \ + " negative = %d\n" \ + " no_overflow = %d\n" \ + " pmc[1] count = 0x%llx\n" \ + " pmc[2] count = 0x%llx\n" \ + " pmc[3] count = 0x%llx\n" \ + " pmc[4] count = 0x%llx\n" \ + " pmc[5] count = 0x%llx\n" \ + " pmc[6] count = 0x%llx\n", + ebb_state.stats.ebb_count, ebb_state.stats.spurious, + ebb_state.stats.negative, ebb_state.stats.no_overflow, + ebb_state.stats.pmc_count[0], ebb_state.stats.pmc_count[1], + ebb_state.stats.pmc_count[2], ebb_state.stats.pmc_count[3], + ebb_state.stats.pmc_count[4], ebb_state.stats.pmc_count[5]); +} + +static char *decode_mmcr0(u32 value) +{ + static char buf[16]; + + buf[0] = '\0'; + + if (value & (1 << 31)) + strcat(buf, "FC "); + if (value & (1 << 26)) + strcat(buf, "PMAE "); + if (value & (1 << 7)) + strcat(buf, "PMAO "); + + return buf; +} + +static char *decode_bescr(u64 value) +{ + static char buf[16]; + + buf[0] = '\0'; + + if (value & (1ull << 63)) + strcat(buf, "GE "); + if (value & (1ull << 32)) + strcat(buf, "PMAE "); + if (value & 1) + strcat(buf, "PMAO "); + + return buf; +} + +void dump_ebb_hw_state(void) +{ + u64 bescr; + u32 mmcr0; + + mmcr0 = mfspr(SPRN_MMCR0); + bescr = mfspr(SPRN_BESCR); + + printf("HW state:\n" \ + "MMCR0 0x%016x %s\n" \ + "EBBHR 0x%016lx\n" \ + "BESCR 0x%016llx %s\n" \ + "PMC1 0x%016lx\n" \ + "PMC2 0x%016lx\n" \ + "PMC3 0x%016lx\n" \ + "PMC4 0x%016lx\n" \ + "PMC5 0x%016lx\n" \ + "PMC6 0x%016lx\n" \ + "SIAR 0x%016lx\n", + mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_EBBHR), bescr, + decode_bescr(bescr), mfspr(SPRN_PMC1), mfspr(SPRN_PMC2), + mfspr(SPRN_PMC3), mfspr(SPRN_PMC4), mfspr(SPRN_PMC5), + mfspr(SPRN_PMC6), mfspr(SPRN_SIAR)); +} + +void dump_ebb_state(void) +{ + dump_summary_ebb_state(); + + dump_ebb_hw_state(); + + trace_buffer_print(ebb_state.trace); +} + +int count_pmc(int pmc, uint32_t sample_period) +{ + uint32_t start_value; + u64 val; + + /* 0) Read PMC */ + start_value = pmc_sample_period(sample_period); + + val = read_pmc(pmc); + if (val < start_value) + ebb_state.stats.negative++; + else + ebb_state.stats.pmc_count[PMC_INDEX(pmc)] += val - start_value; + + trace_log_reg(ebb_state.trace, SPRN_PMC1 + pmc - 1, val); + + /* 1) Reset PMC */ + write_pmc(pmc, start_value); + + /* Report if we overflowed */ + return val >= COUNTER_OVERFLOW; +} + +int ebb_event_enable(struct |