mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 02:47:35 +00:00
Kernel: Start work on full system profiling :^)
The superuser can now call sys$profiling_enable() with PID -1 to enable profiling of all running threads in the system. The perf events are collected in a global PerformanceEventBuffer (currently 32 MiB in size.) The events can be accessed via /proc/profile
This commit is contained in:
parent
b425c2602c
commit
ea500dd3e3
5 changed files with 91 additions and 23 deletions
|
@ -474,6 +474,15 @@ static bool procfs$modules(InodeIdentifier, KBufferBuilder& builder)
|
||||||
return true;
|
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)
|
static bool procfs$pid_perf_events(InodeIdentifier identifier, KBufferBuilder& builder)
|
||||||
{
|
{
|
||||||
auto process = Process::from_pid(to_pid(identifier));
|
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_uptime] = { "uptime", FI_Root_uptime, false, procfs$uptime };
|
||||||
m_entries[FI_Root_cmdline] = { "cmdline", FI_Root_cmdline, true, procfs$cmdline };
|
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_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_sys] = { "sys", FI_Root_sys, true };
|
||||||
m_entries[FI_Root_net] = { "net", FI_Root_net, false };
|
m_entries[FI_Root_net] = { "net", FI_Root_net, false };
|
||||||
|
|
||||||
|
|
|
@ -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()));
|
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));
|
memcpy(event.stack, backtrace.data(), event.stack_size * sizeof(FlatPtr));
|
||||||
|
|
||||||
|
event.tid = Thread::current()->tid().value();
|
||||||
event.timestamp = TimeManagement::the().uptime_ms();
|
event.timestamp = TimeManagement::the().uptime_ms();
|
||||||
at(m_count++) = event;
|
at(m_count++) = event;
|
||||||
return KSuccess;
|
return KSuccess;
|
||||||
|
@ -118,27 +119,9 @@ OwnPtr<KBuffer> PerformanceEventBuffer::to_json(ProcessID pid, const String& exe
|
||||||
return builder.build();
|
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");
|
auto array = object.add_array("events");
|
||||||
for (size_t i = 0; i < m_count; ++i) {
|
for (size_t i = 0; i < m_count; ++i) {
|
||||||
auto& event = at(i);
|
auto& event = at(i);
|
||||||
|
@ -171,6 +154,36 @@ bool PerformanceEventBuffer::to_json(KBufferBuilder& builder, ProcessID pid, con
|
||||||
return true;
|
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)
|
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);
|
auto buffer = KBuffer::try_create_with_size(buffer_size, Region::Access::Read | Region::Access::Write, "Performance events", AllocationStrategy::AllocateNow);
|
||||||
|
|
|
@ -78,9 +78,15 @@ public:
|
||||||
OwnPtr<KBuffer> to_json(ProcessID, const String& executable_path) const;
|
OwnPtr<KBuffer> to_json(ProcessID, const String& executable_path) const;
|
||||||
bool to_json(KBufferBuilder&, 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:
|
private:
|
||||||
explicit PerformanceEventBuffer(NonnullOwnPtr<KBuffer>);
|
explicit PerformanceEventBuffer(NonnullOwnPtr<KBuffer>);
|
||||||
|
|
||||||
|
template<typename Serializer>
|
||||||
|
bool to_json_impl(Serializer&) const;
|
||||||
|
|
||||||
PerformanceEvent& at(size_t index);
|
PerformanceEvent& at(size_t index);
|
||||||
|
|
||||||
size_t m_count { 0 };
|
size_t m_count { 0 };
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
extern bool g_profiling_all_threads;
|
||||||
|
extern PerformanceEventBuffer* g_global_perf_events;
|
||||||
|
|
||||||
class SchedulerPerProcessorData {
|
class SchedulerPerProcessorData {
|
||||||
AK_MAKE_NONCOPYABLE(SchedulerPerProcessorData);
|
AK_MAKE_NONCOPYABLE(SchedulerPerProcessorData);
|
||||||
AK_MAKE_NONMOVABLE(SchedulerPerProcessorData);
|
AK_MAKE_NONMOVABLE(SchedulerPerProcessorData);
|
||||||
|
@ -542,10 +545,22 @@ void Scheduler::timer_tick(const RegisterState& regs)
|
||||||
if (!is_bsp)
|
if (!is_bsp)
|
||||||
return; // TODO: This prevents scheduling on other CPUs!
|
return; // TODO: This prevents scheduling on other CPUs!
|
||||||
#endif
|
#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());
|
VERIFY(current_thread->process().perf_events());
|
||||||
auto& perf_events = *current_thread->process().perf_events();
|
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);
|
}
|
||||||
|
|
||||||
|
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())
|
if (current_thread->tick())
|
||||||
|
|
|
@ -32,9 +32,25 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
PerformanceEventBuffer* g_global_perf_events;
|
||||||
|
bool g_profiling_all_threads;
|
||||||
|
|
||||||
KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
||||||
{
|
{
|
||||||
REQUIRE_NO_PROMISES;
|
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);
|
ScopedSpinLock lock(g_processes_lock);
|
||||||
auto process = Process::from_pid(pid);
|
auto process = Process::from_pid(pid);
|
||||||
if (!process)
|
if (!process)
|
||||||
|
@ -51,6 +67,14 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
||||||
|
|
||||||
KResultOr<int> Process::sys$profiling_disable(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);
|
ScopedSpinLock lock(g_processes_lock);
|
||||||
auto process = Process::from_pid(pid);
|
auto process = Process::from_pid(pid);
|
||||||
if (!process)
|
if (!process)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue