Serenity Operating System
at master 156 lines 5.2 kB view raw
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}