summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-01-11 09:52:18 +0100
committerAndreas Kling <kling@serenityos.org>2021-01-11 11:36:00 +0100
commit5dafb723700c798bc54515a33a3572e01a092812 (patch)
tree80692b22f17a44a171282c7c1647ce4e580b99d7
parentf259d96871d66a4e764a43613ab0e9866a22e182 (diff)
downloadserenity-5dafb723700c798bc54515a33a3572e01a092812.zip
Kernel+Profiler: Make profiling per-process and without core dumps
This patch merges the profiling functionality in the kernel with the performance events mechanism. A profiler sample is now just another perf event, rather than a dedicated thing. Since perf events were already per-process, this now makes profiling per-process as well. Processes with perf events would already write out a perfcore.PID file to the current directory on death, but since we may want to profile a process and then let it continue running, recorded perf events can now be accessed at any time via /proc/PID/perf_events. This patch also adds information about process memory regions to the perfcore JSON format. This removes the need to supply a core dump to the Profiler app for symbolication, and so the "profiler coredump" mechanism is removed entirely. There's still a hard limit of 4MB worth of perf events per process, so this is by no means a perfect final design, but it's a nice step forward for both simplicity and stability. Fixes #4848 Fixes #4849
-rw-r--r--DevTools/Profiler/DisassemblyModel.cpp6
-rw-r--r--DevTools/Profiler/Profile.cpp82
-rw-r--r--DevTools/Profiler/Profile.h34
-rw-r--r--DevTools/Profiler/main.cpp10
-rw-r--r--Kernel/CMakeLists.txt1
-rw-r--r--Kernel/FileSystem/ProcFS.cpp45
-rw-r--r--Kernel/PerformanceEventBuffer.cpp45
-rw-r--r--Kernel/PerformanceEventBuffer.h12
-rw-r--r--Kernel/Process.cpp21
-rw-r--r--Kernel/Process.h4
-rw-r--r--Kernel/Profiling.cpp110
-rw-r--r--Kernel/Profiling.h59
-rw-r--r--Kernel/Scheduler.cpp16
-rw-r--r--Kernel/Syscalls/execve.cpp10
-rw-r--r--Kernel/Syscalls/perf_event.cpp6
-rw-r--r--Kernel/Syscalls/profiling.cpp23
-rw-r--r--Kernel/Thread.cpp6
-rw-r--r--Kernel/UnixTypes.h1
-rw-r--r--Libraries/LibC/serenity.h1
-rw-r--r--Services/SystemServer/main.cpp15
20 files changed, 196 insertions, 311 deletions
diff --git a/DevTools/Profiler/DisassemblyModel.cpp b/DevTools/Profiler/DisassemblyModel.cpp
index 77f74585bd..4195f103cd 100644
--- a/DevTools/Profiler/DisassemblyModel.cpp
+++ b/DevTools/Profiler/DisassemblyModel.cpp
@@ -68,13 +68,13 @@ DisassemblyModel::DisassemblyModel(Profile& profile, ProfileNode& node)
kernel_elf = make<ELF::Image>((const u8*)m_kernel_file->data(), m_kernel_file->size());
elf = kernel_elf.ptr();
} else {
- auto library_data = profile.coredump().library_containing(node.address());
+ auto library_data = profile.libraries().library_containing(node.address());
if (!library_data) {
dbgln("no library data");
return;
}
- elf = &library_data->lib_elf;
- base_address = library_data->base_address;
+ elf = &library_data->elf;
+ base_address = library_data->base;
}
ASSERT(elf != nullptr);
diff --git a/DevTools/Profiler/Profile.cpp b/DevTools/Profiler/Profile.cpp
index bf2caa7f08..308fdcc7ac 100644
--- a/DevTools/Profiler/Profile.cpp
+++ b/DevTools/Profiler/Profile.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -46,20 +46,10 @@ static void sort_profile_nodes(Vector<NonnullRefPtr<ProfileNode>>& nodes)
child->sort_children();
}
-static String symbolicate_from_coredump(CoreDump::Reader& coredump, u32 ptr, [[maybe_unused]] u32& offset)
-{
- auto library_data = coredump.library_containing(ptr);
- if (!library_data) {
- dbgln("could not symbolicate: {:p}", ptr);
- return "??";
- }
- return String::formatted("[{}] {}", library_data->name, library_data->lib_elf.symbolicate(ptr - library_data->base_address, &offset));
-}
-
-Profile::Profile(String executable_path, NonnullOwnPtr<CoreDump::Reader>&& coredump, Vector<Event> events)
+Profile::Profile(String executable_path, Vector<Event> events, NonnullOwnPtr<LibraryMetadata> library_metadata)
: m_executable_path(move(executable_path))
- , m_coredump(move(coredump))
, m_events(move(events))
+ , m_library_metadata(move(library_metadata))
{
m_first_timestamp = m_events.first().timestamp;
m_last_timestamp = m_events.last().timestamp;
@@ -226,10 +216,6 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
if (!pid.is_u32())
return String { "Invalid perfcore format (no process ID)" };
- auto coredump = CoreDump::Reader::create(String::formatted("/tmp/profiler_coredumps/{}", pid.as_u32()));
- if (!coredump)
- return String { "Could not open coredump" };
-
auto file_or_error = MappedFile::map("/boot/Kernel");
OwnPtr<ELF::Image> kernel_elf;
if (!file_or_error.is_error())
@@ -239,10 +225,16 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
if (!events_value.is_array())
return String { "Malformed profile (events is not an array)" };
+ auto regions_value = object.get("regions");
+ if (!regions_value.is_array() || regions_value.as_array().is_empty())
+ return String { "Malformed profile (regions is not an array, or it is empty)" };
+
auto& perf_events = events_value.as_array();
if (perf_events.is_empty())
return String { "No events captured (targeted process was never on CPU)" };
+ auto library_metadata = make<LibraryMetadata>(regions_value.as_array());
+
Vector<Event> events;
for (auto& perf_event_value : perf_events.values()) {
@@ -274,7 +266,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
symbol = "??";
}
} else {
- symbol = symbolicate_from_coredump(*coredump, ptr, offset);
+ symbol = library_metadata->symbolicate(ptr, offset);
}
event.frames.append({ symbol, ptr, offset });
@@ -289,7 +281,7 @@ Result<NonnullOwnPtr<Profile>, String> Profile::load_from_perfcore_file(const St
events.append(move(event));
}
- return adopt_own(*new Profile(executable_path, coredump.release_nonnull(), move(events)));
+ return adopt_own(*new Profile(executable_path, move(events), move(library_metadata)));
}
void ProfileNode::sort_children()
@@ -353,3 +345,55 @@ GUI::Model* Profile::disassembly_model()
{
return m_disassembly_model;
}
+
+Profile::LibraryMetadata::LibraryMetadata(JsonArray regions)
+ : m_regions(move(regions))
+{
+ for (auto& region_value : m_regions.values()) {
+ auto& region = region_value.as_object();
+ auto base = region.get("base").as_u32();
+ auto size = region.get("size").as_u32();
+ auto name = region.get("name").as_string();
+
+ String path;
+ if (name.contains("Loader.so"))
+ path = "Loader.so";
+ else if (!name.contains(":"))
+ continue;
+ else
+ path = name.substring(0, name.view().find_first_of(":").value());
+
+ if (name.contains(".so"))
+ path = String::formatted("/usr/lib/{}", path);
+
+ auto file_or_error = MappedFile::map(path);
+ if (file_or_error.is_error()) {
+ m_libraries.set(name, nullptr);
+ continue;
+ }
+ auto elf = ELF::Image(file_or_error.value()->bytes());
+ if (!elf.is_valid())
+ continue;
+ auto library = make<Library>(base, size, name, file_or_error.release_value(), move(elf));
+ m_libraries.set(name, move(library));
+ }
+}
+
+const Profile::LibraryMetadata::Library* Profile::LibraryMetadata::library_containing(FlatPtr ptr) const
+{
+ for (auto& it : m_libraries) {
+ if (!it.value)
+ continue;
+ auto& library = *it.value;
+ if (ptr >= library.base && ptr < (library.base + library.size))
+ return &library;
+ }
+ return nullptr;
+}
+
+String Profile::LibraryMetadata::symbolicate(FlatPtr ptr, u32& offset) const
+{
+ if (auto* library = library_containing(ptr))
+ return String::formatted("[{}] {}", library->name, library->elf.symbolicate(ptr - library->base, &offset));
+ return "??";
+}
diff --git a/DevTools/Profiler/Profile.h b/DevTools/Profiler/Profile.h
index 79c618a306..0acf3e917f 100644
--- a/DevTools/Profiler/Profile.h
+++ b/DevTools/Profiler/Profile.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -30,10 +30,11 @@
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
+#include <AK/MappedFile.h>
#include <AK/NonnullRefPtrVector.h>
#include <AK/OwnPtr.h>
#include <AK/Result.h>
-#include <LibCoreDump/Reader.h>
+#include <LibELF/Image.h>
#include <LibGUI/Forward.h>
#include <LibGUI/ModelIndex.h>
@@ -177,15 +178,36 @@ public:
void set_show_percentages(bool);
const String& executable_path() const { return m_executable_path; }
- const CoreDump::Reader& coredump() const { return *m_coredump; }
+
+ class LibraryMetadata {
+ public:
+ LibraryMetadata(JsonArray regions);
+
+ String symbolicate(FlatPtr ptr, u32& offset) const;
+
+ struct Library {
+ FlatPtr base;
+ size_t size;
+ String name;
+ NonnullRefPtr<MappedFile> file;
+ ELF::Image elf;
+ };
+
+ const Library* library_containing(FlatPtr) const;
+
+ private:
+ mutable HashMap<String, OwnPtr<Library>> m_libraries;
+ JsonArray m_regions;
+ };
+
+ const LibraryMetadata& libraries() const { return *m_library_metadata; }
private:
- Profile(String executable_path, NonnullOwnPtr<CoreDump::Reader>&&, Vector<Event>);
+ Profile(String executable_path, Vector<Event>, NonnullOwnPtr<LibraryMetadata>);
void rebuild_tree();
String m_executable_path;
- NonnullOwnPtr<CoreDump::Reader> m_coredump;
RefPtr<ProfileModel> m_model;
RefPtr<DisassemblyModel> m_disassembly_model;
@@ -199,6 +221,8 @@ private:
Vector<Event> m_events;
+ NonnullOwnPtr<LibraryMetadata> m_library_metadata;
+
bool m_has_timestamp_filter_range { false };
u64 m_timestamp_filter_range_start { 0 };
u64 m_timestamp_filter_range_end { 0 };
diff --git a/DevTools/Profiler/main.cpp b/DevTools/Profiler/main.cpp
index fcef34ffbc..5f801936c0 100644
--- a/DevTools/Profiler/main.cpp
+++ b/DevTools/Profiler/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,7 +50,7 @@
#include <stdio.h>
#include <string.h>
-static bool generate_profile(pid_t specified_pid);
+static bool generate_profile(pid_t& pid);
int main(int argc, char** argv)
{
@@ -62,11 +62,11 @@ int main(int argc, char** argv)
auto app = GUI::Application::construct(argc, argv);
auto app_icon = GUI::Icon::default_icon("app-profiler");
- const char* path = nullptr;
+ String path;
if (argc != 2) {
if (!generate_profile(pid))
return 0;
- path = "/proc/profile";
+ path = String::formatted("/proc/{}/perf_events", pid);
} else {
path = argv[1];
}
@@ -182,7 +182,7 @@ static bool prompt_to_stop_profiling(pid_t pid, const String& process_name)
return GUI::Application::the()->exec() == 0;
}
-bool generate_profile(pid_t pid)
+bool generate_profile(pid_t& pid)
{
if (!pid) {
auto process_chooser = GUI::ProcessChooser::construct("Profiler", "Profile", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-profiler.png"));
diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt
index 5b1c8b11f3..c3268d5a67 100644
--- a/Kernel/CMakeLists.txt
+++ b/Kernel/CMakeLists.txt
@@ -96,7 +96,6 @@ set(KERNEL_SOURCES
PerformanceEventBuffer.cpp
Process.cpp
ProcessGroup.cpp
- Profiling.cpp
Ptrace.cpp
RTC.cpp
Random.cpp
diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp
index 4d4b566b80..87ec817a34 100644
--- a/Kernel/FileSystem/ProcFS.cpp
+++ b/Kernel/FileSystem/ProcFS.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -51,8 +51,8 @@
#include <Kernel/Net/TCPSocket.h>
#include <Kernel/Net/UDPSocket.h>
#include <Kernel/PCI/Access.h>
+#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
-#include <Kernel/Profiling.h>
#include <Kernel/Scheduler.h>
#include <Kernel/StdLib.h>
#include <Kernel/TTY/TTY.h>
@@ -113,6 +113,7 @@ enum ProcFileType {
FI_PID,
__FI_PID_Start,
+ FI_PID_perf_events,
FI_PID_vm,
FI_PID_vmobjects,
FI_PID_stacks, // directory
@@ -460,35 +461,21 @@ static bool procfs$modules(InodeIdentifier, KBufferBuilder& builder)
return true;
}
-static bool procfs$profile(InodeIdentifier, KBufferBuilder& builder)
+static bool procfs$pid_perf_events(InodeIdentifier identifier, KBufferBuilder& builder)
{
+ auto process = Process::from_pid(to_pid(identifier));
+ if (!process)
+ return false;
+
InterruptDisabler disabler;
- JsonObjectSerializer object(builder);
- object.add("pid", Profiling::pid().value());
- object.add("executable", Profiling::executable_path());
-
- auto array = object.add_array("events");
- bool mask_kernel_addresses = !Process::current()->is_superuser();
- Profiling::for_each_sample([&](auto& sample) {
- auto object = array.add_object();
- object.add("type", "sample");
- object.add("tid", sample.tid.value());
- object.add("timestamp", sample.timestamp);
- auto frames_array = object.add_array("stack");
- for (size_t i = 0; i < Profiling::max_stack_frame_count; ++i) {
- if (sample.frames[i] == 0)
- break;
- u32 address = (u32)sample.frames[i];
- if (mask_kernel_addresses && !is_user_address(VirtualAddress(address)))
- address = 0xdeadc0de;
- frames_array.add(address);
- }
- frames_array.finish();
- });
- array.finish();
- object.finish();
- return true;
+ if (!process->executable())
+ return false;
+
+ if (!process->perf_events())
+ return false;
+
+ return process->perf_events()->to_json(builder, process->pid(), process->executable()->absolute_path());
}
static bool procfs$net_adapters(InodeIdentifier, KBufferBuilder& builder)
@@ -1752,7 +1739,6 @@ 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, false, procfs$profile };
m_entries[FI_Root_sys] = { "sys", FI_Root_sys, true };
m_entries[FI_Root_net] = { "net", FI_Root_net, false };
@@ -1770,6 +1756,7 @@ ProcFS::ProcFS()
m_entries[FI_PID_cwd] = { "cwd", FI_PID_cwd, false, procfs$pid_cwd };
m_entries[FI_PID_unveil] = { "unveil", FI_PID_unveil, false, procfs$pid_unveil };
m_entries[FI_PID_root] = { "root", FI_PID_root, false, procfs$pid_root };
+ m_entries[FI_PID_perf_events] = { "perf_events", FI_PID_perf_events, false, procfs$pid_perf_events };
m_entries[FI_PID_fd] = { "fd", FI_PID_fd, false };
}
diff --git a/Kernel/PerformanceEventBuffer.cpp b/Kernel/PerformanceEventBuffer.cpp
index 51c35c051e..b564c2ee35 100644
--- a/Kernel/PerformanceEventBuffer.cpp
+++ b/Kernel/PerformanceEventBuffer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,11 +29,12 @@
#include <AK/JsonObjectSerializer.h>
#include <Kernel/KBufferBuilder.h>
#include <Kernel/PerformanceEventBuffer.h>
+#include <Kernel/Process.h>
namespace Kernel {
PerformanceEventBuffer::PerformanceEventBuffer()
- : m_buffer(KBuffer::try_create_with_size(4 * MiB))
+ : m_buffer(KBuffer::try_create_with_size(4 * MiB, Region::Access::Read | Region::Access::Write, "Performance events", AllocationStrategy::AllocateNow))
{
}
@@ -46,18 +47,14 @@ KResult PerformanceEventBuffer::append(int type, FlatPtr arg1, FlatPtr arg2)
event.type = type;
switch (type) {
+ case PERF_EVENT_SAMPLE:
+ break;
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);
@@ -76,11 +73,6 @@ KResult PerformanceEventBuffer::append(int type, FlatPtr arg1, FlatPtr arg2)
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));
-#ifdef VERY_DEBUG
- for (size_t i = 0; i < event.stack_size; ++i)
- dbg() << " " << (void*)event.stack[i];
-#endif
-
event.timestamp = TimeManagement::the().uptime_ms();
at(m_count++) = event;
return KSuccess;
@@ -96,16 +88,40 @@ PerformanceEvent& PerformanceEventBuffer::at(size_t index)
OwnPtr<KBuffer> PerformanceEventBuffer::to_json(ProcessID pid, const String& executable_path) const
{
KBufferBuilder builder;
+ if (!to_json(builder, pid, executable_path))
+ return nullptr;
+ return builder.build();
+}
+
+bool PerformanceEventBuffer::to_json(KBufferBuilder& builder, ProcessID pid, const String& executable_path) const
+{
+ auto process = Process::from_pid(pid);
+ ASSERT(process);
+ ScopedSpinLock locker(process->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->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);
auto event_object = array.add_object();
switch (event.type) {
+ case PERF_EVENT_SAMPLE:
+ event_object.add("type", "sample");
+ break;
case PERF_EVENT_MALLOC:
event_object.add("type", "malloc");
event_object.add("ptr", static_cast<u64>(event.data.malloc.ptr));
@@ -116,6 +132,7 @@ OwnPtr<KBuffer> PerformanceEventBuffer::to_json(ProcessID pid, const String& exe
event_object.add("ptr", static_cast<u64>(event.data.free.ptr));
break;
}
+ event_object.add("tid", event.tid);
event_object.add("timestamp", event.timestamp);
auto stack_array = event_object.add_array("stack");
for (size_t j = 0; j < event.stack_size; ++j) {
@@ -126,7 +143,7 @@ OwnPtr<KBuffer> PerformanceEventBuffer::to_json(ProcessID pid, const String& exe
}
array.finish();
object.finish();
- return builder.build();
+ return true;
}
}
diff --git a/Kernel/PerformanceEventBuffer.h b/Kernel/PerformanceEventBuffer.h
index 5e78489c12..19844b7103 100644
--- a/Kernel/PerformanceEventBuffer.h
+++ b/Kernel/PerformanceEventBuffer.h
@@ -31,6 +31,8 @@
namespace Kernel {
+class KBufferBuilder;
+
struct [[gnu::packed]] MallocPerformanceEvent {
size_t size;
FlatPtr ptr;
@@ -44,12 +46,14 @@ struct [[gnu::packed]] FreePerformanceEvent {
struct [[gnu::packed]] PerformanceEvent {
u8 type { 0 };
u8 stack_size { 0 };
+ u32 tid { 0 };
u64 timestamp;
union {
MallocPerformanceEvent malloc;
FreePerformanceEvent free;
} data;
- FlatPtr stack[32];
+ static constexpr size_t max_stack_frame_count = 32;
+ FlatPtr stack[max_stack_frame_count];
};
class PerformanceEventBuffer {
@@ -58,6 +62,11 @@ public:
KResult append(int type, FlatPtr arg1, FlatPtr arg2);
+ void clear()
+ {
+ m_count = 0;
+ }
+
size_t capacity() const
{
if (!m_buffer)
@@ -71,6 +80,7 @@ public:
}
OwnPtr<KBuffer> to_json(ProcessID, const String& executable_path) const;
+ bool to_json(KBufferBuilder&, ProcessID, const String& executable_path) const;
private:
PerformanceEvent& at(size_t index);
diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp
index a270b8c198..2e5b981692 100644
--- a/Kernel/Process.cpp
+++ b/Kernel/Process.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -597,16 +597,6 @@ void Process::finalize()
dbg() << "Finalizing process " << *this;
#endif
- if (is_profiling()) {
- auto coredump = CoreDump::create(*this, String::formatted("/tmp/profiler_coredumps/{}", pid().value()));
- if (coredump) {
- auto result = coredump->write();
- if (result.is_error())
- dbgln("Core dump generation failed: {}", result.error());
- } else {
- dbgln("Could not create coredump");
- }
- }
if (m_should_dump_core) {
dbgln("Generating coredump for pid: {}", m_pid.value());
@@ -622,7 +612,7 @@ void Process::finalize()
}
if (m_perf_event_buffer) {
- auto description_or_error = VFS::the().open(String::format("perfcore.%d", m_pid), O_CREAT | O_EXCL, 0400, current_directory(), UidAndGid { m_uid, m_gid });
+ auto description_or_error = VFS::the().open(String::formatted("perfcore.{}", m_pid), 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() : "");
@@ -922,4 +912,11 @@ void Process::tracer_trap(Thread& thread, const RegisterState& regs)
thread.send_urgent_signal_to_self(SIGTRAP);
}
+PerformanceEventBuffer& Process::ensure_perf_events()
+{
+ if (!m_perf_event_buffer)
+ m_perf_event_buffer = make<PerformanceEventBuffer>();
+ return *m_perf_event_buffer;
+}
+
}
diff --git a/Kernel/Process.h b/Kernel/Process.h
index e5f7fbdf77..b8462908e8 100644
--- a/Kernel/Process.h
+++ b/Kernel/Process.h
@@ -509,11 +509,15 @@ public:
const HashMap<String, String>& coredump_metadata() const { return m_coredump_metadata; }
+ PerformanceEventBuffer* perf_events() { return m_perf_event_buffer; }
+
private:
friend class MemoryManager;
friend class Scheduler;
friend class Region;
+ PerformanceEventBuffer& ensure_perf_events();
+
Process(RefPtr<Thread>& first_thread, const String& name, uid_t, gid_t, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> cwd = nullptr, RefPtr<Custody> executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr);
static ProcessID allocate_pid();
diff --git a/Kernel/Profiling.cpp b/Kernel/Profiling.cpp
deleted file mode 100644
index 08278a5c01..0000000000
--- a/Kernel/Profiling.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * 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 <AK/Demangle.h>
-#include <AK/Singleton.h>
-#include <AK/StringBuilder.h>
-#include <Kernel/FileSystem/Custody.h>
-#include <Kernel/KBuffer.h>
-#include <Kernel/KSyms.h>
-#include <Kernel/Process.h>
-#include <Kernel/Profiling.h>
-
-namespace Kernel {
-
-namespace Profiling {
-
-static size_t s_slot_count;
-static AK::Singleton<KBuffer, []() -> KBuffer* {
- auto buffer = KBuffer::try_create_with_size(8 * MiB, Region::Access::Read | Region::Access::Write, "Profiling Buffer", AllocationStrategy::AllocateNow);
- s_slot_count = buffer->size() / sizeof(Sample);
- return buffer.leak_ptr();
-}>
- s_profiling_buffer;
-static size_t s_next_slot_index;
-static ProcessID s_pid { -1 };
-
-String& executable_path()
-{
- static String* path;
- if (!path)
- path = new String;
- return *path;
-}
-
-ProcessID pid()
-{
- return s_pid;
-}
-
-void start(Process& process)
-{
- if (process.executable())
- executable_path() = process.executable()->absolute_path().impl();
- else
- executable_path() = {};
- s_pid = process.pid();
-
- s_profiling_buffer.ensure_instance();
-
- s_next_slot_index = 0;
-}
-
-static Sample& sample_slot(size_t index)
-{
- return ((Sample*)s_profiling_buffer->data())[index];
-}
-
-Sample& next_sample_slot()
-{
- auto& slot = sample_slot(s_next_slot_index++);
- if (s_next_slot_index >= s_slot_count)
- s_next_slot_index = 0;
- return slot;
-}
-
-void stop()
-{
- // FIXME: This probably shouldn't be empty.
-}
-
-void did_exec(const String& new_executable_path)
-{
- executable_path() = new_executable_path;
- s_next_slot_index = 0;
-}
-
-void for_each_sample(Function<void(Sample&)> callback)
-{
- for (size_t i = 0; i < s_next_slot_index; ++i) {
- auto& sample = sample_slot(i);
- callback(sample);
- }
-}
-
-}
-
-}
diff --git a/Kernel/Profiling.h b/Kernel/Profiling.h
deleted file mode 100644
index fa7d682b68..0000000000
--- a/Kernel/Profiling.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
- * 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 <AK/Function.h>
-#include <AK/String.h>
-#include <AK/Types.h>
-
-namespace Kernel {
-
-class Process;
-
-namespace Profiling {
-
-constexpr size_t max_stack_frame_count = 50;
-
-struct Sample {
- ProcessID pid;
- ThreadID tid;
- u64 timestamp;
- u32 frames[max_stack_frame_count];
-};
-
-extern ProcessID pid();
-extern String& executable_path();
-
-Sample& next_sample_slot();
-void start(Process&);
-void stop();
-void did_exec(const String& new_executable_path);
-void for_each_sample(Function<void(Sample&)>);
-
-}
-
-}
diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp
index 7e7aac64b3..ac5e573399 100644
--- a/Kernel/Scheduler.cpp
+++ b/Kernel/Scheduler.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,8 +28,8 @@
#include <AK/ScopeGuard.h>
#include <AK/TemporaryChange.h>
#include <AK/Time.h>
+#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
-#include <Kernel/Profiling.h>
#include <Kernel/RTC.h>
#include <Kernel/Scheduler.h>
#include <Kernel/Time/TimeManagement.h>
@@ -477,15 +477,9 @@ void Scheduler::timer_tick(const RegisterState& regs)
if (!is_bsp)
return; // TODO: This prevents scheduling on other CPUs!
if (current_thread->process().is_profiling()) {
- SmapDisabler disabler;
- auto backtrace = current_thread->raw_backtrace(regs.ebp, regs.eip);
- auto& sample = Profiling::next_sample_slot();
- sample.pid = current_thread->process().pid();
- sample.tid = current_thread->tid();
- sample.timestamp = TimeManagement::the().uptime_ms();
- for (size_t i = 0; i < min(backtrace.size(), Profiling::max_stack_frame_count); ++i) {
- sample.frames[i] = backtrace[i];
- }
+ ASSERT(current_thread->process().perf_events());
+ auto& perf_events = *current_thread->process().perf_events();
+ [[maybe_unused]] auto rc = perf_events.append(PERF_EVENT_SAMPLE, 0, 0);
}
if (current_thread->tick((regs.cs & 3) == 0))
diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp
index b02c45bd77..7a42d2366f 100644
--- a/Kernel/Syscalls/execve.cpp
+++ b/Kernel/Syscalls/execve.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,8 +29,8 @@
#include <AK/TemporaryChange.h>
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/FileDescription.h>
+#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
-#include <Kernel/Profiling.h>
#include <Kernel/Random.h>
#include <Kernel/Time/TimeManagement.h>
#include <Kernel/VM/AllocationStrategy.h>
@@ -446,7 +446,6 @@ int Process::do_exec(NonnullRefPtr<FileDescription> main_program_description, Ve
return -ENOENT;
// Disable profiling temporarily in case it's running on this process.
- bool was_profiling = is_profiling();
TemporaryChange profiling_disabler(m_profiling, false);
// Mark this thread as the current thread that does exec
@@ -589,8 +588,9 @@ int Process::do_exec(NonnullRefPtr<FileDescription> main_program_description, Ve
tss.cr3 = m_page_directory->cr3();
tss.ss2 = m_pid.value();
- if (was_profiling)
- Profiling::did_exec(path);
+ // Throw away any recorded performance events in this process.
+ if (m_perf_event_buffer)
+ m_perf_event_buffer->clear();
{
ScopedSpinLock lock(g_scheduler_lock);
diff --git a/Kernel/Syscalls/perf_event.cpp b/Kernel/Syscalls/perf_event.cpp
index f1b50c722e..d7f9ea584a 100644
--- a/Kernel/Syscalls/perf_event.cpp
+++ b/Kernel/Syscalls/perf_event.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,7 @@ namespace Kernel {
int Process::sys$perf_event(int type, FlatPtr arg1, FlatPtr arg2)
{
- if (!m_perf_event_buffer)
- m_perf_event_buffer = make<PerformanceEventBuffer>();
- return m_perf_event_buffer->append(type, arg1, arg2);
+ return ensure_perf_events().append(type, arg1, arg2);
}
}
diff --git a/Kernel/Syscalls/profiling.cpp b/Kernel/Syscalls/profiling.cpp
index 9e9ba0c56b..7d69f6ba22 100644
--- a/Kernel/Syscalls/profiling.cpp
+++ b/Kernel/Syscalls/profiling.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,8 +25,10 @@
*/
#include <Kernel/CoreDump.h>
+#include <Kernel/FileSystem/FileDescription.h>
+#include <Kernel/FileSystem/VirtualFileSystem.h>
+#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
-#include <Kernel/Profiling.h>
namespace Kernel {
@@ -41,7 +43,7 @@ int Process::sys$profiling_enable(pid_t pid)
return -ESRCH;
if (!is_superuser() && process->uid() != m_uid)
return -EPERM;
- Profiling::start(*process);
+ process->ensure_perf_events();
process->set_profiling(true);
return 0;
}
@@ -54,20 +56,9 @@ int Process::sys$profiling_disable(pid_t pid)
return -ESRCH;
if (!is_superuser() && process->uid() != m_uid)
return -EPERM;
+ if (!process->is_profiling())
+ return -EINVAL;
process->set_profiling(false);
- Profiling::stop();
-
- // We explicitly unlock here because we can't hold the lock when writing the coredump VFS
- lock.unlock();
-
- if (auto coredump = CoreDump::create(*process, String::formatted("/tmp/profiler_coredumps/{}", pid))) {
- auto result = coredump->write();
- if (result.is_error())
- return result.error();
- } else {
- // FIXME: Return an error maybe?
- dbgln("Unable to create profiler coredump for PID {}", pid);
- }
return 0;
}
diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp
index 8bb6664ff8..71283101f0 100644
--- a/Kernel/Thread.cpp
+++ b/Kernel/Thread.cpp
@@ -31,8 +31,8 @@
#include <Kernel/Arch/i386/CPU.h>
#include <Kernel/FileSystem/FileDescription.h>
#include <Kernel/KSyms.h>
+#include <Kernel/PerformanceEventBuffer.h>
#include <Kernel/Process.h>
-#include <Kernel/Profiling.h>
#include <Kernel/Scheduler.h>
#include <Kernel/Thread.h>
#include <Kernel/ThreadTracer.h>
@@ -1036,7 +1036,7 @@ Vector<FlatPtr> Thread::raw_backtrace(FlatPtr ebp, FlatPtr eip) const
InterruptDisabler disabler;
auto& process = const_cast<Process&>(this->process());
ProcessPagingScope paging_scope(process);
- Vector<FlatPtr, Profiling::max_stack_frame_count> backtrace;
+ Vector<FlatPtr, PerformanceEvent::max_stack_frame_count> backtrace;
backtrace.append(eip);
FlatPtr stack_ptr_copy;
FlatPtr stack_ptr = (FlatPtr)ebp;
@@ -1048,7 +1048,7 @@ Vector<FlatPtr> Thread::raw_backtrace(FlatPtr ebp, FlatPtr eip) const
if (!safe_memcpy(&retaddr, (void*)(stack_ptr + sizeof(FlatPtr)), sizeof(FlatPtr), fault_at))
break;
backtrace.append(retaddr);
- if (backtrace.size() == Profiling::max_stack_frame_count)
+ if (backtrace.size() == PerformanceEvent::max_stack_frame_count)
break;
stack_ptr = stack_ptr_copy;
}
diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h
index 0665d464f6..cc5c9fe9dc 100644
--- a/Kernel/UnixTypes.h
+++ b/Kernel/UnixTypes.h
@@ -63,6 +63,7 @@ enum {
_SC_OPEN_MAX
};
+#define PERF_EVENT_SAMPLE 0
#define PERF_EVENT_MALLOC 1
#define PERF_EVENT_FREE 2
diff --git a/Libraries/LibC/serenity.h b/Libraries/LibC/serenity.h
index e68b391d31..4654476469 100644
--- a/Libraries/LibC/serenity.h
+++ b/Libraries/LibC/serenity.h
@@ -65,6 +65,7 @@ int futex(int32_t* userspace_address, int futex_op, int32_t value, const struct
int purge(int mode);
+#define PERF_EVENT_SAMPLE 0
#define PERF_EVENT_MALLOC 1
#define PERF_EVENT_FREE 2
diff --git a/Services/SystemServer/main.cpp b/Services/SystemServer/main.cpp
index e0f11ac4b6..f243032b95 100644
--- a/Services/SystemServer/main.cpp
+++ b/Services/SystemServer/main.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -205,18 +205,6 @@ static void create_tmp_coredump_directory()
umask(old_umask);
}
-static void create_tmp_profiler_coredumps_directory()
-{
- dbgln("Creating /tmp/profiler_coredumps directory");
- auto old_umask = umask(0);
- auto rc = mkdir("/tmp/profiler_coredumps", 0755);
- if (rc < 0) {
- perror("mkdir(/tmp/profiler_coredumps)");
- ASSERT_NOT_REACHED();
- }
- umask(old_umask);
-}
-
int main(int, char**)
{
prepare_devfs();
@@ -229,7 +217,6 @@ int main(int, char**)
mount_all_filesystems();
create_tmp_rpc_directory();
create_tmp_coredump_directory();
- create_tmp_profiler_coredumps_directory();
parse_boot_mode();
Core::EventLoop event_loop;