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.
This commit is contained in:
Gunnar Beutner 2021-05-13 22:15:13 +02:00 committed by Andreas Kling
parent d6b3513aab
commit 8614d18956
Notes: sideshowbarker 2024-07-18 18:11:54 +09:00
5 changed files with 46 additions and 8 deletions

View file

@ -58,11 +58,7 @@ public:
if (g_profiling_all_threads) { if (g_profiling_all_threads) {
VERIFY(g_global_perf_events); VERIFY(g_global_perf_events);
// FIXME: We currently don't collect samples while idle. perf_events = g_global_perf_events;
// That will be an interesting mode to add in the future. :^)
if (&current_thread != Processor::current().idle_thread()) {
perf_events = g_global_perf_events;
}
} else if (current_thread.process().is_profiling()) { } else if (current_thread.process().is_profiling()) {
VERIFY(current_thread.process().perf_events()); VERIFY(current_thread.process().perf_events());
perf_events = 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); [[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);
}
}; };
} }

View file

@ -10,7 +10,6 @@
#include <AK/Time.h> #include <AK/Time.h>
#include <Kernel/Debug.h> #include <Kernel/Debug.h>
#include <Kernel/Panic.h> #include <Kernel/Panic.h>
#include <Kernel/PerformanceManager.h>
#include <Kernel/Process.h> #include <Kernel/Process.h>
#include <Kernel/RTC.h> #include <Kernel/RTC.h>
#include <Kernel/Scheduler.h> #include <Kernel/Scheduler.h>
@ -501,8 +500,6 @@ void Scheduler::timer_tick(const RegisterState& regs)
return; // TODO: This prevents scheduling on other CPUs! return; // TODO: This prevents scheduling on other CPUs!
#endif #endif
PerformanceManager::add_cpu_sample_event(*current_thread, regs);
if (current_thread->tick()) if (current_thread->tick())
return; return;

View file

@ -9,6 +9,7 @@
#include <Kernel/FileSystem/VirtualFileSystem.h> #include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/PerformanceManager.h> #include <Kernel/PerformanceManager.h>
#include <Kernel/Process.h> #include <Kernel/Process.h>
#include <Kernel/Time/TimeManagement.h>
namespace Kernel { namespace Kernel {
@ -34,6 +35,7 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid)
PerformanceManager::add_process_created_event(process); PerformanceManager::add_process_created_event(process);
return IterationDecision::Continue; return IterationDecision::Continue;
}); });
TimeManagement::the().enable_profile_timer();
return 0; return 0;
} }
@ -48,6 +50,7 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid)
if (!process->create_perf_events_buffer_if_needed()) if (!process->create_perf_events_buffer_if_needed())
return ENOMEM; return ENOMEM;
process->set_profiling(true); process->set_profiling(true);
TimeManagement::the().enable_profile_timer();
return 0; return 0;
} }
@ -60,6 +63,7 @@ KResultOr<int> Process::sys$profiling_disable(pid_t pid)
return EPERM; return EPERM;
ScopedCritical critical; ScopedCritical critical;
g_profiling_all_threads = false; g_profiling_all_threads = false;
TimeManagement::the().disable_profile_timer();
return 0; return 0;
} }
@ -71,6 +75,7 @@ KResultOr<int> Process::sys$profiling_disable(pid_t pid)
return EPERM; return EPERM;
if (!process->is_profiling()) if (!process->is_profiling())
return EINVAL; return EINVAL;
TimeManagement::the().disable_profile_timer();
process->set_profiling(false); process->set_profiling(false);
return 0; return 0;
} }

View file

@ -10,6 +10,7 @@
#include <Kernel/ACPI/Parser.h> #include <Kernel/ACPI/Parser.h>
#include <Kernel/CommandLine.h> #include <Kernel/CommandLine.h>
#include <Kernel/Interrupts/APIC.h> #include <Kernel/Interrupts/APIC.h>
#include <Kernel/PerformanceManager.h>
#include <Kernel/Scheduler.h> #include <Kernel/Scheduler.h>
#include <Kernel/Time/APICTimer.h> #include <Kernel/Time/APICTimer.h>
#include <Kernel/Time/HPET.h> #include <Kernel/Time/HPET.h>
@ -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 // We don't need an interrupt for time keeping purposes because we
// can query the timer. // can query the timer.
m_time_keeper_timer = m_system_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; return true;
} }
@ -379,4 +389,16 @@ void TimeManagement::system_timer_tick(const RegisterState& regs)
Scheduler::timer_tick(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));
}
} }

View file

@ -16,6 +16,7 @@
namespace Kernel { namespace Kernel {
#define OPTIMAL_TICKS_PER_SECOND_RATE 250 #define OPTIMAL_TICKS_PER_SECOND_RATE 250
#define OPTIMAL_PROFILE_TICKS_PER_SECOND_RATE 1000
class HardwareTimerBase; class HardwareTimerBase;
@ -55,6 +56,9 @@ public:
static bool is_hpet_periodic_mode_allowed(); static bool is_hpet_periodic_mode_allowed();
void enable_profile_timer();
void disable_profile_timer();
u64 uptime_ms() const; u64 uptime_ms() const;
static Time now(); static Time now();
@ -90,6 +94,9 @@ private:
RefPtr<HardwareTimerBase> m_system_timer; RefPtr<HardwareTimerBase> m_system_timer;
RefPtr<HardwareTimerBase> m_time_keeper_timer; RefPtr<HardwareTimerBase> m_time_keeper_timer;
Atomic<u32> m_profile_enable_count { 0 };
RefPtr<HardwareTimerBase> m_profile_timer;
}; };
} }