From 8614d18956f90edb08c5057f8448493b4cd3315c Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 13 May 2021 22:15:13 +0200 Subject: Kernel: Use a separate timer for profiling the system This updates the profiling subsystem to use a separate timer to trigger CPU sampling. This timer has a higher resolution (1000Hz) and is independent from the scheduler. At a later time the resolution could even be made configurable with an argument for sys$profiling_enable() - but not today. --- Kernel/PerformanceManager.h | 17 ++++++++++++----- Kernel/Scheduler.cpp | 3 --- Kernel/Syscalls/profiling.cpp | 5 +++++ Kernel/Time/TimeManagement.cpp | 22 ++++++++++++++++++++++ Kernel/Time/TimeManagement.h | 7 +++++++ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/Kernel/PerformanceManager.h b/Kernel/PerformanceManager.h index e6ac95b7a1..00a21785ec 100644 --- a/Kernel/PerformanceManager.h +++ b/Kernel/PerformanceManager.h @@ -58,11 +58,7 @@ public: if (g_profiling_all_threads) { VERIFY(g_global_perf_events); - // FIXME: We currently don't collect samples while idle. - // That will be an interesting mode to add in the future. :^) - if (¤t_thread != Processor::current().idle_thread()) { - perf_events = g_global_perf_events; - } + perf_events = g_global_perf_events; } else if (current_thread.process().is_profiling()) { VERIFY(current_thread.process().perf_events()); perf_events = current_thread.process().perf_events(); @@ -88,6 +84,17 @@ public: [[maybe_unused]] auto res = event_buffer->append(PERF_EVENT_MUNMAP, region.base().get(), region.size(), nullptr); } } + + inline static void timer_tick(RegisterState const& regs) + { + auto current_thread = Thread::current(); + // FIXME: We currently don't collect samples while idle. + // That will be an interesting mode to add in the future. :^) + if (!current_thread || current_thread == Processor::current().idle_thread()) + return; + + PerformanceManager::add_cpu_sample_event(*current_thread, regs); + } }; } diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 457db25350..8417719bc3 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -501,8 +500,6 @@ void Scheduler::timer_tick(const RegisterState& regs) return; // TODO: This prevents scheduling on other CPUs! #endif - PerformanceManager::add_cpu_sample_event(*current_thread, regs); - if (current_thread->tick()) return; diff --git a/Kernel/Syscalls/profiling.cpp b/Kernel/Syscalls/profiling.cpp index ba7685488c..806a040187 100644 --- a/Kernel/Syscalls/profiling.cpp +++ b/Kernel/Syscalls/profiling.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace Kernel { @@ -34,6 +35,7 @@ KResultOr Process::sys$profiling_enable(pid_t pid) PerformanceManager::add_process_created_event(process); return IterationDecision::Continue; }); + TimeManagement::the().enable_profile_timer(); return 0; } @@ -48,6 +50,7 @@ KResultOr Process::sys$profiling_enable(pid_t pid) if (!process->create_perf_events_buffer_if_needed()) return ENOMEM; process->set_profiling(true); + TimeManagement::the().enable_profile_timer(); return 0; } @@ -60,6 +63,7 @@ KResultOr Process::sys$profiling_disable(pid_t pid) return EPERM; ScopedCritical critical; g_profiling_all_threads = false; + TimeManagement::the().disable_profile_timer(); return 0; } @@ -71,6 +75,7 @@ KResultOr Process::sys$profiling_disable(pid_t pid) return EPERM; if (!process->is_profiling()) return EINVAL; + TimeManagement::the().disable_profile_timer(); process->set_profiling(false); return 0; } diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp index 153b02bf21..106f836082 100644 --- a/Kernel/Time/TimeManagement.cpp +++ b/Kernel/Time/TimeManagement.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -289,6 +290,15 @@ UNMAP_AFTER_INIT bool TimeManagement::probe_and_set_non_legacy_hardware_timers() // We don't need an interrupt for time keeping purposes because we // can query the timer. m_time_keeper_timer = m_system_timer; + + if (periodic_timers.size() > 1) + m_profile_timer = periodic_timers[1]; + else + m_profile_timer = non_periodic_timers[1]; + + m_profile_timer->set_callback(PerformanceManager::timer_tick); + m_profile_timer->try_to_set_frequency(m_profile_timer->calculate_nearest_possible_frequency(1)); + return true; } @@ -379,4 +389,16 @@ void TimeManagement::system_timer_tick(const RegisterState& regs) Scheduler::timer_tick(regs); } +void TimeManagement::enable_profile_timer() +{ + if (m_profile_enable_count.fetch_add(1) == 0) + m_profile_timer->try_to_set_frequency(m_profile_timer->calculate_nearest_possible_frequency(OPTIMAL_PROFILE_TICKS_PER_SECOND_RATE)); +} + +void TimeManagement::disable_profile_timer() +{ + if (m_profile_enable_count.fetch_sub(1) == 1) + m_profile_timer->try_to_set_frequency(m_profile_timer->calculate_nearest_possible_frequency(1)); +} + } diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h index 0c48dd79c6..4c382d9577 100644 --- a/Kernel/Time/TimeManagement.h +++ b/Kernel/Time/TimeManagement.h @@ -16,6 +16,7 @@ namespace Kernel { #define OPTIMAL_TICKS_PER_SECOND_RATE 250 +#define OPTIMAL_PROFILE_TICKS_PER_SECOND_RATE 1000 class HardwareTimerBase; @@ -55,6 +56,9 @@ public: static bool is_hpet_periodic_mode_allowed(); + void enable_profile_timer(); + void disable_profile_timer(); + u64 uptime_ms() const; static Time now(); @@ -90,6 +94,9 @@ private: RefPtr m_system_timer; RefPtr m_time_keeper_timer; + + Atomic m_profile_enable_count { 0 }; + RefPtr m_profile_timer; }; } -- cgit v1.2.3