diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index be267ea97b..10b8136662 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -243,8 +244,11 @@ void page_fault_handler(TrapFrame* trap) auto current_thread = Thread::current(); - if (current_thread) + if (current_thread) { current_thread->set_handling_page_fault(true); + PerformanceManager::add_page_fault_event(*current_thread, regs); + } + ScopeGuard guard = [current_thread] { if (current_thread) current_thread->set_handling_page_fault(false); diff --git a/Kernel/PerformanceEventBuffer.cpp b/Kernel/PerformanceEventBuffer.cpp index 3f7ec2c393..3871e96f7b 100644 --- a/Kernel/PerformanceEventBuffer.cpp +++ b/Kernel/PerformanceEventBuffer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,17 @@ KResult PerformanceEventBuffer::append_with_eip_and_ebp(ProcessID pid, ThreadID if ((g_profiling_event_mask & type) == 0) return EINVAL; + auto current_thread = Thread::current(); + u32 enter_count = 0; + if (current_thread) + enter_count = current_thread->enter_profiler(); + ScopeGuard leave_profiler([&] { + if (current_thread) + current_thread->leave_profiler(); + }); + if (enter_count > 0) + return EINVAL; + PerformanceEvent event; event.type = type; event.lost_samples = lost_samples; @@ -122,6 +134,8 @@ KResult PerformanceEventBuffer::append_with_eip_and_ebp(ProcessID pid, ThreadID event.data.kfree.size = arg1; event.data.kfree.ptr = arg2; break; + case PERF_EVENT_PAGE_FAULT: + break; default: return EINVAL; } @@ -210,6 +224,9 @@ bool PerformanceEventBuffer::to_json_impl(Serializer& object) const event_object.add("ptr", static_cast(event.data.kfree.ptr)); event_object.add("size", static_cast(event.data.kfree.size)); break; + case PERF_EVENT_PAGE_FAULT: + event_object.add("type", "page_fault"); + break; } event_object.add("pid", event.pid); event_object.add("tid", event.tid); diff --git a/Kernel/PerformanceManager.h b/Kernel/PerformanceManager.h index 57fd9d7ea3..f00c4db797 100644 --- a/Kernel/PerformanceManager.h +++ b/Kernel/PerformanceManager.h @@ -106,6 +106,15 @@ public: } } + inline static void add_page_fault_event(Thread& thread, const RegisterState& regs) + { + if (auto* event_buffer = thread.process().current_perf_events_buffer()) { + [[maybe_unused]] auto rc = event_buffer->append_with_eip_and_ebp( + thread.pid(), thread.tid(), + regs.eip, regs.ebp, PERF_EVENT_PAGE_FAULT, 0, 0, 0, nullptr); + } + } + inline static void timer_tick(RegisterState const& regs) { static Time last_wakeup; diff --git a/Kernel/Thread.h b/Kernel/Thread.h index d288d6e07f..ee77fd3bd2 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -1120,6 +1120,16 @@ public: void set_idle_thread() { m_is_idle_thread = true; } bool is_idle_thread() const { return m_is_idle_thread; } + ALWAYS_INLINE u32 enter_profiler() + { + return m_nested_profiler_calls.fetch_add(1, AK::MemoryOrder::memory_order_acq_rel); + } + + ALWAYS_INLINE u32 leave_profiler() + { + return m_nested_profiler_calls.fetch_sub(1, AK::MemoryOrder::memory_order_acquire); + } + private: Thread(NonnullRefPtr, NonnullOwnPtr kernel_stack_region); @@ -1257,6 +1267,7 @@ private: bool m_in_block { false }; bool m_is_idle_thread { false }; Atomic m_have_any_unmasked_pending_signals { false }; + Atomic m_nested_profiler_calls { 0 }; void yield_without_holding_big_lock(); void donate_without_holding_big_lock(RefPtr&, const char*); diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 0a27c8aecb..cf943e659e 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -60,6 +60,7 @@ enum { PERF_EVENT_CONTEXT_SWITCH = 1024, PERF_EVENT_KMALLOC = 2048, PERF_EVENT_KFREE = 4096, + PERF_EVENT_PAGE_FAULT = 8192, }; #define WNOHANG 1 diff --git a/Userland/Libraries/LibC/serenity.h b/Userland/Libraries/LibC/serenity.h index 02fe85e2e4..0557a10f78 100644 --- a/Userland/Libraries/LibC/serenity.h +++ b/Userland/Libraries/LibC/serenity.h @@ -89,6 +89,7 @@ enum { PERF_EVENT_CONTEXT_SWITCH = 1024, PERF_EVENT_KMALLOC = 2048, PERF_EVENT_KFREE = 4096, + PERF_EVENT_PAGE_FAULT = 8192, }; #define PERF_EVENT_MASK_ALL (~0ull) diff --git a/Userland/Utilities/profile.cpp b/Userland/Utilities/profile.cpp index 5b16c08daa..6bce266bac 100644 --- a/Userland/Utilities/profile.cpp +++ b/Userland/Utilities/profile.cpp @@ -44,6 +44,8 @@ int main(int argc, char** argv) event_mask |= PERF_EVENT_KMALLOC; else if (event_type == "kfree") event_mask |= PERF_EVENT_KFREE; + else if (event_type == "page_fault") + event_mask |= PERF_EVENT_PAGE_FAULT; else { warnln("Unknown event type '{}' specified.", event_type); exit(1); @@ -53,7 +55,7 @@ int main(int argc, char** argv) auto print_types = [] { outln(); - outln("Event type can be one of: sample, context_switch, kmalloc and kfree."); + outln("Event type can be one of: sample, context_switch, page_fault, kmalloc and kfree."); }; if (!args_parser.parse(argc, argv, false)) {