mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-04 05:20:30 +00:00
Kernel+LibC: Add support for filtering profiling events
This adds the -t command-line argument for the profile tool. Using this argument you can filter which event types you want in your profile.
This commit is contained in:
parent
8b2ace0326
commit
572bbf28cc
Notes:
sideshowbarker
2024-07-18 17:45:43 +09:00
Author: https://github.com/gunnarbeutner Commit: https://github.com/SerenityOS/serenity/commit/572bbf28ccd Pull-request: https://github.com/SerenityOS/serenity/pull/7246 Reviewed-by: https://github.com/tomuta
10 changed files with 72 additions and 33 deletions
|
@ -60,6 +60,9 @@ KResult PerformanceEventBuffer::append_with_eip_and_ebp(ProcessID pid, ThreadID
|
|||
if (count() >= capacity())
|
||||
return ENOBUFS;
|
||||
|
||||
if ((g_profiling_event_mask & type) == 0)
|
||||
return EINVAL;
|
||||
|
||||
PerformanceEvent event;
|
||||
event.type = type;
|
||||
event.lost_samples = lost_samples;
|
||||
|
|
|
@ -53,7 +53,7 @@ struct [[gnu::packed]] ContextSwitchPerformanceEvent {
|
|||
};
|
||||
|
||||
struct [[gnu::packed]] PerformanceEvent {
|
||||
u8 type { 0 };
|
||||
u16 type { 0 };
|
||||
u8 stack_size { 0 };
|
||||
u32 pid { 0 };
|
||||
u32 tid { 0 };
|
||||
|
@ -116,5 +116,6 @@ private:
|
|||
|
||||
extern bool g_profiling_all_threads;
|
||||
extern PerformanceEventBuffer* g_global_perf_events;
|
||||
extern u64 g_profiling_event_mask;
|
||||
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ public:
|
|||
KResultOr<int> sys$setkeymap(Userspace<const Syscall::SC_setkeymap_params*>);
|
||||
KResultOr<int> sys$module_load(Userspace<const char*> path, size_t path_length);
|
||||
KResultOr<int> sys$module_unload(Userspace<const char*> name, size_t name_length);
|
||||
KResultOr<int> sys$profiling_enable(pid_t);
|
||||
KResultOr<int> sys$profiling_enable(pid_t, u64);
|
||||
KResultOr<int> sys$profiling_disable(pid_t);
|
||||
KResultOr<int> sys$profiling_free_buffer(pid_t);
|
||||
KResultOr<int> sys$futex(Userspace<const Syscall::SC_futex_params*>);
|
||||
|
|
|
@ -15,8 +15,9 @@ namespace Kernel {
|
|||
|
||||
bool g_profiling_all_threads;
|
||||
PerformanceEventBuffer* g_global_perf_events;
|
||||
u64 g_profiling_event_mask;
|
||||
|
||||
KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
||||
KResultOr<int> Process::sys$profiling_enable(pid_t pid, u64 event_mask)
|
||||
{
|
||||
REQUIRE_NO_PROMISES;
|
||||
|
||||
|
@ -24,6 +25,7 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
|||
if (!is_superuser())
|
||||
return EPERM;
|
||||
ScopedCritical critical;
|
||||
g_profiling_event_mask = event_mask;
|
||||
if (g_global_perf_events)
|
||||
g_global_perf_events->clear();
|
||||
else
|
||||
|
@ -33,6 +35,7 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
|||
if (!TimeManagement::the().enable_profile_timer())
|
||||
return ENOTSUP;
|
||||
g_profiling_all_threads = true;
|
||||
PerformanceManager::add_process_created_event(*Scheduler::colonel());
|
||||
Process::for_each([](auto& process) {
|
||||
PerformanceManager::add_process_created_event(process);
|
||||
return IterationDecision::Continue;
|
||||
|
@ -52,6 +55,7 @@ KResultOr<int> Process::sys$profiling_enable(pid_t pid)
|
|||
return ENOMEM;
|
||||
if (!TimeManagement::the().enable_profile_timer())
|
||||
return ENOTSUP;
|
||||
g_profiling_event_mask = event_mask;
|
||||
process->set_profiling(true);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -47,17 +47,17 @@ enum {
|
|||
};
|
||||
|
||||
enum {
|
||||
PERF_EVENT_SAMPLE,
|
||||
PERF_EVENT_MALLOC,
|
||||
PERF_EVENT_FREE,
|
||||
PERF_EVENT_MMAP,
|
||||
PERF_EVENT_MUNMAP,
|
||||
PERF_EVENT_PROCESS_CREATE,
|
||||
PERF_EVENT_PROCESS_EXEC,
|
||||
PERF_EVENT_PROCESS_EXIT,
|
||||
PERF_EVENT_THREAD_CREATE,
|
||||
PERF_EVENT_THREAD_EXIT,
|
||||
PERF_EVENT_CONTEXT_SWITCH,
|
||||
PERF_EVENT_SAMPLE = 1,
|
||||
PERF_EVENT_MALLOC = 2,
|
||||
PERF_EVENT_FREE = 4,
|
||||
PERF_EVENT_MMAP = 8,
|
||||
PERF_EVENT_MUNMAP = 16,
|
||||
PERF_EVENT_PROCESS_CREATE = 32,
|
||||
PERF_EVENT_PROCESS_EXEC = 64,
|
||||
PERF_EVENT_PROCESS_EXIT = 128,
|
||||
PERF_EVENT_THREAD_CREATE = 256,
|
||||
PERF_EVENT_THREAD_EXIT = 512,
|
||||
PERF_EVENT_CONTEXT_SWITCH = 1024,
|
||||
};
|
||||
|
||||
#define WNOHANG 1
|
||||
|
|
|
@ -287,7 +287,7 @@ void init_stage2(void*)
|
|||
|
||||
if (boot_profiling) {
|
||||
dbgln("Starting full system boot profiling");
|
||||
auto result = Process::current()->sys$profiling_enable(-1);
|
||||
auto result = Process::current()->sys$profiling_enable(-1, ~0ull);
|
||||
VERIFY(!result.is_error());
|
||||
}
|
||||
|
||||
|
|
|
@ -280,7 +280,7 @@ bool generate_profile(pid_t& pid)
|
|||
process_name = "(unknown)";
|
||||
}
|
||||
|
||||
if (profiling_enable(pid) < 0) {
|
||||
if (profiling_enable(pid, PERF_EVENT_MASK_ALL) < 0) {
|
||||
int saved_errno = errno;
|
||||
GUI::MessageBox::show(nullptr, String::formatted("Unable to profile process {}({}): {}", process_name, pid, strerror(saved_errno)), "Profiler", GUI::MessageBox::Type::Error);
|
||||
return false;
|
||||
|
|
|
@ -30,9 +30,9 @@ int module_unload(const char* name, size_t name_length)
|
|||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
int profiling_enable(pid_t pid)
|
||||
int profiling_enable(pid_t pid, uint64_t event_mask)
|
||||
{
|
||||
int rc = syscall(SC_profiling_enable, pid);
|
||||
int rc = syscall(SC_profiling_enable, pid, event_mask);
|
||||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ int disown(pid_t);
|
|||
int module_load(const char* path, size_t path_length);
|
||||
int module_unload(const char* name, size_t name_length);
|
||||
|
||||
int profiling_enable(pid_t);
|
||||
int profiling_enable(pid_t, uint64_t);
|
||||
int profiling_disable(pid_t);
|
||||
int profiling_free_buffer(pid_t);
|
||||
|
||||
|
@ -76,19 +76,21 @@ int futex(uint32_t* userspace_address, int futex_op, uint32_t value, const struc
|
|||
int purge(int mode);
|
||||
|
||||
enum {
|
||||
PERF_EVENT_SAMPLE,
|
||||
PERF_EVENT_MALLOC,
|
||||
PERF_EVENT_FREE,
|
||||
PERF_EVENT_MMAP,
|
||||
PERF_EVENT_MUNMAP,
|
||||
PERF_EVENT_PROCESS_CREATE,
|
||||
PERF_EVENT_PROCESS_EXEC,
|
||||
PERF_EVENT_PROCESS_EXIT,
|
||||
PERF_EVENT_THREAD_CREATE,
|
||||
PERF_EVENT_THREAD_EXIT,
|
||||
PERF_EVENT_CONTEXT_SWITCH,
|
||||
PERF_EVENT_SAMPLE = 1,
|
||||
PERF_EVENT_MALLOC = 2,
|
||||
PERF_EVENT_FREE = 4,
|
||||
PERF_EVENT_MMAP = 8,
|
||||
PERF_EVENT_MUNMAP = 16,
|
||||
PERF_EVENT_PROCESS_CREATE = 32,
|
||||
PERF_EVENT_PROCESS_EXEC = 64,
|
||||
PERF_EVENT_PROCESS_EXIT = 128,
|
||||
PERF_EVENT_THREAD_CREATE = 256,
|
||||
PERF_EVENT_THREAD_EXIT = 512,
|
||||
PERF_EVENT_CONTEXT_SWITCH = 1024,
|
||||
};
|
||||
|
||||
#define PERF_EVENT_MASK_ALL (~0ull)
|
||||
|
||||
int perf_event(int type, uintptr_t arg1, uintptr_t arg2);
|
||||
|
||||
int get_stack_bounds(uintptr_t* user_stack_base, size_t* user_stack_size);
|
||||
|
|
|
@ -21,6 +21,9 @@ int main(int argc, char** argv)
|
|||
bool enable = false;
|
||||
bool disable = false;
|
||||
bool all_processes = false;
|
||||
u64 event_mask = PERF_EVENT_MMAP | PERF_EVENT_MUNMAP | PERF_EVENT_PROCESS_CREATE
|
||||
| PERF_EVENT_PROCESS_EXEC | PERF_EVENT_PROCESS_EXIT | PERF_EVENT_THREAD_CREATE | PERF_EVENT_THREAD_EXIT;
|
||||
bool seen_event_type_arg = false;
|
||||
|
||||
args_parser.add_option(pid_argument, "Target PID", nullptr, 'p', "PID");
|
||||
args_parser.add_option(all_processes, "Profile all processes (super-user only)", nullptr, 'a');
|
||||
|
@ -29,14 +32,40 @@ int main(int argc, char** argv)
|
|||
args_parser.add_option(free, "Free the profiling buffer for the associated process(es).", nullptr, 'f');
|
||||
args_parser.add_option(wait, "Enable profiling and wait for user input to disable.", nullptr, 'w');
|
||||
args_parser.add_option(cmd_argument, "Command", nullptr, 'c', "command");
|
||||
args_parser.add_option(Core::ArgsParser::Option {
|
||||
true, "Enable tracking specific event type", nullptr, 't', "event_type",
|
||||
[&](String event_type) {
|
||||
seen_event_type_arg = true;
|
||||
if (event_type == "sample")
|
||||
event_mask |= PERF_EVENT_SAMPLE;
|
||||
else if (event_type == "context_switch")
|
||||
event_mask |= PERF_EVENT_CONTEXT_SWITCH;
|
||||
else {
|
||||
warnln("Unknown event type '{}' specified.", event_type);
|
||||
exit(1);
|
||||
}
|
||||
return true;
|
||||
} });
|
||||
|
||||
args_parser.parse(argc, argv);
|
||||
auto print_types = [] {
|
||||
outln();
|
||||
outln("Event type can be one of: sample and context_switch.");
|
||||
};
|
||||
|
||||
if (!args_parser.parse(argc, argv, false)) {
|
||||
print_types();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!pid_argument && !cmd_argument && !all_processes) {
|
||||
args_parser.print_usage(stdout, argv[0]);
|
||||
print_types();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!seen_event_type_arg)
|
||||
event_mask |= PERF_EVENT_SAMPLE;
|
||||
|
||||
if (pid_argument || all_processes) {
|
||||
if (!(enable ^ disable ^ wait ^ free)) {
|
||||
fprintf(stderr, "-p <PID> requires -e xor -d xor -w xor -f.\n");
|
||||
|
@ -46,7 +75,7 @@ int main(int argc, char** argv)
|
|||
pid_t pid = all_processes ? -1 : atoi(pid_argument);
|
||||
|
||||
if (wait || enable) {
|
||||
if (profiling_enable(pid) < 0) {
|
||||
if (profiling_enable(pid, event_mask) < 0) {
|
||||
perror("profiling_enable");
|
||||
return 1;
|
||||
}
|
||||
|
@ -85,7 +114,7 @@ int main(int argc, char** argv)
|
|||
cmd_argv.append(nullptr);
|
||||
|
||||
dbgln("Enabling profiling for PID {}", getpid());
|
||||
profiling_enable(getpid());
|
||||
profiling_enable(getpid(), event_mask);
|
||||
if (execvp(cmd_argv[0], const_cast<char**>(cmd_argv.data())) < 0) {
|
||||
perror("execv");
|
||||
return 1;
|
||||
|
|
Loading…
Reference in a new issue