mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +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
Notes:
sideshowbarker
2024-07-18 21:46:01 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/ea500dd3e31
5 changed files with 91 additions and 23 deletions
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue