Serenity Operating System
1/*
2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibCore/ArgsParser.h>
8#include <LibCore/System.h>
9#include <LibMain/Main.h>
10#include <serenity.h>
11#include <stdio.h>
12#include <stdlib.h>
13
14ErrorOr<int> serenity_main(Main::Arguments arguments)
15{
16 Core::ArgsParser args_parser;
17
18 StringView pid_argument {};
19 Vector<StringView> command;
20 bool wait = false;
21 bool free = false;
22 bool enable = false;
23 bool disable = false;
24 bool all_processes = false;
25 u64 event_mask = PERF_EVENT_MMAP | PERF_EVENT_MUNMAP | PERF_EVENT_PROCESS_CREATE
26 | PERF_EVENT_PROCESS_EXEC | PERF_EVENT_PROCESS_EXIT | PERF_EVENT_THREAD_CREATE | PERF_EVENT_THREAD_EXIT
27 | PERF_EVENT_SIGNPOST;
28 bool seen_event_type_arg = false;
29
30 args_parser.add_option(pid_argument, "Target PID", nullptr, 'p', "PID");
31 args_parser.add_option(all_processes, "Profile all processes (super-user only), result at /sys/kernel/profile", nullptr, 'a');
32 args_parser.add_option(enable, "Enable", nullptr, 'e');
33 args_parser.add_option(disable, "Disable", nullptr, 'd');
34 args_parser.add_option(free, "Free the profiling buffer for the associated process(es).", nullptr, 'f');
35 args_parser.add_option(wait, "Enable profiling and wait for user input to disable.", nullptr, 'w');
36 args_parser.add_option(Core::ArgsParser::Option {
37 Core::ArgsParser::OptionArgumentMode::Required,
38 "Enable tracking specific event type", nullptr, 't', "event_type",
39 [&](DeprecatedString event_type) {
40 seen_event_type_arg = true;
41 if (event_type == "sample")
42 event_mask |= PERF_EVENT_SAMPLE;
43 else if (event_type == "context_switch")
44 event_mask |= PERF_EVENT_CONTEXT_SWITCH;
45 else if (event_type == "kmalloc")
46 event_mask |= PERF_EVENT_KMALLOC;
47 else if (event_type == "kfree")
48 event_mask |= PERF_EVENT_KFREE;
49 else if (event_type == "page_fault")
50 event_mask |= PERF_EVENT_PAGE_FAULT;
51 else if (event_type == "syscall")
52 event_mask |= PERF_EVENT_SYSCALL;
53 else if (event_type == "read")
54 event_mask |= PERF_EVENT_READ;
55 else {
56 warnln("Unknown event type '{}' specified.", event_type);
57 exit(1);
58 }
59 return true;
60 } });
61 args_parser.add_positional_argument(command, "Command to profile", "command", Core::ArgsParser::Required::No);
62 args_parser.set_stop_on_first_non_option(true);
63
64 auto print_types = [] {
65 outln();
66 outln("Event type can be one of: sample, context_switch, page_fault, syscall, read, kmalloc and kfree.");
67 };
68
69 if (!args_parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage)) {
70 print_types();
71 exit(0);
72 }
73
74 if (pid_argument.is_empty() && command.is_empty() && !all_processes) {
75 args_parser.print_usage(stdout, arguments.strings[0]);
76 print_types();
77 return 0;
78 }
79
80 if (!seen_event_type_arg)
81 event_mask |= PERF_EVENT_SAMPLE;
82
83 if (!pid_argument.is_empty() || all_processes) {
84 if (!(enable ^ disable ^ wait ^ free)) {
85 warnln("-p <PID> requires -e xor -d xor -w xor -f.");
86 return 1;
87 }
88
89 // FIXME: Handle error case.
90 pid_t pid = all_processes ? -1 : pid_argument.to_int().release_value();
91
92 if (wait || enable) {
93 TRY(Core::System::profiling_enable(pid, event_mask));
94
95 if (!wait)
96 return 0;
97 }
98
99 if (wait) {
100 outln("Profiling enabled, waiting for user input to disable...");
101 (void)getchar();
102 }
103
104 if (wait || disable)
105 TRY(Core::System::profiling_disable(pid));
106
107 if (free)
108 TRY(Core::System::profiling_free_buffer(pid));
109
110 return 0;
111 }
112
113 dbgln("Enabling profiling for PID {}", getpid());
114 TRY(Core::System::profiling_enable(getpid(), event_mask));
115 TRY(Core::System::exec(command[0], command, Core::System::SearchInPath::Yes));
116
117 return 0;
118}