/* * 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"); }