diff options
-rw-r--r-- | arch/x86/machine/pmu_intel.c | 18 | ||||
-rw-r--r-- | kern/perfmon.c | 38 | ||||
-rw-r--r-- | kern/perfmon.h | 3 | ||||
-rw-r--r-- | kern/perfmon_i.h | 38 | ||||
-rw-r--r-- | test/Kconfig | 4 | ||||
-rw-r--r-- | test/test_perfmon_cpu.c | 24 |
6 files changed, 122 insertions, 3 deletions
diff --git a/arch/x86/machine/pmu_intel.c b/arch/x86/machine/pmu_intel.c index 6439282..40e1450 100644 --- a/arch/x86/machine/pmu_intel.c +++ b/arch/x86/machine/pmu_intel.c @@ -231,6 +231,21 @@ pmu_intel_read(unsigned int pmc_id) return cpu_get_msr64(PMU_INTEL_MSR_PMC0 + pmc_id); } +#ifdef CONFIG_PERFMON_TEST + +static void +pmu_intel_write(unsigned int pmc_id, uint64_t value) +{ + uint32_t low, high; + + low = value & 0xffffffff; + high = value >> 32; + + cpu_set_msr(PMU_INTEL_MSR_PMC0 + pmc_id, low, high); +} + +#endif /* CONFIG_PERFMON_TEST */ + static void pmu_intel_handle_of_intr_v1(struct trap_frame *frame) { @@ -318,6 +333,9 @@ pmu_intel_setup(void) pmu_driver.read = pmu_intel_read; pmu_driver.get_pmc_width = pmu_intel_get_pmc_width; pmu_driver.handle_of_intr = pmu_intel_handle_of_intr_v1; +#ifdef CONFIG_PERFMON_TEST + pmu_driver.write = pmu_intel_write; +#endif return perfmon_pmu_register(&pmu_driver); } diff --git a/kern/perfmon.c b/kern/perfmon.c index e91f3cd..4da95fb 100644 --- a/kern/perfmon.c +++ b/kern/perfmon.c @@ -40,6 +40,7 @@ #include <kern/panic.h> #include <kern/percpu.h> #include <kern/perfmon.h> +#include <kern/perfmon_i.h> #include <kern/spinlock.h> #include <kern/thread.h> #include <kern/xcall.h> @@ -61,6 +62,10 @@ struct perfmon_event { unsigned int type; unsigned int id; unsigned int pmc_index; +#ifdef CONFIG_PERFMON_TEST + uint64_t value; + bool set_value; +#endif }; #define PERFMON_INVALID_CPU ((unsigned int)-1) @@ -612,6 +617,22 @@ perfmon_event_read(const struct perfmon_event *event) return event->count; } +#ifdef CONFIG_PERFMON_TEST + +int +perfmon_event_write(struct perfmon_event *event, uint64_t value) +{ + if (!pmu_driver.write) { + return ENODEV; + } + event->value = value; + event->set_value = true; + + return 0; +} + +#endif /* CONFIG_PERFMON_TEST */ + void perfmon_event_reset(struct perfmon_event *event) { @@ -903,6 +924,9 @@ perfmon_group_load(struct perfmon_group *group) { struct perfmon_cpu_pmu *cpu_pmu; struct perfmon_event *event; +#ifdef CONFIG_PERFMON_TEST + struct perfmon_pmc *pmc; +#endif assert(!thread_preempt_enabled()); assert(perfmon_group_enabled(group)); @@ -910,6 +934,20 @@ perfmon_group_load(struct perfmon_group *group) cpu_pmu = cpu_local_ptr(perfmon_cpu_pmu); +#ifdef CONFIG_PERFMON_TEST + /* XXX: could be done in the loading loop, but performance does not + * matters in the functional tests using this feature. + */ + list_for_each_entry(&group->events, event, node) { + if (!event->set_value) { + continue; + } + pmc = perfmon_pmc_from_index(event->pmc_index); + pmu_driver.write(pmc->id, event->value); + event->set_value = false; + } +#endif + list_for_each_entry(&group->events, event, node) { perfmon_cpu_pmu_load(cpu_pmu, event->pmc_index); event->prev = pmu_driver.read(perfmon_pmu.pmcs[event->pmc_index].id); diff --git a/kern/perfmon.h b/kern/perfmon.h index 60e40e0..3b9a002 100644 --- a/kern/perfmon.h +++ b/kern/perfmon.h @@ -66,6 +66,9 @@ struct perfmon_pmu_ops { uint64_t (*read)(unsigned int pmc_id); uint8_t (*get_pmc_width)(void); void (*handle_of_intr)(struct trap_frame *frame); +#ifdef CONFIG_PERFMON_TEST + void (*write)(unsigned int pmc_id, uint64_t value); +#endif /* CONFIG_PERFMON_TEST */ }; /* diff --git a/kern/perfmon_i.h b/kern/perfmon_i.h new file mode 100644 index 0000000..5cb4d45 --- /dev/null +++ b/kern/perfmon_i.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Remy Noel. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * Performance monitoring based on performance counters internal functions. + * + */ +#ifndef _KERN_PERFMON_I_H +#define _KERN_PERFMON_I_H + +#include <kern/perfmon.h> + +#ifdef CONFIG_PERFMON_TEST + +/* + * Set an running event hardware counter value for overflow tests purposes. + * + * Beware, this will affect all events associated to the same hardware counter. + */ +int perfmon_event_write(struct perfmon_event *event, uint64_t value); + +#endif /* CONFIG_PERFMON_TEST */ + +#endif /* _KERN_PERFMON_H */ + diff --git a/test/Kconfig b/test/Kconfig index df2272e..d99fdbb 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -55,3 +55,7 @@ config TEST_MODULE_XCALL endchoice endif + +config PERFMON_TEST + def_bool y + depends on TEST_MODULE_PERFMON_THREAD || TEST_MODULE_PERFMON_CPU diff --git a/test/test_perfmon_cpu.c b/test/test_perfmon_cpu.c index 0d2bd76..60db729 100644 --- a/test/test_perfmon_cpu.c +++ b/test/test_perfmon_cpu.c @@ -23,7 +23,7 @@ #include <stddef.h> #include <kern/error.h> -#include <kern/perfmon.h> +#include <kern/perfmon_i.h> #include <kern/printf.h> #include <kern/thread.h> #include <test/test.h> @@ -80,12 +80,30 @@ test_run(void *arg) cpu_delay(WAIT_DELAY_USEC); error = perfmon_group_stop(group); error_check(error, "perfmon_group_stop"); - error = perfmon_group_detach(group); - error_check(error, "perfmon_group_detach"); + test_report_event(ev_cycle, "cycle"); + test_report_event(ev_instruction, "instruction"); + + printf("checking with overflow ...\n"); + /* TODO: choose value depending of architecture */ + perfmon_event_write(ev_cycle, + UINT64_MAX - perfmon_event_read(ev_cycle) / 2); + perfmon_event_write(ev_instruction, + UINT64_MAX - perfmon_event_read(ev_instruction) / 3); + perfmon_event_reset(ev_cycle); + perfmon_event_reset(ev_instruction); + + error = perfmon_group_start(group); + error_check(error, "perfmon_group_start"); + cpu_delay(WAIT_DELAY_USEC); + error = perfmon_group_stop(group); + error_check(error, "perfmon_group_stop"); test_report_event(ev_cycle, "cycle"); test_report_event(ev_instruction, "instruction"); + error = perfmon_group_detach(group); + error_check(error, "perfmon_group_detach"); + error = perfmon_group_destroy(group); error_check(error, "perfmon_group_destroy"); |