diff --git a/Kernel/Makefile b/Kernel/Makefile index 92ccf9467e..e648405877 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -75,6 +75,7 @@ OBJS = \ PCI/IOAccess.o \ PCI/MMIOAccess.o \ PCI/Initializer.o \ + PerformanceEventBuffer.o \ Process.o \ ProcessTracer.o \ Profiling.o \ diff --git a/Kernel/PerformanceEventBuffer.cpp b/Kernel/PerformanceEventBuffer.cpp new file mode 100644 index 0000000000..dc0618101e --- /dev/null +++ b/Kernel/PerformanceEventBuffer.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +PerformanceEventBuffer::PerformanceEventBuffer() + : m_buffer(KBuffer::create_with_size(4 * MB)) +{ +} + +KResult PerformanceEventBuffer::append(int type, uintptr_t arg1, uintptr_t arg2) +{ + if (count() >= capacity()) + return KResult(-ENOBUFS); + + PerformanceEvent event; + event.type = type; + + switch (type) { + case PERF_EVENT_MALLOC: + event.data.malloc.size = arg1; + event.data.malloc.ptr = arg2; +#ifdef VERY_DEBUG + dbg() << "PERF_EVENT_MALLOC: " << (void*)event.data.malloc.ptr << " (" << event.data.malloc.size << ")"; +#endif + break; + case PERF_EVENT_FREE: + event.data.free.ptr = arg1; +#ifdef VERY_DEBUG + dbg() << "PERF_EVENT_FREE: " << (void*)event.data.free.ptr; +#endif + break; + default: + return KResult(-EINVAL); + } + + uintptr_t ebp; + asm volatile("movl %%ebp, %%eax" + : "=a"(ebp)); + //copy_from_user(&ebp, (uintptr_t*)current->get_register_dump_from_stack().ebp); + Vector backtrace; + { + SmapDisabler disabler; + backtrace = current->raw_backtrace(ebp); + } + event.stack_size = min(sizeof(event.stack) / sizeof(uintptr_t), static_cast(backtrace.size())); + memcpy(event.stack, backtrace.data(), event.stack_size * sizeof(uintptr_t)); + +#ifdef VERY_DEBUG + for (size_t i = 0; i < event.stack_size; ++i) + dbg() << " " << (void*)event.stack[i]; +#endif + + event.timestamp = g_uptime; + at(m_count++) = event; + return KSuccess; +} + +PerformanceEvent& PerformanceEventBuffer::at(size_t index) +{ + ASSERT(index < capacity()); + auto* events = reinterpret_cast(m_buffer.data()); + return events[index]; +} + +KBuffer PerformanceEventBuffer::to_json(pid_t pid, const String& executable_path) const +{ + KBufferBuilder builder; + + JsonObjectSerializer object(builder); + object.add("pid", pid); + object.add("executable", executable_path); + + auto array = object.add_array("events"); + for (size_t i = 0; i < m_count; ++i) { + auto& event = at(i); + auto object = array.add_object(); + switch (event.type) { + case PERF_EVENT_MALLOC: + object.add("type", "malloc"); + object.add("ptr", static_cast(event.data.malloc.ptr)); + object.add("size", static_cast(event.data.malloc.size)); + break; + case PERF_EVENT_FREE: + object.add("type", "free"); + object.add("ptr", static_cast(event.data.free.ptr)); + break; + } + object.add("timestamp", event.timestamp); + auto stack_array = object.add_array("stack"); + for (size_t j = 0; j < event.stack_size; ++j) { + stack_array.add(event.stack[j]); + } + stack_array.finish(); + object.finish(); + } + array.finish(); + object.finish(); + return builder.build(); +} diff --git a/Kernel/PerformanceEventBuffer.h b/Kernel/PerformanceEventBuffer.h new file mode 100644 index 0000000000..eb4f24baed --- /dev/null +++ b/Kernel/PerformanceEventBuffer.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +struct [[gnu::packed]] MallocPerformanceEvent { + size_t size; + uintptr_t ptr; +}; + +struct [[gnu::packed]] FreePerformanceEvent { + size_t size; + uintptr_t ptr; +}; + +struct [[gnu::packed]] PerformanceEvent { + u8 type { 0 }; + u8 stack_size { 0 }; + u64 timestamp; + union { + MallocPerformanceEvent malloc; + FreePerformanceEvent free; + } data; + uintptr_t stack[32]; +}; + +class PerformanceEventBuffer { +public: + PerformanceEventBuffer(); + + KResult append(int type, uintptr_t arg1, uintptr_t arg2); + + size_t capacity() const { return m_buffer.size() / sizeof(PerformanceEvent); } + size_t count() const { return m_count; } + const PerformanceEvent& at(size_t index) const + { + return const_cast(*this).at(index); + } + + KBuffer to_json(pid_t, const String& executable_path) const; + +private: + PerformanceEvent& at(size_t index); + + size_t m_count { 0 }; + KBuffer m_buffer; +}; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index acac5fb086..484147c12c 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -2940,6 +2940,15 @@ void Process::finalize() dbg() << "Finalizing process " << *this; #endif + if (m_perf_event_buffer) { + auto description_or_error = VFS::the().open("perfcore", O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { m_uid, m_gid }); + if (!description_or_error.is_error()) { + auto& description = description_or_error.value(); + auto json = m_perf_event_buffer->to_json(m_pid, m_executable ? m_executable->absolute_path() : ""); + description->write(json.data(), json.size()); + } + } + m_fds.clear(); m_tty = nullptr; m_executable = nullptr; @@ -4670,3 +4679,10 @@ int Process::sys$unveil(const Syscall::SC_unveil_params* user_params) m_veil_state = VeilState::Dropped; return 0; } + +int Process::sys$perf_event(int type, uintptr_t arg1, uintptr_t arg2) +{ + if (!m_perf_event_buffer) + m_perf_event_buffer = make(); + return m_perf_event_buffer->append(type, arg1, arg2); +} diff --git a/Kernel/Process.h b/Kernel/Process.h index 0e05096c7f..9dc4538fb7 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -301,6 +302,7 @@ public: int sys$chroot(const char* path, size_t path_length, int mount_flags); int sys$pledge(const Syscall::SC_pledge_params*); int sys$unveil(const Syscall::SC_unveil_params*); + int sys$perf_event(int type, uintptr_t arg1, uintptr_t arg2); template int get_sock_or_peer_name(const Params&); @@ -511,6 +513,8 @@ private: WaitQueue& futex_queue(i32*); HashMap> m_futex_queues; + + OwnPtr m_perf_event_buffer; }; class ProcessInspectionHandle { diff --git a/Kernel/Syscall.h b/Kernel/Syscall.h index a5541ab68c..19f755a99e 100644 --- a/Kernel/Syscall.h +++ b/Kernel/Syscall.h @@ -175,7 +175,8 @@ typedef u32 socklen_t; __ENUMERATE_SYSCALL(set_process_boost) \ __ENUMERATE_SYSCALL(chroot) \ __ENUMERATE_SYSCALL(pledge) \ - __ENUMERATE_SYSCALL(unveil) + __ENUMERATE_SYSCALL(unveil) \ + __ENUMERATE_SYSCALL(perf_event) namespace Syscall { diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 6b5492f7ac..1c638e6910 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -28,6 +28,9 @@ #include +#define PERF_EVENT_MALLOC 1 +#define PERF_EVENT_FREE 2 + #define WNOHANG 1 #define WUNTRACED 2 #define WSTOPPED WUNTRACED