mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:32:44 +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
					
				
					 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gunnar Beutner
						Gunnar Beutner