/*
* Copyright (c) 2014 Remy Noel.
* Copyright (c) 2014 Richard Braun.
*
* 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 .
*
*
* This test checks that cpu-local remote thread monitoring works properly.
* The test uses two threads: the main one which is monitored and another that
* acts as a control thread. The instruction count should increase while the
* main thread runs and shouldn't otherwise.
* Initially the main thread runs while the control one doesn't.
* The control thread is then scheduled and finally the main thread is
* rescheduled.
*
* In order to trigger the counter slot reuse mechanism, this tests also adds a
* cpu counter on the same cpu as the threads.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define NR_LOOPS 1000000UL
static inline void
test_loop(void)
{
volatile unsigned long i;
for (i = 0; i < NR_LOOPS; i++);
}
struct thread *test_main, *test_control;
struct perfmon_group *thread_group;
struct perfmon_event *thread_ev_cycle;
bool test_monitoring = true;
struct perfmon_group *cpu_group;
struct perfmon_event *cpu_ev_cycle;
struct proxy_thread_runq {
struct spinlock lock;
unsigned int cpu;
};
static void
x15_test_main_run(void *arg)
{
unsigned long long thread_count1, thread_count2;
unsigned long long cpu_count1, cpu_count2;
int error;
(void)arg;
thread_preempt_disable();
/* Create a perfmon group to monitor this cpu. */
error = perfmon_group_create(&cpu_group);
error_check(error, "perfmon_cpu_group_create");
error = perfmon_event_create(&cpu_ev_cycle, PERFMON_ET_GENERIC,
PERFMON_EV_CYCLE, PERFMON_EF_KERN);
error_check(error, "perfmon_cpu_event_create");
perfmon_group_add(cpu_group, cpu_ev_cycle);
error = perfmon_group_attach_cpu(cpu_group, 0);
error_check(error, "perfmon_cpu_group_attach");
error = perfmon_group_start(cpu_group);
error_check(error, "perfmon_group_start_cpu");
perfmon_group_update(cpu_group);
cpu_count1 = perfmon_event_read(cpu_ev_cycle);
/* Create a perfmon group to monitor this thread.*/
error = perfmon_group_create(&thread_group);
error_check(error, "perfmon_thread_group_create");
error = perfmon_event_create(&thread_ev_cycle, PERFMON_ET_GENERIC,
PERFMON_EV_CYCLE, PERFMON_EF_KERN);
error_check(error, "perfmon_thread_event_create");
perfmon_group_add(thread_group, thread_ev_cycle);
error = perfmon_group_attach(thread_group, thread_self());
error_check(error, "perfmon_thread_group_attach");
/* Start monitoring */
error = perfmon_group_start(thread_group);
error_check(error, "perfmon_group_start_thread");
perfmon_group_update(thread_group);
thread_count1 = perfmon_event_read(thread_ev_cycle);
test_loop();
perfmon_group_update(thread_group);
thread_count2 = perfmon_event_read(thread_ev_cycle);
perfmon_group_update(cpu_group);
cpu_count2 = perfmon_event_read(cpu_ev_cycle);
if (thread_count1 == thread_count2) {
panic("not monitoring thread after monitoring start \n"
"stayed at %llu cycles\n", thread_count1);
}
if (cpu_count1 == cpu_count2) {
panic("not monitoring cpu after monitoring start \n"
"stayed at %llu cycles\n", cpu_count1);
}
/* Lets switch to the other thread and sleep */
test_monitoring = false;
thread_wakeup(test_control);
thread_sleep(NULL, &test_monitoring, "dummy sync object");
/* waking up */
if (!test_monitoring) {
panic("main thread woke up when it should not");
}
/* Check monitoring is active again */
perfmon_group_update(cpu_group);
cpu_count1 = perfmon_event_read(cpu_ev_cycle);
perfmon_group_update(thread_group);
thread_count1 = perfmon_event_read(thread_ev_cycle);
test_loop();
perfmon_group_update(thread_group);
thread_count2 = perfmon_event_read(thread_ev_cycle);
perfmon_group_update(cpu_group);
cpu_count2 = perfmon_event_read(cpu_ev_cycle);
if (thread_count1 == thread_count2) {
panic("not monitoring thread after thread re-schedueling\n"
"stayed at %llu cycles\n", thread_count1);
}
if (cpu_count1 == cpu_count2) {
panic("not monitoring cpu after thread got re-scheduled \n"
"stayed at %llu cycles\n", cpu_count1);
}
thread_preempt_enable();
error = perfmon_group_stop(thread_group);
error_check(error, "perfmon_group_stop_thread");
error = perfmon_group_detach(thread_group);
error_check(error, "perfmon_group_detach_thread");
error = perfmon_group_destroy(thread_group);
error_check(error, "perfmon_group_destroy_thread");
error = perfmon_group_stop(cpu_group);
error_check(error, "perfmon_group_stop_cpu");
error = perfmon_group_detach(cpu_group);
error_check(error, "perfmon_group_detach_cpu");
error = perfmon_group_destroy(cpu_group);
error_check(error, "perfmon_group_destroy_cpu");
printf("test perfmon thread sched finished\n");
}
static void
x15_test_control_run(void *arg)
{
unsigned long long thread_count1, thread_count2;
unsigned long long cpu_count1, cpu_count2;
(void)arg;
thread_preempt_disable();
/* Let first thread run */
while (test_monitoring) {
thread_sleep(NULL, &test_monitoring, "dummy sync object");
}
/* Check this thread is not monitored (but the cpu is) */
perfmon_group_update(cpu_group);
cpu_count1 = perfmon_event_read(cpu_ev_cycle);
perfmon_group_update(thread_group);
thread_count1 = perfmon_event_read(thread_ev_cycle);
test_loop();
perfmon_group_update(thread_group);
thread_count2 = perfmon_event_read(thread_ev_cycle);
perfmon_group_update(cpu_group);
cpu_count2 = perfmon_event_read(cpu_ev_cycle);
if (thread_count1 != thread_count2) {
panic("still monitoring while thread is unschedueled\n"
"gone from %llu to %llu cycles\n", thread_count1, thread_count2);
}
if (cpu_count1 == cpu_count2) {
panic("not monitoring cpu after thread got unscheduled \n"
"stayed at %llu cycles\n", cpu_count1);
}
/* Wakeup x15_test_main */
test_monitoring = true;
thread_wakeup(test_main);
thread_preempt_enable();
}
void
test_setup(void)
{
struct thread_attr attr;
struct cpumap *cpumap;
int error;
printf("test perfmon thread sched start\n");
error = cpumap_create(&cpumap);
error_check(error, "cpumap_create");
cpumap_zero(cpumap);
cpumap_set(cpumap, 0);
thread_attr_init(&attr, "x15_test_main thread");
thread_attr_set_detached(&attr);
thread_attr_set_cpumap(&attr, cpumap);
thread_attr_set_policy(&attr, THREAD_SCHED_POLICY_FIFO);
error = thread_create(&test_main, &attr, x15_test_main_run, NULL);
error_check(error, "thread_create 0");
thread_attr_init(&attr, "15_test_control_thread");
thread_attr_set_detached(&attr);
thread_attr_set_cpumap(&attr, cpumap);
thread_attr_set_policy(&attr, THREAD_SCHED_POLICY_FIFO);
error = thread_create(&test_control, &attr, x15_test_control_run, NULL);
error_check(error, "thread_create 1");
}