diff options
-rw-r--r-- | Kernel/FileSystem/ProcFS.cpp | 10 | ||||
-rw-r--r-- | Kernel/PerformanceEventBuffer.cpp | 53 | ||||
-rw-r--r-- | Kernel/PerformanceEventBuffer.h | 6 | ||||
-rw-r--r-- | Kernel/Scheduler.cpp | 21 | ||||
-rw-r--r-- | Kernel/Syscalls/profiling.cpp | 24 |
5 files changed, 91 insertions, 23 deletions
diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 3f77764b9d..0a41328964 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -474,6 +474,15 @@ static bool procfs$modules(InodeIdentifier, KBufferBuilder& builder) return true; } +static bool procfs$profile(InodeIdentifier, KBufferBuilder& builder) +{ + extern PerformanceEventBuffer* g_global_perf_events; + if (!g_global_perf_events) + return false; + + return g_global_perf_events->to_json(builder); +} + static bool procfs$pid_perf_events(InodeIdentifier identifier, KBufferBuilder& builder) { auto process = Process::from_pid(to_pid(identifier)); @@ -1703,6 +1712,7 @@ ProcFS::ProcFS() m_entries[FI_Root_uptime] = { "uptime", FI_Root_uptime, false, procfs$uptime }; m_entries[FI_Root_cmdline] = { "cmdline", FI_Root_cmdline, true, procfs$cmdline }; m_entries[FI_Root_modules] = { "modules", FI_Root_modules, true, procfs$modules }; + m_entries[FI_Root_profile] = { "profile", FI_Root_profile, true, procfs$profile }; m_entries[FI_Root_sys] = { "sys", FI_Root_sys, true }; m_entries[FI_Root_net] = { "net", FI_Root_net, false }; diff --git a/Kernel/PerformanceEventBuffer.cpp b/Kernel/PerformanceEventBuffer.cpp index 0af0269d50..990ddfc5c4 100644 --- a/Kernel/PerformanceEventBuffer.cpp +++ b/Kernel/PerformanceEventBuffer.cpp @@ -98,6 +98,7 @@ KResult PerformanceEventBuffer::append_with_eip_and_ebp(u32 eip, u32 ebp, int ty event.stack_size = min(sizeof(event.stack) / sizeof(FlatPtr), static_cast<size_t>(backtrace.size())); memcpy(event.stack, backtrace.data(), event.stack_size * sizeof(FlatPtr)); + event.tid = Thread::current()->tid().value(); event.timestamp = TimeManagement::the().uptime_ms(); at(m_count++) = event; return KSuccess; @@ -118,27 +119,9 @@ OwnPtr<KBuffer> PerformanceEventBuffer::to_json(ProcessID pid, const String& exe return builder.build(); } -bool PerformanceEventBuffer::to_json(KBufferBuilder& builder, ProcessID pid, const String& executable_path) const +template<typename Serializer> +bool PerformanceEventBuffer::to_json_impl(Serializer& object) const { - auto process = Process::from_pid(pid); - VERIFY(process); - ScopedSpinLock locker(process->space().get_lock()); - - JsonObjectSerializer object(builder); - object.add("pid", pid.value()); - object.add("executable", executable_path); - - { - auto region_array = object.add_array("regions"); - for (const auto& region : process->space().regions()) { - auto region_object = region_array.add_object(); - region_object.add("base", region.vaddr().get()); - region_object.add("size", region.size()); - region_object.add("name", region.name()); - } - region_array.finish(); - } - auto array = object.add_array("events"); for (size_t i = 0; i < m_count; ++i) { auto& event = at(i); @@ -171,6 +154,36 @@ bool PerformanceEventBuffer::to_json(KBufferBuilder& builder, ProcessID pid, con return true; } +bool PerformanceEventBuffer::to_json(KBufferBuilder& builder) +{ + JsonObjectSerializer object(builder); + return to_json_impl(object); +} + +bool PerformanceEventBuffer::to_json(KBufferBuilder& builder, ProcessID pid, const String& executable_path) const +{ + auto process = Process::from_pid(pid); + VERIFY(process); + ScopedSpinLock locker(process->space().get_lock()); + + JsonObjectSerializer object(builder); + object.add("pid", pid.value()); + object.add("executable", executable_path); + + { + auto region_array = object.add_array("regions"); + for (const auto& region : process->space().regions()) { + auto region_object = region_array.add_object(); + region_object.add("base", region.vaddr().get()); + region_object.add("size", region.size()); + region_object.add("name", region.name()); + } + region_array.finish(); + } + + return to_json_impl(object); +} + OwnPtr<PerformanceEventBuffer> PerformanceEventBuffer::try_create_with_size(size_t buffer_size) { auto buffer = KBuffer::try_create_with_size(buffer_size, Region::Access::Read | Region::Access::Write, "Performance events", AllocationStrategy::AllocateNow); diff --git a/Kernel/PerformanceEventBuffer.h b/Kernel/PerformanceEventBuffer.h index 81911a1784..b85b60f7c8 100644 --- a/Kernel/PerformanceEventBuffer.h +++ b/Kernel/PerformanceEventBuffer.h @@ -78,9 +78,15 @@ public: OwnPtr<KBuffer> to_json(ProcessID, const String& executable_path) const; bool to_json(KBufferBuilder&, ProcessID, const String& executable_path) const; + // Used by full-system profile (/proc/profile) + bool to_json(KBufferBuilder&); + private: explicit PerformanceEventBuffer(NonnullOwnPtr<KBuffer>); + template<typename Serializer> + bool to_json_impl(Serializer&) const; + PerformanceEvent& at(size_t index); size_t m_count { 0 }; diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 0bf6e0b6e0..fb71b6af28 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -42,6 +42,9 @@ namespace Kernel { +extern bool g_profiling_all_threads; +extern PerformanceEventBuffer* g_global_perf_events; + class SchedulerPerProcessorData { AK_MAKE_NONCOPYABLE(SchedulerPerProcessorData); AK_MAKE_NONMOVABLE(SchedulerPerProcessorData); @@ -542,10 +545,22 @@ void Scheduler::timer_tick(const RegisterState& regs) if (!is_bsp) return; // TODO: This prevents scheduling on other CPUs! #endif - if (current_thread->process().is_profiling()) { + + PerformanceEventBuffer* perf_events = nullptr; + + 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; + } else if (current_thread->process().is_profiling()) { VERIFY(current_thread->process().perf_events()); - auto& perf_events = *current_thread->process().perf_events(); - [[maybe_unused]] auto rc = perf_events.append_with_eip_and_ebp(regs.eip, regs.ebp, PERF_EVENT_SAMPLE, 0, 0); + perf_events = current_thread->process().perf_events(); + } + + if (perf_events) { + [[maybe_unused]] auto rc = perf_events->append_with_eip_and_ebp(regs.eip, regs.ebp, PERF_EVENT_SAMPLE, 0, 0); } if (current_thread->tick()) diff --git a/Kernel/Syscalls/profiling.cpp b/Kernel/Syscalls/profiling.cpp index 42e1381697..01001fe161 100644 --- a/Kernel/Syscalls/profiling.cpp +++ b/Kernel/Syscalls/profiling.cpp @@ -32,9 +32,25 @@ namespace Kernel { +PerformanceEventBuffer* g_global_perf_events; +bool g_profiling_all_threads; + KResultOr<int> Process::sys$profiling_enable(pid_t pid) { REQUIRE_NO_PROMISES; + + if (pid == -1) { + if (!is_superuser()) + return EPERM; + ScopedCritical critical; + if (g_global_perf_events) + g_global_perf_events->clear(); + else + g_global_perf_events = PerformanceEventBuffer::try_create_with_size(32 * MiB).leak_ptr(); + g_profiling_all_threads = true; + return 0; + } + ScopedSpinLock lock(g_processes_lock); auto process = Process::from_pid(pid); if (!process) @@ -51,6 +67,14 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid) KResultOr<int> Process::sys$profiling_disable(pid_t pid) { + if (pid == -1) { + if (!is_superuser()) + return EPERM; + ScopedCritical critical; + g_profiling_all_threads = false; + return 0; + } + ScopedSpinLock lock(g_processes_lock); auto process = Process::from_pid(pid); if (!process) |