summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86/machine/pmu_intel.c18
-rw-r--r--kern/perfmon.c38
-rw-r--r--kern/perfmon.h3
-rw-r--r--kern/perfmon_i.h38
-rw-r--r--test/Kconfig4
-rw-r--r--test/test_perfmon_cpu.c24
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");