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 <Kernel/Coredump.h>
8#include <Kernel/PerformanceManager.h>
9#include <Kernel/Process.h>
10#include <Kernel/Scheduler.h>
11#include <Kernel/Time/TimeManagement.h>
12
13namespace Kernel {
14
15bool g_profiling_all_threads;
16PerformanceEventBuffer* g_global_perf_events;
17u64 g_profiling_event_mask;
18
19// NOTE: event_mask needs to be passed as a pointer as u64
20// does not fit into a register on 32bit architectures.
21ErrorOr<FlatPtr> Process::sys$profiling_enable(pid_t pid, Userspace<u64 const*> userspace_event_mask)
22{
23 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
24 TRY(require_no_promises());
25
26 auto const event_mask = TRY(copy_typed_from_user(userspace_event_mask));
27 return profiling_enable(pid, event_mask);
28}
29
30// NOTE: This second entrypoint exists to allow the kernel to invoke the syscall to enable boot profiling.
31ErrorOr<FlatPtr> Process::profiling_enable(pid_t pid, u64 event_mask)
32{
33 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
34
35 if (pid == -1) {
36 auto credentials = this->credentials();
37 if (!credentials->is_superuser())
38 return EPERM;
39 ScopedCritical critical;
40 g_profiling_event_mask = PERF_EVENT_PROCESS_CREATE | PERF_EVENT_THREAD_CREATE | PERF_EVENT_MMAP;
41 if (g_global_perf_events) {
42 g_global_perf_events->clear();
43 } else {
44 g_global_perf_events = PerformanceEventBuffer::try_create_with_size(32 * MiB).leak_ptr();
45 if (!g_global_perf_events) {
46 g_profiling_event_mask = 0;
47 return ENOMEM;
48 }
49 }
50
51 SpinlockLocker lock(g_profiling_lock);
52 if (!TimeManagement::the().enable_profile_timer())
53 return ENOTSUP;
54 g_profiling_all_threads = true;
55 PerformanceManager::add_process_created_event(*Scheduler::colonel());
56 TRY(Process::for_each_in_same_jail([](auto& process) -> ErrorOr<void> {
57 PerformanceManager::add_process_created_event(process);
58 return {};
59 }));
60 g_profiling_event_mask = event_mask;
61 return 0;
62 }
63
64 auto process = Process::from_pid_in_same_jail(pid);
65 if (!process)
66 return ESRCH;
67 if (process->is_dead())
68 return ESRCH;
69 auto credentials = this->credentials();
70 auto profile_process_credentials = process->credentials();
71 if (!credentials->is_superuser() && profile_process_credentials->uid() != credentials->euid())
72 return EPERM;
73 SpinlockLocker lock(g_profiling_lock);
74 g_profiling_event_mask = PERF_EVENT_PROCESS_CREATE | PERF_EVENT_THREAD_CREATE | PERF_EVENT_MMAP;
75 process->set_profiling(true);
76 if (!process->create_perf_events_buffer_if_needed()) {
77 process->set_profiling(false);
78 return ENOMEM;
79 }
80 g_profiling_event_mask = event_mask;
81 if (!TimeManagement::the().enable_profile_timer()) {
82 process->set_profiling(false);
83 return ENOTSUP;
84 }
85 return 0;
86}
87
88ErrorOr<FlatPtr> Process::sys$profiling_disable(pid_t pid)
89{
90 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
91 TRY(require_no_promises());
92
93 if (pid == -1) {
94 auto credentials = this->credentials();
95 if (!credentials->is_superuser())
96 return EPERM;
97 ScopedCritical critical;
98 if (!TimeManagement::the().disable_profile_timer())
99 return ENOTSUP;
100 g_profiling_all_threads = false;
101 return 0;
102 }
103
104 auto process = Process::from_pid_in_same_jail(pid);
105 if (!process)
106 return ESRCH;
107 auto credentials = this->credentials();
108 auto profile_process_credentials = process->credentials();
109 if (!credentials->is_superuser() && profile_process_credentials->uid() != credentials->euid())
110 return EPERM;
111 SpinlockLocker lock(g_profiling_lock);
112 if (!process->is_profiling())
113 return EINVAL;
114 // FIXME: If we enabled the profile timer and it's not supported, how do we disable it now?
115 if (!TimeManagement::the().disable_profile_timer())
116 return ENOTSUP;
117 process->set_profiling(false);
118 return 0;
119}
120
121ErrorOr<FlatPtr> Process::sys$profiling_free_buffer(pid_t pid)
122{
123 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
124 TRY(require_no_promises());
125
126 if (pid == -1) {
127 auto credentials = this->credentials();
128 if (!credentials->is_superuser())
129 return EPERM;
130
131 OwnPtr<PerformanceEventBuffer> perf_events;
132
133 {
134 ScopedCritical critical;
135
136 perf_events = adopt_own_if_nonnull(g_global_perf_events);
137 g_global_perf_events = nullptr;
138 }
139
140 return 0;
141 }
142
143 auto process = Process::from_pid_in_same_jail(pid);
144 if (!process)
145 return ESRCH;
146 auto credentials = this->credentials();
147 auto profile_process_credentials = process->credentials();
148 if (!credentials->is_superuser() && profile_process_credentials->uid() != credentials->euid())
149 return EPERM;
150 SpinlockLocker lock(g_profiling_lock);
151 if (process->is_profiling())
152 return EINVAL;
153 process->delete_perf_events_buffer();
154 return 0;
155}
156}