2020-01-18 09:38:21 +01:00
|
|
|
/*
|
2021-03-21 16:00:52 +01:00
|
|
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
2020-01-18 09:38:21 +01:00
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 09:38:21 +01:00
|
|
|
*/
|
|
|
|
|
|
2020-02-22 11:01:37 +01:00
|
|
|
#include <LibCore/ArgsParser.h>
|
2022-02-12 21:57:44 +02:00
|
|
|
#include <LibCore/System.h>
|
|
|
|
|
#include <LibMain/Main.h>
|
2019-12-11 20:36:56 +01:00
|
|
|
#include <serenity.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
2023-05-02 22:26:16 +01:00
|
|
|
static Optional<pid_t> determine_pid_to_profile(StringView pid_argument, bool all_processes);
|
|
|
|
|
|
2022-02-12 21:57:44 +02:00
|
|
|
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
2019-12-11 20:36:56 +01:00
|
|
|
{
|
2020-02-22 11:01:37 +01:00
|
|
|
Core::ArgsParser args_parser;
|
|
|
|
|
|
2022-07-11 20:42:03 +00:00
|
|
|
StringView pid_argument {};
|
2022-11-28 20:49:33 +00:00
|
|
|
Vector<StringView> command;
|
2021-04-18 21:02:50 -07:00
|
|
|
bool wait = false;
|
2021-04-18 21:25:43 -07:00
|
|
|
bool free = false;
|
2020-02-22 11:01:37 +01:00
|
|
|
bool enable = false;
|
|
|
|
|
bool disable = false;
|
2021-03-21 16:00:52 +01:00
|
|
|
bool all_processes = false;
|
2021-05-14 08:10:43 +02:00
|
|
|
u64 event_mask = PERF_EVENT_MMAP | PERF_EVENT_MUNMAP | PERF_EVENT_PROCESS_CREATE
|
2021-08-11 20:04:46 +02:00
|
|
|
| PERF_EVENT_PROCESS_EXEC | PERF_EVENT_PROCESS_EXIT | PERF_EVENT_THREAD_CREATE | PERF_EVENT_THREAD_EXIT
|
|
|
|
|
| PERF_EVENT_SIGNPOST;
|
2021-05-14 08:10:43 +02:00
|
|
|
bool seen_event_type_arg = false;
|
2020-02-22 11:01:37 +01:00
|
|
|
|
|
|
|
|
args_parser.add_option(pid_argument, "Target PID", nullptr, 'p', "PID");
|
2022-10-14 21:56:19 +03:00
|
|
|
args_parser.add_option(all_processes, "Profile all processes (super-user only), result at /sys/kernel/profile", nullptr, 'a');
|
2020-02-22 11:01:37 +01:00
|
|
|
args_parser.add_option(enable, "Enable", nullptr, 'e');
|
|
|
|
|
args_parser.add_option(disable, "Disable", nullptr, 'd');
|
2021-04-18 21:25:43 -07:00
|
|
|
args_parser.add_option(free, "Free the profiling buffer for the associated process(es).", nullptr, 'f');
|
2021-04-18 21:02:50 -07:00
|
|
|
args_parser.add_option(wait, "Enable profiling and wait for user input to disable.", nullptr, 'w');
|
2021-05-14 08:10:43 +02:00
|
|
|
args_parser.add_option(Core::ArgsParser::Option {
|
2022-07-12 22:13:38 +02:00
|
|
|
Core::ArgsParser::OptionArgumentMode::Required,
|
|
|
|
|
"Enable tracking specific event type", nullptr, 't', "event_type",
|
2022-12-04 18:02:33 +00:00
|
|
|
[&](DeprecatedString event_type) {
|
2021-05-14 08:10:43 +02:00
|
|
|
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;
|
2021-05-13 13:09:00 +02:00
|
|
|
else if (event_type == "kmalloc")
|
|
|
|
|
event_mask |= PERF_EVENT_KMALLOC;
|
|
|
|
|
else if (event_type == "kfree")
|
|
|
|
|
event_mask |= PERF_EVENT_KFREE;
|
2021-05-18 02:26:11 -07:00
|
|
|
else if (event_type == "page_fault")
|
|
|
|
|
event_mask |= PERF_EVENT_PAGE_FAULT;
|
2021-08-10 21:02:59 +02:00
|
|
|
else if (event_type == "syscall")
|
|
|
|
|
event_mask |= PERF_EVENT_SYSCALL;
|
2022-01-18 23:32:29 +01:00
|
|
|
else if (event_type == "read")
|
|
|
|
|
event_mask |= PERF_EVENT_READ;
|
2021-05-14 08:10:43 +02:00
|
|
|
else {
|
|
|
|
|
warnln("Unknown event type '{}' specified.", event_type);
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} });
|
2022-11-28 20:49:33 +00:00
|
|
|
args_parser.add_positional_argument(command, "Command to profile", "command", Core::ArgsParser::Required::No);
|
|
|
|
|
args_parser.set_stop_on_first_non_option(true);
|
2021-05-14 08:10:43 +02:00
|
|
|
|
|
|
|
|
auto print_types = [] {
|
|
|
|
|
outln();
|
2022-02-12 17:53:23 +01:00
|
|
|
outln("Event type can be one of: sample, context_switch, page_fault, syscall, read, kmalloc and kfree.");
|
2021-05-14 08:10:43 +02:00
|
|
|
};
|
2020-02-22 11:01:37 +01:00
|
|
|
|
2022-02-12 21:57:44 +02:00
|
|
|
if (!args_parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage)) {
|
2021-05-14 08:10:43 +02:00
|
|
|
print_types();
|
2021-10-26 21:38:10 +02:00
|
|
|
exit(0);
|
2021-05-14 08:10:43 +02:00
|
|
|
}
|
2020-02-22 11:01:37 +01:00
|
|
|
|
2022-11-28 20:49:33 +00:00
|
|
|
if (pid_argument.is_empty() && command.is_empty() && !all_processes) {
|
2023-02-21 15:14:41 +03:30
|
|
|
args_parser.print_usage(stdout, arguments.strings[0]);
|
2021-05-14 08:10:43 +02:00
|
|
|
print_types();
|
2019-12-11 20:36:56 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 08:10:43 +02:00
|
|
|
if (!seen_event_type_arg)
|
|
|
|
|
event_mask |= PERF_EVENT_SAMPLE;
|
|
|
|
|
|
2022-07-11 20:42:03 +00:00
|
|
|
if (!pid_argument.is_empty() || all_processes) {
|
2021-04-18 21:25:43 -07:00
|
|
|
if (!(enable ^ disable ^ wait ^ free)) {
|
2023-05-02 22:12:56 +01:00
|
|
|
warnln("-a and -p <PID> requires -e xor -d xor -w xor -f.");
|
2020-02-22 11:01:37 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-02 22:26:16 +01:00
|
|
|
auto pid_opt = determine_pid_to_profile(pid_argument, all_processes);
|
|
|
|
|
if (!pid_opt.has_value()) {
|
|
|
|
|
warnln("-p <PID> requires an integer value.");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2019-12-11 20:36:56 +01:00
|
|
|
|
2023-05-02 22:26:16 +01:00
|
|
|
pid_t pid = pid_opt.value();
|
2021-04-18 21:02:50 -07:00
|
|
|
if (wait || enable) {
|
2022-02-12 21:57:44 +02:00
|
|
|
TRY(Core::System::profiling_enable(pid, event_mask));
|
2021-04-18 21:02:50 -07:00
|
|
|
|
|
|
|
|
if (!wait)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wait) {
|
|
|
|
|
outln("Profiling enabled, waiting for user input to disable...");
|
|
|
|
|
(void)getchar();
|
2020-02-22 11:01:37 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-12 21:57:44 +02:00
|
|
|
if (wait || disable)
|
|
|
|
|
TRY(Core::System::profiling_disable(pid));
|
2021-04-18 21:25:43 -07:00
|
|
|
|
2022-02-12 21:57:44 +02:00
|
|
|
if (free)
|
|
|
|
|
TRY(Core::System::profiling_free_buffer(pid));
|
2020-02-22 11:01:37 +01:00
|
|
|
|
2019-12-11 20:36:56 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-09 15:09:40 +01:00
|
|
|
dbgln("Enabling profiling for PID {}", getpid());
|
2022-02-12 21:57:44 +02:00
|
|
|
TRY(Core::System::profiling_enable(getpid(), event_mask));
|
2022-11-28 20:49:33 +00:00
|
|
|
TRY(Core::System::exec(command[0], command, Core::System::SearchInPath::Yes));
|
2020-02-22 11:01:37 +01:00
|
|
|
|
2019-12-11 20:36:56 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
2023-05-02 22:26:16 +01:00
|
|
|
|
|
|
|
|
static Optional<pid_t> determine_pid_to_profile(StringView pid_argument, bool all_processes)
|
|
|
|
|
{
|
|
|
|
|
if (all_processes) {
|
|
|
|
|
return { -1 };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pid_argument is guaranteed to have a value
|
|
|
|
|
return pid_argument.to_int();
|
|
|
|
|
}
|