summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/FileSystem/ProcFS.cpp10
-rw-r--r--Kernel/PerformanceEventBuffer.cpp53
-rw-r--r--Kernel/PerformanceEventBuffer.h6
-rw-r--r--Kernel/Scheduler.cpp21
-rw-r--r--Kernel/Syscalls/profiling.cpp24
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)