summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Beutner <gbeutner@serenityos.org>2021-05-13 22:15:13 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-14 00:35:57 +0200
commit8614d18956f90edb08c5057f8448493b4cd3315c (patch)
tree8d41f67f098f031355916163e736edf0618d4f75
parentd6b3513aabbf1f34eb5232517cb612ca0b7cbc9d (diff)
downloadserenity-8614d18956f90edb08c5057f8448493b4cd3315c.zip
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.
-rw-r--r--Kernel/PerformanceManager.h17
-rw-r--r--Kernel/Scheduler.cpp3
-rw-r--r--Kernel/Syscalls/profiling.cpp5
-rw-r--r--Kernel/Time/TimeManagement.cpp22
-rw-r--r--Kernel/Time/TimeManagement.h7
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 (&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);
+ }
};
}
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 <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;
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 <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;
}
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 <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));
+}
+
}
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<HardwareTimerBase> m_system_timer;
RefPtr<HardwareTimerBase> m_time_keeper_timer;
+
+ Atomic<u32> m_profile_enable_count { 0 };
+ RefPtr<HardwareTimerBase> m_profile_timer;
};
}