mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:32:46 +00:00 
			
		
		
		
	Kernel+ProfileViewer: Display additional filesystem events
This commit is contained in:
		
							parent
							
								
									c184a0786f
								
							
						
					
					
						commit
						54e79aa1d9
					
				
					 16 changed files with 655 additions and 135 deletions
				
			
		|  | @ -32,7 +32,7 @@ enum { | ||||||
|     PERF_EVENT_PAGE_FAULT = 8192, |     PERF_EVENT_PAGE_FAULT = 8192, | ||||||
|     PERF_EVENT_SYSCALL = 16384, |     PERF_EVENT_SYSCALL = 16384, | ||||||
|     PERF_EVENT_SIGNPOST = 32768, |     PERF_EVENT_SIGNPOST = 32768, | ||||||
|     PERF_EVENT_READ = 65536, |     PERF_EVENT_FILESYSTEM = 65536, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define PERF_EVENT_MASK_ALL (~0ull) | #define PERF_EVENT_MASK_ALL (~0ull) | ||||||
|  |  | ||||||
|  | @ -309,6 +309,7 @@ set(KERNEL_SOURCES | ||||||
|     Syscalls/prctl.cpp |     Syscalls/prctl.cpp | ||||||
|     Syscalls/process.cpp |     Syscalls/process.cpp | ||||||
|     Syscalls/profiling.cpp |     Syscalls/profiling.cpp | ||||||
|  |     Syscalls/profiled_syscalls.cpp | ||||||
|     Syscalls/ptrace.cpp |     Syscalls/ptrace.cpp | ||||||
|     Syscalls/purge.cpp |     Syscalls/purge.cpp | ||||||
|     Syscalls/read.cpp |     Syscalls/read.cpp | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| ErrorOr<FlatPtr> Process::sys$open(Userspace<Syscall::SC_open_params const*> user_params) | ErrorOr<FlatPtr> Process::open_impl(Userspace<Syscall::SC_open_params const*> user_params) | ||||||
| { | { | ||||||
|     VERIFY_NO_PROCESS_BIG_LOCK(this); |     VERIFY_NO_PROCESS_BIG_LOCK(this); | ||||||
|     auto params = TRY(copy_typed_from_user(user_params)); |     auto params = TRY(copy_typed_from_user(user_params)); | ||||||
|  | @ -68,7 +68,7 @@ ErrorOr<FlatPtr> Process::sys$open(Userspace<Syscall::SC_open_params const*> use | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<FlatPtr> Process::sys$close(int fd) | ErrorOr<FlatPtr> Process::close_impl(int fd) | ||||||
| { | { | ||||||
|     VERIFY_NO_PROCESS_BIG_LOCK(this); |     VERIFY_NO_PROCESS_BIG_LOCK(this); | ||||||
|     TRY(require_promise(Pledge::stdio)); |     TRY(require_promise(Pledge::stdio)); | ||||||
|  |  | ||||||
							
								
								
									
										274
									
								
								Kernel/Syscalls/profiled_syscalls.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								Kernel/Syscalls/profiled_syscalls.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,274 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022-2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <Kernel/FileSystem/OpenFileDescription.h> | ||||||
|  | #include <Kernel/Tasks/PerformanceManager.h> | ||||||
|  | #include <Kernel/Tasks/Process.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | ErrorOr<NonnullRefPtr<OpenFileDescription>> open_readable_file_description(auto& fds, int fd); | ||||||
|  | 
 | ||||||
|  | static ErrorOr<size_t> get_path_index(auto& fds, int fd, PerformanceEventBuffer* event_buffer) | ||||||
|  | { | ||||||
|  |     auto description = TRY(open_readable_file_description(fds, fd)); | ||||||
|  | 
 | ||||||
|  |     if (auto path = description->original_absolute_path(); !path.is_error()) { | ||||||
|  |         return TRY(event_buffer->register_string(move(path.value()))); | ||||||
|  |     } else if (auto pseudo_path = description->pseudo_path(); !pseudo_path.is_error()) { | ||||||
|  |         return TRY(event_buffer->register_string(move(pseudo_path.value()))); | ||||||
|  |     } else { | ||||||
|  |         auto invalid_path_string = TRY(KString::try_create("<INVALID_FILE_PATH>"sv)); // TODO: Performance, unecessary allocations.
 | ||||||
|  |         return TRY(event_buffer->register_string(move(invalid_path_string))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FIXME: Following functions are very similar, and could be refactored into a more generic solution.
 | ||||||
|  | //        However, it's not clear how to do it in a way that would be easy to read and understand.
 | ||||||
|  | ErrorOr<FlatPtr> Process::sys$open(Userspace<Syscall::SC_open_params const*> user_params) | ||||||
|  | { | ||||||
|  |     Optional<MonotonicTime> start_timestamp = {}; | ||||||
|  | 
 | ||||||
|  |     // We have to check whether profiling is enabled at before going into the syscall implementation
 | ||||||
|  |     // so that we can measure the time it took to execute the syscall.
 | ||||||
|  |     // This approach ensures that we don't have a race condition in case profiling was enabled during
 | ||||||
|  |     // the execution of the syscall.
 | ||||||
|  |     // If profiling is disabled at the beginning, we don't want to call TimeManagement::the().monotonic_time()
 | ||||||
|  |     // because of the overhead it would introduce for every syscall.
 | ||||||
|  |     bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); | ||||||
|  |     if (profiling_enabled_at_entry) { | ||||||
|  |         start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto const params = TRY(copy_typed_from_user(user_params)); | ||||||
|  |     auto result = open_impl(user_params); | ||||||
|  | 
 | ||||||
|  |     if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto* event_buffer = current_perf_events_buffer(); | ||||||
|  |     if (event_buffer == nullptr) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     auto const duration = end_timestamp - start_timestamp.value(); | ||||||
|  | 
 | ||||||
|  |     FilesystemEvent data; | ||||||
|  |     data.type = FilesystemEventType::Open; | ||||||
|  |     data.durationNs = static_cast<u64>(duration.to_nanoseconds()); | ||||||
|  | 
 | ||||||
|  |     if (result.is_error()) { | ||||||
|  |         data.result.is_error = true; | ||||||
|  |         data.result.value = result.error().code(); | ||||||
|  |     } else { | ||||||
|  |         data.result.is_error = false; | ||||||
|  |         data.result.value = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto path = get_syscall_path_argument(params.path); | ||||||
|  |     if (!path.is_error()) { | ||||||
|  |         auto value = event_buffer->register_string(move(path.value())); | ||||||
|  |         data.data.open.filename_index = value.value(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     data.data.open.dirfd = params.dirfd; | ||||||
|  |     data.data.open.options = params.options; | ||||||
|  |     data.data.open.mode = params.mode; | ||||||
|  | 
 | ||||||
|  |     (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<FlatPtr> Process::sys$close(int fd) | ||||||
|  | { | ||||||
|  |     Optional<MonotonicTime> start_timestamp = {}; | ||||||
|  | 
 | ||||||
|  |     bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); | ||||||
|  |     if (profiling_enabled_at_entry) { | ||||||
|  |         start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto result = close_impl(fd); | ||||||
|  |     if (Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto* event_buffer = current_perf_events_buffer(); | ||||||
|  |     if (event_buffer == nullptr) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     auto const duration = end_timestamp - start_timestamp.value(); | ||||||
|  | 
 | ||||||
|  |     FilesystemEvent data; | ||||||
|  |     data.type = FilesystemEventType::Close; | ||||||
|  |     data.durationNs = static_cast<u64>(duration.to_nanoseconds()); | ||||||
|  |     data.data.close.fd = fd; | ||||||
|  | 
 | ||||||
|  |     if (result.is_error()) { | ||||||
|  |         data.result.is_error = true; | ||||||
|  |         data.result.value = result.error().code(); | ||||||
|  |     } else { | ||||||
|  |         data.result.is_error = false; | ||||||
|  |         data.result.value = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto maybe_path_index = get_path_index(fds(), fd, event_buffer); | ||||||
|  |     if (maybe_path_index.is_error()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     data.data.close.filename_index = maybe_path_index.value(); | ||||||
|  | 
 | ||||||
|  |     (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<FlatPtr> Process::sys$readv(int fd, Userspace<const struct iovec*> iov, int iov_count) | ||||||
|  | { | ||||||
|  |     Optional<MonotonicTime> start_timestamp = {}; | ||||||
|  | 
 | ||||||
|  |     bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); | ||||||
|  |     if (profiling_enabled_at_entry) { | ||||||
|  |         start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto result = readv_impl(fd, iov, iov_count); | ||||||
|  | 
 | ||||||
|  |     if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     if (Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto* event_buffer = current_perf_events_buffer(); | ||||||
|  |     if (event_buffer == nullptr) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     auto const duration = end_timestamp - start_timestamp.value(); | ||||||
|  | 
 | ||||||
|  |     FilesystemEvent data; | ||||||
|  |     data.type = FilesystemEventType::Readv; | ||||||
|  |     data.durationNs = static_cast<u64>(duration.to_nanoseconds()); | ||||||
|  |     data.data.readv.fd = fd; | ||||||
|  | 
 | ||||||
|  |     if (result.is_error()) { | ||||||
|  |         data.result.is_error = true; | ||||||
|  |         data.result.value = result.error().code(); | ||||||
|  |     } else { | ||||||
|  |         data.result.is_error = false; | ||||||
|  |         data.result.value = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto maybe_path_index = get_path_index(fds(), fd, event_buffer); | ||||||
|  |     if (maybe_path_index.is_error()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     data.data.readv.filename_index = maybe_path_index.value(); | ||||||
|  | 
 | ||||||
|  |     (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<FlatPtr> Process::sys$read(int fd, Userspace<u8*> buffer, size_t size) | ||||||
|  | { | ||||||
|  |     Optional<MonotonicTime> start_timestamp = {}; | ||||||
|  | 
 | ||||||
|  |     bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); | ||||||
|  |     if (profiling_enabled_at_entry) { | ||||||
|  |         start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto result = read_impl(fd, buffer, size); | ||||||
|  | 
 | ||||||
|  |     if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto* event_buffer = current_perf_events_buffer(); | ||||||
|  |     if (event_buffer == nullptr) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     auto const duration = end_timestamp - start_timestamp.value(); | ||||||
|  | 
 | ||||||
|  |     FilesystemEvent data; | ||||||
|  |     data.type = FilesystemEventType::Read; | ||||||
|  |     data.durationNs = static_cast<u64>(duration.to_nanoseconds()); | ||||||
|  |     data.data.read.fd = fd; | ||||||
|  | 
 | ||||||
|  |     if (result.is_error()) { | ||||||
|  |         data.result.is_error = true; | ||||||
|  |         data.result.value = result.error().code(); | ||||||
|  |     } else { | ||||||
|  |         data.result.is_error = false; | ||||||
|  |         data.result.value = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto maybe_path_index = get_path_index(fds(), fd, event_buffer); | ||||||
|  |     if (maybe_path_index.is_error()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     data.data.read.filename_index = maybe_path_index.value(); | ||||||
|  | 
 | ||||||
|  |     (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<FlatPtr> Process::sys$pread(int fd, Userspace<u8*> buffer, size_t size, off_t userspace_offset) | ||||||
|  | { | ||||||
|  |     Optional<MonotonicTime> start_timestamp = {}; | ||||||
|  | 
 | ||||||
|  |     bool const profiling_enabled_at_entry = !Thread::current()->is_profiling_suppressed(); | ||||||
|  |     if (profiling_enabled_at_entry) { | ||||||
|  |         start_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto result = pread_impl(fd, buffer, size, userspace_offset); | ||||||
|  | 
 | ||||||
|  |     if (!profiling_enabled_at_entry || Thread::current()->is_profiling_suppressed()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto* event_buffer = current_perf_events_buffer(); | ||||||
|  |     if (event_buffer == nullptr) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     auto const end_timestamp = TimeManagement::the().monotonic_time(TimePrecision::Precise); | ||||||
|  |     auto const duration = end_timestamp - start_timestamp.value(); | ||||||
|  | 
 | ||||||
|  |     FilesystemEvent data; | ||||||
|  |     data.type = FilesystemEventType::Pread; | ||||||
|  |     data.durationNs = static_cast<u64>(duration.to_nanoseconds()); | ||||||
|  |     data.data.pread.fd = fd; | ||||||
|  |     data.data.pread.buffer_ptr = buffer.ptr(); | ||||||
|  |     data.data.pread.size = size; | ||||||
|  |     data.data.pread.offset = userspace_offset; | ||||||
|  | 
 | ||||||
|  |     if (result.is_error()) { | ||||||
|  |         data.result.is_error = true; | ||||||
|  |         data.result.value = result.error().code(); | ||||||
|  |     } else { | ||||||
|  |         data.result.is_error = false; | ||||||
|  |         data.result.value = 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto maybe_path_index = get_path_index(fds(), fd, event_buffer); | ||||||
|  |     if (maybe_path_index.is_error()) | ||||||
|  |         return result; | ||||||
|  | 
 | ||||||
|  |     data.data.pread.filename_index = maybe_path_index.value(); | ||||||
|  | 
 | ||||||
|  |     (void)event_buffer->append(PERF_EVENT_FILESYSTEM, 0, 0, {}, Thread::current(), data); | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -13,7 +13,7 @@ namespace Kernel { | ||||||
| 
 | 
 | ||||||
| using BlockFlags = Thread::FileBlocker::BlockFlags; | using BlockFlags = Thread::FileBlocker::BlockFlags; | ||||||
| 
 | 
 | ||||||
| static ErrorOr<NonnullRefPtr<OpenFileDescription>> open_readable_file_description(auto& fds, int fd) | ErrorOr<NonnullRefPtr<OpenFileDescription>> open_readable_file_description(auto& fds, int fd) | ||||||
| { | { | ||||||
|     auto description = TRY(fds.with_shared([&](auto& fds) { return fds.open_file_description(fd); })); |     auto description = TRY(fds.with_shared([&](auto& fds) { return fds.open_file_description(fd); })); | ||||||
|     if (!description->is_readable()) |     if (!description->is_readable()) | ||||||
|  | @ -38,7 +38,7 @@ static ErrorOr<void> check_blocked_read(OpenFileDescription* description) | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<FlatPtr> Process::sys$readv(int fd, Userspace<const struct iovec*> iov, int iov_count) | ErrorOr<FlatPtr> Process::readv_impl(int fd, Userspace<const struct iovec*> iov, int iov_count) | ||||||
| { | { | ||||||
|     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); |     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); | ||||||
|     TRY(require_promise(Pledge::stdio)); |     TRY(require_promise(Pledge::stdio)); | ||||||
|  | @ -71,20 +71,6 @@ ErrorOr<FlatPtr> Process::sys$readv(int fd, Userspace<const struct iovec*> iov, | ||||||
|     return nread; |     return nread; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<FlatPtr> Process::sys$read(int fd, Userspace<u8*> buffer, size_t size) |  | ||||||
| { |  | ||||||
|     auto const start_timestamp = TimeManagement::the().uptime_ms(); |  | ||||||
|     auto result = read_impl(fd, buffer, size); |  | ||||||
| 
 |  | ||||||
|     if (Thread::current()->is_profiling_suppressed()) |  | ||||||
|         return result; |  | ||||||
| 
 |  | ||||||
|     auto description = TRY(open_readable_file_description(fds(), fd)); |  | ||||||
|     PerformanceManager::add_read_event(*Thread::current(), fd, size, description, start_timestamp, result); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ErrorOr<FlatPtr> Process::read_impl(int fd, Userspace<u8*> buffer, size_t size) | ErrorOr<FlatPtr> Process::read_impl(int fd, Userspace<u8*> buffer, size_t size) | ||||||
| { | { | ||||||
|     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); |     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); | ||||||
|  | @ -101,7 +87,7 @@ ErrorOr<FlatPtr> Process::read_impl(int fd, Userspace<u8*> buffer, size_t size) | ||||||
|     return TRY(description->read(user_buffer, size)); |     return TRY(description->read(user_buffer, size)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<FlatPtr> Process::sys$pread(int fd, Userspace<u8*> buffer, size_t size, off_t offset) | ErrorOr<FlatPtr> Process::pread_impl(int fd, Userspace<u8*> buffer, size_t size, off_t offset) | ||||||
| { | { | ||||||
|     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); |     VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this); | ||||||
|     TRY(require_promise(Pledge::stdio)); |     TRY(require_promise(Pledge::stdio)); | ||||||
|  |  | ||||||
|  | @ -24,10 +24,10 @@ PerformanceEventBuffer::PerformanceEventBuffer(NonnullOwnPtr<KBuffer> buffer) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| NEVER_INLINE ErrorOr<void> PerformanceEventBuffer::append(int type, FlatPtr arg1, FlatPtr arg2, StringView arg3, Thread* current_thread, FlatPtr arg4, u64 arg5, ErrorOr<FlatPtr> const& arg6) | NEVER_INLINE ErrorOr<void> PerformanceEventBuffer::append(int type, FlatPtr arg1, FlatPtr arg2, StringView arg3, Thread* current_thread, FilesystemEvent filesystem_event) | ||||||
| { | { | ||||||
|     FlatPtr base_pointer = (FlatPtr)__builtin_frame_address(0); |     FlatPtr base_pointer = (FlatPtr)__builtin_frame_address(0); | ||||||
|     return append_with_ip_and_bp(current_thread->pid(), current_thread->tid(), 0, base_pointer, type, 0, arg1, arg2, arg3, arg4, arg5, arg6); |     return append_with_ip_and_bp(current_thread->pid(), current_thread->tid(), 0, base_pointer, type, 0, arg1, arg2, arg3, filesystem_event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static Vector<FlatPtr, PerformanceEvent::max_stack_frame_count> raw_backtrace(FlatPtr bp, FlatPtr ip) | static Vector<FlatPtr, PerformanceEvent::max_stack_frame_count> raw_backtrace(FlatPtr bp, FlatPtr ip) | ||||||
|  | @ -68,13 +68,13 @@ static Vector<FlatPtr, PerformanceEvent::max_stack_frame_count> raw_backtrace(Fl | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<void> PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, ThreadID tid, RegisterState const& regs, | ErrorOr<void> PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, ThreadID tid, RegisterState const& regs, | ||||||
|     int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FlatPtr arg4, u64 arg5, ErrorOr<FlatPtr> const& arg6) |     int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event) | ||||||
| { | { | ||||||
|     return append_with_ip_and_bp(pid, tid, regs.ip(), regs.bp(), type, lost_samples, arg1, arg2, arg3, arg4, arg5, arg6); |     return append_with_ip_and_bp(pid, tid, regs.ip(), regs.bp(), type, lost_samples, arg1, arg2, arg3, filesystem_event); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ErrorOr<void> PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, ThreadID tid, | ErrorOr<void> PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, ThreadID tid, | ||||||
|     FlatPtr ip, FlatPtr bp, int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FlatPtr arg4, u64 arg5, ErrorOr<FlatPtr> const& arg6) |     FlatPtr ip, FlatPtr bp, int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event) | ||||||
| { | { | ||||||
|     if (count() >= capacity()) |     if (count() >= capacity()) | ||||||
|         return ENOBUFS; |         return ENOBUFS; | ||||||
|  | @ -160,12 +160,8 @@ ErrorOr<void> PerformanceEventBuffer::append_with_ip_and_bp(ProcessID pid, Threa | ||||||
|         event.data.signpost.arg1 = arg1; |         event.data.signpost.arg1 = arg1; | ||||||
|         event.data.signpost.arg2 = arg2; |         event.data.signpost.arg2 = arg2; | ||||||
|         break; |         break; | ||||||
|     case PERF_EVENT_READ: |     case PERF_EVENT_FILESYSTEM: | ||||||
|         event.data.read.fd = arg1; |         event.data.filesystem = filesystem_event; | ||||||
|         event.data.read.size = arg2; |  | ||||||
|         event.data.read.filename_index = arg4; |  | ||||||
|         event.data.read.start_timestamp = arg5; |  | ||||||
|         event.data.read.success = !arg6.is_error(); |  | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|         return EINVAL; |         return EINVAL; | ||||||
|  | @ -295,13 +291,51 @@ ErrorOr<void> PerformanceEventBuffer::to_json_impl(Serializer& object) const | ||||||
|             TRY(event_object.add("arg1"sv, event.data.signpost.arg1)); |             TRY(event_object.add("arg1"sv, event.data.signpost.arg1)); | ||||||
|             TRY(event_object.add("arg2"sv, event.data.signpost.arg2)); |             TRY(event_object.add("arg2"sv, event.data.signpost.arg2)); | ||||||
|             break; |             break; | ||||||
|         case PERF_EVENT_READ: |         case PERF_EVENT_FILESYSTEM: | ||||||
|             TRY(event_object.add("type"sv, "read")); |             TRY(event_object.add("type"sv, "filesystem"sv)); | ||||||
|             TRY(event_object.add("fd"sv, event.data.read.fd)); |             TRY(event_object.add("durationNs"sv, event.data.filesystem.durationNs)); | ||||||
|             TRY(event_object.add("size"sv, event.data.read.size)); |             switch (event.data.filesystem.type) { | ||||||
|             TRY(event_object.add("filename_index"sv, event.data.read.filename_index)); |             case FilesystemEventType::Open: { | ||||||
|             TRY(event_object.add("start_timestamp"sv, event.data.read.start_timestamp)); |                 auto const& open = event.data.filesystem.data.open; | ||||||
|             TRY(event_object.add("success"sv, event.data.read.success)); |                 TRY(event_object.add("fs_event_type"sv, "open"sv)); | ||||||
|  |                 TRY(event_object.add("dirfd"sv, open.dirfd)); | ||||||
|  |                 TRY(event_object.add("filename_index"sv, open.filename_index)); | ||||||
|  |                 TRY(event_object.add("options"sv, open.options)); | ||||||
|  |                 TRY(event_object.add("mode"sv, open.mode)); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case FilesystemEventType::Close: { | ||||||
|  |                 auto const& close = event.data.filesystem.data.close; | ||||||
|  |                 TRY(event_object.add("fs_event_type"sv, "close"sv)); | ||||||
|  |                 TRY(event_object.add("fd"sv, close.fd)); | ||||||
|  |                 TRY(event_object.add("filename_index"sv, close.filename_index)); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case FilesystemEventType::Readv: { | ||||||
|  |                 auto const& readv = event.data.filesystem.data.readv; | ||||||
|  |                 TRY(event_object.add("fs_event_type"sv, "readv"sv)); | ||||||
|  |                 TRY(event_object.add("fd"sv, readv.fd)); | ||||||
|  |                 TRY(event_object.add("filename_index"sv, readv.filename_index)); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case FilesystemEventType::Read: { | ||||||
|  |                 auto const& read = event.data.filesystem.data.read; | ||||||
|  |                 TRY(event_object.add("fs_event_type"sv, "read"sv)); | ||||||
|  |                 TRY(event_object.add("fd"sv, read.fd)); | ||||||
|  |                 TRY(event_object.add("filename_index"sv, read.filename_index)); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case FilesystemEventType::Pread: { | ||||||
|  |                 auto const& pread = event.data.filesystem.data.pread; | ||||||
|  |                 TRY(event_object.add("fs_event_type"sv, "pread"sv)); | ||||||
|  |                 TRY(event_object.add("fd"sv, pread.fd)); | ||||||
|  |                 TRY(event_object.add("filename_index"sv, pread.filename_index)); | ||||||
|  |                 TRY(event_object.add("buffer_ptr"sv, pread.buffer_ptr)); | ||||||
|  |                 TRY(event_object.add("size"sv, pread.size)); | ||||||
|  |                 TRY(event_object.add("offset"sv, pread.offset)); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         TRY(event_object.add("pid"sv, event.pid)); |         TRY(event_object.add("pid"sv, event.pid)); | ||||||
|  |  | ||||||
|  | @ -77,6 +77,66 @@ struct [[gnu::packed]] ReadPerformanceEvent { | ||||||
|     bool success; |     bool success; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | enum class FilesystemEventType : u8 { | ||||||
|  |     Open, | ||||||
|  |     Close, | ||||||
|  |     Readv, | ||||||
|  |     Read, | ||||||
|  |     Pread | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct [[gnu::packed]] OpenEventData { | ||||||
|  |     int dirfd; | ||||||
|  |     size_t filename_index; | ||||||
|  |     int options; | ||||||
|  |     u64 mode; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct [[gnu::packed]] CloseEventData { | ||||||
|  |     int fd; | ||||||
|  |     size_t filename_index; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct [[gnu::packed]] ReadvEventData { | ||||||
|  |     int fd; | ||||||
|  |     size_t filename_index; | ||||||
|  |     // struct iovec* iov; // TODO: Implement
 | ||||||
|  |     // int iov_count; // TODO: Implement
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct [[gnu::packed]] ReadEventData { | ||||||
|  |     int fd; | ||||||
|  |     size_t filename_index; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct [[gnu::packed]] PreadEventData { | ||||||
|  |     int fd; | ||||||
|  |     size_t filename_index; | ||||||
|  |     FlatPtr buffer_ptr; | ||||||
|  |     size_t size; | ||||||
|  |     off_t offset; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // FIXME: This is a hack to make the compiler pack this struct correctly.
 | ||||||
|  | struct [[gnu::packed]] PackedErrorOr { | ||||||
|  |     bool is_error; | ||||||
|  |     FlatPtr value; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct [[gnu::packed]] FilesystemEvent { | ||||||
|  |     FilesystemEventType type; | ||||||
|  |     u64 durationNs; | ||||||
|  |     PackedErrorOr result; | ||||||
|  | 
 | ||||||
|  |     union { | ||||||
|  |         OpenEventData open; | ||||||
|  |         CloseEventData close; | ||||||
|  |         ReadvEventData readv; | ||||||
|  |         ReadEventData read; | ||||||
|  |         PreadEventData pread; | ||||||
|  |     } data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| struct [[gnu::packed]] PerformanceEvent { | struct [[gnu::packed]] PerformanceEvent { | ||||||
|     u32 type { 0 }; |     u32 type { 0 }; | ||||||
|     u8 stack_size { 0 }; |     u8 stack_size { 0 }; | ||||||
|  | @ -96,7 +156,7 @@ struct [[gnu::packed]] PerformanceEvent { | ||||||
|         KMallocPerformanceEvent kmalloc; |         KMallocPerformanceEvent kmalloc; | ||||||
|         KFreePerformanceEvent kfree; |         KFreePerformanceEvent kfree; | ||||||
|         SignpostPerformanceEvent signpost; |         SignpostPerformanceEvent signpost; | ||||||
|         ReadPerformanceEvent read; |         FilesystemEvent filesystem; | ||||||
|     } data; |     } data; | ||||||
|     static constexpr size_t max_stack_frame_count = 64; |     static constexpr size_t max_stack_frame_count = 64; | ||||||
|     FlatPtr stack[max_stack_frame_count]; |     FlatPtr stack[max_stack_frame_count]; | ||||||
|  | @ -111,11 +171,11 @@ class PerformanceEventBuffer { | ||||||
| public: | public: | ||||||
|     static OwnPtr<PerformanceEventBuffer> try_create_with_size(size_t buffer_size); |     static OwnPtr<PerformanceEventBuffer> try_create_with_size(size_t buffer_size); | ||||||
| 
 | 
 | ||||||
|     ErrorOr<void> append(int type, FlatPtr arg1, FlatPtr arg2, StringView arg3, Thread* current_thread = Thread::current(), FlatPtr arg4 = 0, u64 arg5 = 0, ErrorOr<FlatPtr> const& arg6 = 0); |     ErrorOr<void> append(int type, FlatPtr arg1, FlatPtr arg2, StringView arg3, Thread* current_thread = Thread::current(), FilesystemEvent filesystem_event = {}); | ||||||
|     ErrorOr<void> append_with_ip_and_bp(ProcessID pid, ThreadID tid, FlatPtr eip, FlatPtr ebp, |     ErrorOr<void> append_with_ip_and_bp(ProcessID pid, ThreadID tid, FlatPtr eip, FlatPtr ebp, | ||||||
|         int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FlatPtr arg4 = 0, u64 arg5 = {}, ErrorOr<FlatPtr> const& arg6 = 0); |         int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event = {}); | ||||||
|     ErrorOr<void> append_with_ip_and_bp(ProcessID pid, ThreadID tid, RegisterState const& regs, |     ErrorOr<void> append_with_ip_and_bp(ProcessID pid, ThreadID tid, RegisterState const& regs, | ||||||
|         int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FlatPtr arg4 = 0, u64 arg5 = {}, ErrorOr<FlatPtr> const& arg6 = 0); |         int type, u32 lost_samples, FlatPtr arg1, FlatPtr arg2, StringView arg3, FilesystemEvent filesystem_event = {}); | ||||||
| 
 | 
 | ||||||
|     void clear() |     void clear() | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org> |  * Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org> | ||||||
|  |  * Copyright (c) 2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -128,40 +129,6 @@ public: | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     static void add_read_event(Thread& thread, int fd, size_t size, OpenFileDescription const& file_description, u64 start_timestamp, ErrorOr<FlatPtr> const& result) |  | ||||||
|     { |  | ||||||
|         if (thread.is_profiling_suppressed()) |  | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         auto* event_buffer = thread.process().current_perf_events_buffer(); |  | ||||||
|         if (event_buffer == nullptr) |  | ||||||
|             return; |  | ||||||
| 
 |  | ||||||
|         size_t filepath_string_index; |  | ||||||
| 
 |  | ||||||
|         if (auto path = file_description.original_absolute_path(); !path.is_error()) { |  | ||||||
|             auto registered_result = event_buffer->register_string(move(path.value())); |  | ||||||
|             if (registered_result.is_error()) |  | ||||||
|                 return; |  | ||||||
|             filepath_string_index = registered_result.value(); |  | ||||||
|         } else if (auto pseudo_path = file_description.pseudo_path(); !pseudo_path.is_error()) { |  | ||||||
|             auto registered_result = event_buffer->register_string(move(pseudo_path.value())); |  | ||||||
|             if (registered_result.is_error()) |  | ||||||
|                 return; |  | ||||||
|             filepath_string_index = registered_result.value(); |  | ||||||
|         } else { |  | ||||||
|             auto invalid_path_string = KString::try_create("<INVALID_FILE_PATH>"sv); // TODO: Performance, unnecessary allocations.
 |  | ||||||
|             if (invalid_path_string.is_error()) |  | ||||||
|                 return; |  | ||||||
|             auto registered_result = event_buffer->register_string(move(invalid_path_string.value())); |  | ||||||
|             if (registered_result.is_error()) |  | ||||||
|                 return; |  | ||||||
|             filepath_string_index = registered_result.value(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         [[maybe_unused]] auto rc = event_buffer->append(PERF_EVENT_READ, fd, size, {}, &thread, filepath_string_index, start_timestamp, result); // wrong arguments
 |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     static void timer_tick(RegisterState const& regs) |     static void timer_tick(RegisterState const& regs) | ||||||
|     { |     { | ||||||
|         static UnixDateTime last_wakeup; |         static UnixDateTime last_wakeup; | ||||||
|  |  | ||||||
|  | @ -690,7 +690,11 @@ private: | ||||||
| 
 | 
 | ||||||
|     ErrorOr<void> remap_range_as_stack(FlatPtr address, size_t size); |     ErrorOr<void> remap_range_as_stack(FlatPtr address, size_t size); | ||||||
| 
 | 
 | ||||||
|  |     ErrorOr<FlatPtr> open_impl(Userspace<Syscall::SC_open_params const*>); | ||||||
|  |     ErrorOr<FlatPtr> close_impl(int fd); | ||||||
|     ErrorOr<FlatPtr> read_impl(int fd, Userspace<u8*> buffer, size_t size); |     ErrorOr<FlatPtr> read_impl(int fd, Userspace<u8*> buffer, size_t size); | ||||||
|  |     ErrorOr<FlatPtr> pread_impl(int fd, Userspace<u8*>, size_t, off_t); | ||||||
|  |     ErrorOr<FlatPtr> readv_impl(int fd, Userspace<const struct iovec*> iov, int iov_count); | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     ErrorOr<void> traverse_as_directory(FileSystemID, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const; |     ErrorOr<void> traverse_as_directory(FileSystemID, Function<ErrorOr<void>(FileSystem::DirectoryEntryView const&)> callback) const; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2022, Jakub Berkop <jakub.berkop@gmail.com> |  * Copyright (c) 2022-2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -22,11 +22,20 @@ FileEventModel::~FileEventModel() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | u64 FileEventNode::total_count() const | ||||||
|  | { | ||||||
|  |     return m_open.count + m_close.count + m_readv.count + m_read.count + m_pread.count; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Duration FileEventNode::total_duration() const | ||||||
|  | { | ||||||
|  |     return m_open.duration + m_close.duration + m_readv.duration + m_read.duration + m_pread.duration; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| FileEventNode& FileEventNode::find_or_create_node(DeprecatedString const& searched_path) | FileEventNode& FileEventNode::find_or_create_node(DeprecatedString const& searched_path) | ||||||
| { | { | ||||||
|     // TODO: Optimize this function.
 |     // TODO: Optimize this function.
 | ||||||
| 
 |     if (searched_path == ""sv || searched_path == "/"sv) { | ||||||
|     if (searched_path == ""sv) { |  | ||||||
|         return *this; |         return *this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -67,7 +76,7 @@ FileEventNode& FileEventNode::create_recursively(DeprecatedString new_path) | ||||||
|     auto parts = lex_path.parts(); |     auto parts = lex_path.parts(); | ||||||
| 
 | 
 | ||||||
|     if (parts.size() == 1) { |     if (parts.size() == 1) { | ||||||
|         auto new_node = FileEventNode::create(new_path, this); |         auto new_node = FileEventNode::create(parts.take_first(), this); | ||||||
|         m_children.append(new_node); |         m_children.append(new_node); | ||||||
|         return *new_node.ptr(); |         return *new_node.ptr(); | ||||||
|     } else { |     } else { | ||||||
|  | @ -147,10 +156,30 @@ ErrorOr<String> FileEventModel::column_name(int column) const | ||||||
|     switch (column) { |     switch (column) { | ||||||
|     case Column::Path: |     case Column::Path: | ||||||
|         return "Path"_string; |         return "Path"_string; | ||||||
|     case Column::Count: |     case Column::TotalCount: | ||||||
|         return "Event Count"_string; |         return "Total Count"_string; | ||||||
|     case Column::Duration: |     case Column::TotalDuration: | ||||||
|         return "Duration [ms]"_string; |         return "Total Duration [ms]"_string; | ||||||
|  |     case Column::OpenCount: | ||||||
|  |         return "Open Count"_string; | ||||||
|  |     case Column::OpenDuration: | ||||||
|  |         return "Open Duration [ms]"_string; | ||||||
|  |     case Column::CloseCount: | ||||||
|  |         return "Close Count"_string; | ||||||
|  |     case Column::CloseDuration: | ||||||
|  |         return "Close Duration [ms]"_string; | ||||||
|  |     case Column::ReadvCount: | ||||||
|  |         return "Readv Count"_string; | ||||||
|  |     case Column::ReadvDuration: | ||||||
|  |         return "Readv Duration [ms]"_string; | ||||||
|  |     case Column::ReadCount: | ||||||
|  |         return "Read Count"_string; | ||||||
|  |     case Column::ReadDuration: | ||||||
|  |         return "Read Duration [ms]"_string; | ||||||
|  |     case Column::PreadCount: | ||||||
|  |         return "Pread Count"_string; | ||||||
|  |     case Column::PreadDuration: | ||||||
|  |         return "Pread Duration [ms]"_string; | ||||||
|     default: |     default: | ||||||
|         VERIFY_NOT_REACHED(); |         VERIFY_NOT_REACHED(); | ||||||
|     } |     } | ||||||
|  | @ -167,19 +196,36 @@ GUI::Variant FileEventModel::data(GUI::ModelIndex const& index, GUI::ModelRole r | ||||||
|     auto* node = static_cast<FileEventNode*>(index.internal_data()); |     auto* node = static_cast<FileEventNode*>(index.internal_data()); | ||||||
| 
 | 
 | ||||||
|     if (role == GUI::ModelRole::Display) { |     if (role == GUI::ModelRole::Display) { | ||||||
|         if (index.column() == Column::Count) { |         switch (index.column()) { | ||||||
|             return node->count(); |         case Column::Path: | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (index.column() == Column::Path) { |  | ||||||
|             return node->path(); |             return node->path(); | ||||||
|  |         case Column::TotalCount: | ||||||
|  |             return node->total_count(); | ||||||
|  |         case Column::TotalDuration: | ||||||
|  |             return static_cast<f32>(node->total_duration().to_nanoseconds()) / 1'000'000; | ||||||
|  |         case Column::OpenCount: | ||||||
|  |             return node->open().count; | ||||||
|  |         case Column::OpenDuration: | ||||||
|  |             return static_cast<f32>(node->open().duration.to_nanoseconds()) / 1'000'000; | ||||||
|  |         case Column::CloseCount: | ||||||
|  |             return node->close().count; | ||||||
|  |         case Column::CloseDuration: | ||||||
|  |             return static_cast<f32>(node->close().duration.to_nanoseconds()) / 1'000'000; | ||||||
|  |         case Column::ReadvCount: | ||||||
|  |             return node->readv().count; | ||||||
|  |         case Column::ReadvDuration: | ||||||
|  |             return static_cast<f32>(node->readv().duration.to_nanoseconds()) / 1'000'000; | ||||||
|  |         case Column::ReadCount: | ||||||
|  |             return node->read().count; | ||||||
|  |         case Column::ReadDuration: | ||||||
|  |             return static_cast<f32>(node->read().duration.to_nanoseconds()) / 1'000'000; | ||||||
|  |         case Column::PreadCount: | ||||||
|  |             return node->pread().count; | ||||||
|  |         case Column::PreadDuration: | ||||||
|  |             return static_cast<f32>(node->pread().duration.to_nanoseconds()) / 1'000'000; | ||||||
|  |         default: | ||||||
|  |             return {}; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         if (index.column() == Column::Duration) { |  | ||||||
|             return node->duration(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return {}; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return {}; |     return {}; | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2022, Jakub Berkop <jakub.berkop@gmail.com> |  * Copyright (c) 2022-2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| #include <AK/DeprecatedString.h> | #include <AK/DeprecatedString.h> | ||||||
| #include <AK/Function.h> | #include <AK/Function.h> | ||||||
| #include <AK/LexicalPath.h> | #include <AK/LexicalPath.h> | ||||||
|  | #include <AK/Time.h> | ||||||
| #include <LibGUI/Model.h> | #include <LibGUI/Model.h> | ||||||
| 
 | 
 | ||||||
| namespace Profiler { | namespace Profiler { | ||||||
|  | @ -35,22 +36,32 @@ public: | ||||||
| 
 | 
 | ||||||
|     DeprecatedString const& path() const { return m_path; } |     DeprecatedString const& path() const { return m_path; } | ||||||
| 
 | 
 | ||||||
|     void increment_count() { m_count++; } |     u64 total_count() const; | ||||||
|     u64 count() const { return m_count; } |     Duration total_duration() const; | ||||||
| 
 | 
 | ||||||
|     void add_to_duration(u64 duration) { duration += duration; } |     struct FileEventType { | ||||||
|     u64 duration() const { return m_duration; } |         u64 count = 0; | ||||||
|  |         Duration duration = {}; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     FileEventType& open() { return m_open; } | ||||||
|  |     FileEventType& close() { return m_close; } | ||||||
|  |     FileEventType& readv() { return m_readv; } | ||||||
|  |     FileEventType& read() { return m_read; } | ||||||
|  |     FileEventType& pread() { return m_pread; } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     FileEventNode(DeprecatedString const& path, FileEventNode* parent = nullptr) |     FileEventNode(DeprecatedString const& path, FileEventNode* parent = nullptr) | ||||||
|         : m_path(path) |         : m_path(path) | ||||||
|         , m_count(0) |  | ||||||
|         , m_duration(0) |  | ||||||
|         , m_parent(parent) {}; |         , m_parent(parent) {}; | ||||||
| 
 | 
 | ||||||
|     DeprecatedString m_path; |     DeprecatedString m_path; | ||||||
|     u64 m_count; | 
 | ||||||
|     u64 m_duration; |     FileEventType m_open; | ||||||
|  |     FileEventType m_close; | ||||||
|  |     FileEventType m_readv; | ||||||
|  |     FileEventType m_read; | ||||||
|  |     FileEventType m_pread; | ||||||
| 
 | 
 | ||||||
|     Vector<NonnullRefPtr<FileEventNode>> m_children; |     Vector<NonnullRefPtr<FileEventNode>> m_children; | ||||||
|     FileEventNode* m_parent = nullptr; |     FileEventNode* m_parent = nullptr; | ||||||
|  | @ -65,8 +76,18 @@ public: | ||||||
| 
 | 
 | ||||||
|     enum Column { |     enum Column { | ||||||
|         Path, |         Path, | ||||||
|         Count, |         TotalCount, | ||||||
|         Duration, |         TotalDuration, | ||||||
|  |         OpenCount, | ||||||
|  |         OpenDuration, | ||||||
|  |         CloseCount, | ||||||
|  |         CloseDuration, | ||||||
|  |         ReadvCount, | ||||||
|  |         ReadvDuration, | ||||||
|  |         ReadCount, | ||||||
|  |         ReadDuration, | ||||||
|  |         PreadCount, | ||||||
|  |         PreadDuration, | ||||||
|         __Count |         __Count | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> |  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> | ||||||
|  |  * Copyright (c) 2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -206,19 +207,51 @@ void Profile::rebuild_tree() | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         if (event.data.has<Event::FilesystemEventData>()) { | ||||||
|  |             auto const& filesystem_event = event.data.get<Event::FilesystemEventData>(); | ||||||
|  |             auto const& path = filesystem_event.data.visit( | ||||||
|  |                 [&](Event::OpenEventData const& data) { | ||||||
|  |                     return data.path; | ||||||
|  |                 }, | ||||||
|  |                 [&](Event::CloseEventData const& data) { | ||||||
|  |                     return data.path; | ||||||
|  |                 }, | ||||||
|  |                 [&](Event::ReadvEventData const& data) { | ||||||
|  |                     return data.path; | ||||||
|  |                 }, | ||||||
|  |                 [&](Event::ReadEventData const& data) { | ||||||
|  |                     return data.path; | ||||||
|  |                 }, | ||||||
|  |                 [&](Event::PreadEventData const& data) { | ||||||
|  |                     return data.path; | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|         if (event.data.has<Event::ReadData>()) { |             auto& event_node = m_file_event_nodes->find_or_create_node(path); | ||||||
|             auto const& read_event = event.data.get<Event::ReadData>(); |  | ||||||
|             auto& event_node = m_file_event_nodes->find_or_create_node(read_event.path); |  | ||||||
| 
 | 
 | ||||||
|             event_node.for_each_parent_node([&](FileEventNode& node) { |             event_node.for_each_parent_node([&](FileEventNode& node) { | ||||||
|                 node.increment_count(); |                 auto const duration = filesystem_event.duration; | ||||||
| 
 | 
 | ||||||
|                 // Fixme: Currently events record 'timestamp' and 'start_timestamp' in ms resolution,
 |                 filesystem_event.data.visit( | ||||||
|                 //        which results in most durations equal to zero. Increasing the resolution should
 |                     [&](Event::OpenEventData const&) { | ||||||
|                 //        make the information more accurate.
 |                         node.open().duration += duration; | ||||||
|                 auto const duration = event.timestamp - read_event.start_timestamp; |                         node.open().count++; | ||||||
|                 node.add_to_duration(duration); |                     }, | ||||||
|  |                     [&](Event::CloseEventData const&) { | ||||||
|  |                         node.close().duration += duration; | ||||||
|  |                         node.close().count++; | ||||||
|  |                     }, | ||||||
|  |                     [&](Event::ReadvEventData const&) { | ||||||
|  |                         node.readv().duration += duration; | ||||||
|  |                         node.readv().count++; | ||||||
|  |                     }, | ||||||
|  |                     [&](Event::ReadEventData const&) { | ||||||
|  |                         node.read().duration += duration; | ||||||
|  |                         node.read().count++; | ||||||
|  |                     }, | ||||||
|  |                     [&](Event::PreadEventData const&) { | ||||||
|  |                         node.pread().duration += duration; | ||||||
|  |                         node.pread().count++; | ||||||
|  |                     }); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -385,15 +418,55 @@ ErrorOr<NonnullOwnPtr<Profile>> Profile::load_from_perfcore_file(StringView path | ||||||
|             if (it != current_processes.end()) |             if (it != current_processes.end()) | ||||||
|                 it->value->handle_thread_exit(event.tid, event.serial); |                 it->value->handle_thread_exit(event.tid, event.serial); | ||||||
|             continue; |             continue; | ||||||
|         } else if (type_string == "read"sv) { |         } else if (type_string == "filesystem"sv) { | ||||||
|             auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); |             Event::FilesystemEventData fsdata { | ||||||
|             event.data = Event::ReadData { |                 .duration = Duration::from_nanoseconds(perf_event.get_integer<u64>("durationNs"sv).value_or(0)), | ||||||
|                 .fd = perf_event.get_integer<int>("fd"sv).value_or(0), |                 .data = Event::OpenEventData {}, | ||||||
|                 .size = perf_event.get_integer<size_t>("size"sv).value_or(0), |  | ||||||
|                 .path = profile_strings.get(string_index).value(), |  | ||||||
|                 .start_timestamp = perf_event.get_integer<size_t>("start_timestamp"sv).value_or(0), |  | ||||||
|                 .success = perf_event.get_bool("success"sv).value_or(false) |  | ||||||
|             }; |             }; | ||||||
|  |             auto const filesystem_event_type = perf_event.get("fs_event_type"sv).value_or("").as_string(); | ||||||
|  |             if (filesystem_event_type == "open"sv) { | ||||||
|  |                 auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); | ||||||
|  |                 auto const filename = profile_strings.get(string_index).value_or("").view(); | ||||||
|  |                 fsdata.data = Event::OpenEventData { | ||||||
|  |                     .dirfd = perf_event.get_integer<int>("dirfd"sv).value_or(0), | ||||||
|  |                     .path = filename, | ||||||
|  |                     .options = perf_event.get_integer<int>("options"sv).value_or(0), | ||||||
|  |                     .mode = perf_event.get_integer<u64>("mode"sv).value_or(0), | ||||||
|  |                 }; | ||||||
|  |             } else if (filesystem_event_type == "close"sv) { | ||||||
|  |                 auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); | ||||||
|  |                 auto const filename = profile_strings.get(string_index).value_or("").view(); | ||||||
|  |                 fsdata.data = Event::CloseEventData { | ||||||
|  |                     .fd = perf_event.get_integer<int>("fd"sv).value_or(0), | ||||||
|  |                     .path = filename, | ||||||
|  |                 }; | ||||||
|  |             } else if (filesystem_event_type == "readv"sv) { | ||||||
|  |                 auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); | ||||||
|  |                 auto const filename = profile_strings.get(string_index).value().view(); | ||||||
|  |                 fsdata.data = Event::ReadvEventData { | ||||||
|  |                     .fd = perf_event.get_integer<int>("fd"sv).value_or(0), | ||||||
|  |                     .path = filename, | ||||||
|  |                 }; | ||||||
|  |             } else if (filesystem_event_type == "read"sv) { | ||||||
|  |                 auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); | ||||||
|  |                 auto const filename = profile_strings.get(string_index).value().view(); | ||||||
|  |                 fsdata.data = Event::ReadEventData { | ||||||
|  |                     .fd = perf_event.get_integer<int>("fd"sv).value_or(0), | ||||||
|  |                     .path = filename, | ||||||
|  |                 }; | ||||||
|  |             } else if (filesystem_event_type == "pread"sv) { | ||||||
|  |                 auto const string_index = perf_event.get_addr("filename_index"sv).value_or(0); | ||||||
|  |                 auto const filename = profile_strings.get(string_index).value().view(); | ||||||
|  |                 fsdata.data = Event::PreadEventData { | ||||||
|  |                     .fd = perf_event.get_integer<int>("fd"sv).value_or(0), | ||||||
|  |                     .path = filename, | ||||||
|  |                     .buffer_ptr = perf_event.get_integer<FlatPtr>("buffer_ptr"sv).value_or(0), | ||||||
|  |                     .size = perf_event.get_integer<size_t>("size"sv).value_or(0), | ||||||
|  |                     .offset = perf_event.get_integer<off_t>("offset"sv).value_or(0), | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             event.data = fsdata; | ||||||
|         } else { |         } else { | ||||||
|             dbgln("Unknown event type '{}'", type_string); |             dbgln("Unknown event type '{}'", type_string); | ||||||
|             VERIFY_NOT_REACHED(); |             VERIFY_NOT_REACHED(); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> |  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> | ||||||
|  |  * Copyright (c) 2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -20,6 +21,7 @@ | ||||||
| #include <AK/JsonObject.h> | #include <AK/JsonObject.h> | ||||||
| #include <AK/JsonValue.h> | #include <AK/JsonValue.h> | ||||||
| #include <AK/OwnPtr.h> | #include <AK/OwnPtr.h> | ||||||
|  | #include <AK/Time.h> | ||||||
| #include <AK/Variant.h> | #include <AK/Variant.h> | ||||||
| #include <LibCore/MappedFile.h> | #include <LibCore/MappedFile.h> | ||||||
| #include <LibELF/Image.h> | #include <LibELF/Image.h> | ||||||
|  | @ -222,15 +224,45 @@ public: | ||||||
|             pid_t parent_tid {}; |             pid_t parent_tid {}; | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         struct ReadData { |         // Based on Syscall::SC_open_params
 | ||||||
|             int fd; |         struct OpenEventData { | ||||||
|             size_t size; |             int dirfd; | ||||||
|             DeprecatedString path; |             DeprecatedString path; | ||||||
|             size_t start_timestamp; |             int options; | ||||||
|             bool success; |             u64 mode; | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         Variant<nullptr_t, SampleData, MallocData, FreeData, SignpostData, MmapData, MunmapData, ProcessCreateData, ProcessExecData, ThreadCreateData, ReadData> data { nullptr }; |         struct CloseEventData { | ||||||
|  |             int fd; | ||||||
|  |             DeprecatedString path; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct ReadvEventData { | ||||||
|  |             int fd; | ||||||
|  |             DeprecatedString path; | ||||||
|  |             // struct iovec* iov; // TODO: Implement
 | ||||||
|  |             // int iov_count; // TODO: Implement
 | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct ReadEventData { | ||||||
|  |             int fd; | ||||||
|  |             DeprecatedString path; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct PreadEventData { | ||||||
|  |             int fd; | ||||||
|  |             DeprecatedString path; | ||||||
|  |             FlatPtr buffer_ptr; | ||||||
|  |             size_t size; | ||||||
|  |             off_t offset; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         struct FilesystemEventData { | ||||||
|  |             Duration duration; | ||||||
|  |             Variant<OpenEventData, CloseEventData, ReadvEventData, ReadEventData, PreadEventData> data; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         Variant<nullptr_t, SampleData, MallocData, FreeData, SignpostData, MmapData, MunmapData, ProcessCreateData, ProcessExecData, ThreadCreateData, FilesystemEventData> data { nullptr }; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     Vector<Event> const& events() const { return m_events; } |     Vector<Event> const& events() const { return m_events; } | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| /*
 | /*
 | ||||||
|  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> |  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> | ||||||
|  * Copyright (c) 2022, the SerenityOS developers. |  * Copyright (c) 2022, the SerenityOS developers. | ||||||
|  |  * Copyright (c) 2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -90,9 +91,24 @@ GUI::Variant SamplesModel::data(GUI::ModelIndex const& index, GUI::ModelRole rol | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (index.column() == Column::Path) { |         if (index.column() == Column::Path) { | ||||||
|             if (!event.data.has<Profile::Event::ReadData>()) |             if (event.data.has<Profile::Event::FilesystemEventData>()) { | ||||||
|                 return ""; |                 return event.data.get<Profile::Event::FilesystemEventData>().data.visit( | ||||||
|             return event.data.get<Profile::Event::ReadData>().path; |                     [&](Profile::Event::OpenEventData const& data) { | ||||||
|  |                         return data.path; | ||||||
|  |                     }, | ||||||
|  |                     [&](Profile::Event::CloseEventData const& data) { | ||||||
|  |                         return data.path; | ||||||
|  |                     }, | ||||||
|  |                     [&](Profile::Event::ReadvEventData const& data) { | ||||||
|  |                         return data.path; | ||||||
|  |                     }, | ||||||
|  |                     [&](Profile::Event::ReadEventData const& data) { | ||||||
|  |                         return data.path; | ||||||
|  |                     }, | ||||||
|  |                     [&](Profile::Event::PreadEventData const& data) { | ||||||
|  |                         return data.path; | ||||||
|  |                     }); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return {}; |         return {}; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
|  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> |  * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> | ||||||
|  * Copyright (c) 2021, Julius Heijmen <julius.heijmen@gmail.com> |  * Copyright (c) 2021, Julius Heijmen <julius.heijmen@gmail.com> | ||||||
|  * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl> |  * Copyright (c) 2023, Jelle Raaijmakers <jelle@gmta.nl> | ||||||
|  |  * Copyright (c) 2023, Jakub Berkop <jakub.berkop@gmail.com> | ||||||
|  * |  * | ||||||
|  * SPDX-License-Identifier: BSD-2-Clause |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  */ |  */ | ||||||
|  | @ -264,6 +265,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | ||||||
|     filesystem_events_tree_view->set_column_headers_visible(true); |     filesystem_events_tree_view->set_column_headers_visible(true); | ||||||
|     filesystem_events_tree_view->set_selection_behavior(GUI::TreeView::SelectionBehavior::SelectRows); |     filesystem_events_tree_view->set_selection_behavior(GUI::TreeView::SelectionBehavior::SelectRows); | ||||||
|     filesystem_events_tree_view->set_model(profile->file_event_model()); |     filesystem_events_tree_view->set_model(profile->file_event_model()); | ||||||
|  |     filesystem_events_tree_view->set_column_visible(FileEventModel::Column::OpenDuration, false); | ||||||
|  |     filesystem_events_tree_view->set_column_visible(FileEventModel::Column::CloseDuration, false); | ||||||
|  |     filesystem_events_tree_view->set_column_visible(FileEventModel::Column::ReadvDuration, false); | ||||||
|  |     filesystem_events_tree_view->set_column_visible(FileEventModel::Column::ReadDuration, false); | ||||||
|  |     filesystem_events_tree_view->set_column_visible(FileEventModel::Column::PreadDuration, false); | ||||||
| 
 | 
 | ||||||
|     auto file_menu = window->add_menu("&File"_string); |     auto file_menu = window->add_menu("&File"_string); | ||||||
|     file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); |     file_menu->add_action(GUI::CommonActions::make_quit_action([&](auto&) { app->quit(); })); | ||||||
|  |  | ||||||
|  | @ -52,8 +52,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | ||||||
|                 event_mask |= PERF_EVENT_PAGE_FAULT; |                 event_mask |= PERF_EVENT_PAGE_FAULT; | ||||||
|             else if (event_type == "syscall") |             else if (event_type == "syscall") | ||||||
|                 event_mask |= PERF_EVENT_SYSCALL; |                 event_mask |= PERF_EVENT_SYSCALL; | ||||||
|             else if (event_type == "read") |             else if (event_type == "filesystem") | ||||||
|                 event_mask |= PERF_EVENT_READ; |                 event_mask |= PERF_EVENT_FILESYSTEM; | ||||||
|             else { |             else { | ||||||
|                 warnln("Unknown event type '{}' specified.", event_type); |                 warnln("Unknown event type '{}' specified.", event_type); | ||||||
|                 exit(1); |                 exit(1); | ||||||
|  | @ -65,7 +65,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | ||||||
| 
 | 
 | ||||||
|     auto print_types = [] { |     auto print_types = [] { | ||||||
|         outln(); |         outln(); | ||||||
|         outln("Event type can be one of: sample, context_switch, page_fault, syscall, read, kmalloc and kfree."); |         outln("Event type can be one of: sample, context_switch, page_fault, syscall, filesystem, kmalloc and kfree."); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (!args_parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage)) { |     if (!args_parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage)) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jakub Berkop
						Jakub Berkop