mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 13:38:11 +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