Procházet zdrojové kódy

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.
Gunnar Beutner před 4 roky
rodič
revize
8614d18956

+ 12 - 5
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 (&current_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);
+    }
 };
 
 }

+ 0 - 3
Kernel/Scheduler.cpp

@@ -10,7 +10,6 @@
 #include <AK/Time.h>
 #include <Kernel/Debug.h>
 #include <Kernel/Panic.h>
-#include <Kernel/PerformanceManager.h>
 #include <Kernel/Process.h>
 #include <Kernel/RTC.h>
 #include <Kernel/Scheduler.h>
@@ -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;
 

+ 5 - 0
Kernel/Syscalls/profiling.cpp

@@ -9,6 +9,7 @@
 #include <Kernel/FileSystem/VirtualFileSystem.h>
 #include <Kernel/PerformanceManager.h>
 #include <Kernel/Process.h>
+#include <Kernel/Time/TimeManagement.h>
 
 namespace Kernel {
 
@@ -34,6 +35,7 @@ KResultOr<int> 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<int> 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<int> 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<int> 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;
 }

+ 22 - 0
Kernel/Time/TimeManagement.cpp

@@ -10,6 +10,7 @@
 #include <Kernel/ACPI/Parser.h>
 #include <Kernel/CommandLine.h>
 #include <Kernel/Interrupts/APIC.h>
+#include <Kernel/PerformanceManager.h>
 #include <Kernel/Scheduler.h>
 #include <Kernel/Time/APICTimer.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
     // 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));
+}
+
 }

+ 7 - 0
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<HardwareTimerBase> m_system_timer;
     RefPtr<HardwareTimerBase> m_time_keeper_timer;
+
+    Atomic<u32> m_profile_enable_count { 0 };
+    RefPtr<HardwareTimerBase> m_profile_timer;
 };
 
 }