diff --git a/Userland/Utilities/strace.cpp b/Userland/Utilities/strace.cpp index a136533bc3..89f99d079c 100644 --- a/Userland/Utilities/strace.cpp +++ b/Userland/Utilities/strace.cpp @@ -5,19 +5,120 @@ */ #include +#include +#include #include #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include +#define HANDLE(VALUE) \ + case VALUE: \ + return #VALUE; +#define VALUES_TO_NAMES(FUNC_NAME) \ + static String FUNC_NAME(int value) \ + { \ + switch (value) { +#define END_VALUES_TO_NAMES() \ + } \ + return String::formatted("{}", value); \ + } + +VALUES_TO_NAMES(errno_name) +HANDLE(EPERM) +HANDLE(ENOENT) +HANDLE(ESRCH) +HANDLE(EINTR) +HANDLE(EIO) +HANDLE(ENXIO) +HANDLE(E2BIG) +HANDLE(ENOEXEC) +HANDLE(EBADF) +HANDLE(ECHILD) +HANDLE(EAGAIN) +HANDLE(ENOMEM) +HANDLE(EACCES) +HANDLE(EFAULT) +HANDLE(ENOTBLK) +HANDLE(EBUSY) +HANDLE(EEXIST) +HANDLE(EXDEV) +HANDLE(ENODEV) +HANDLE(ENOTDIR) +HANDLE(EISDIR) +HANDLE(EINVAL) +HANDLE(ENFILE) +HANDLE(EMFILE) +HANDLE(ENOTTY) +HANDLE(ETXTBSY) +HANDLE(EFBIG) +HANDLE(ENOSPC) +HANDLE(ESPIPE) +HANDLE(EROFS) +HANDLE(EMLINK) +HANDLE(EPIPE) +HANDLE(ERANGE) +HANDLE(ENAMETOOLONG) +HANDLE(ELOOP) +HANDLE(EOVERFLOW) +HANDLE(EOPNOTSUPP) +HANDLE(ENOSYS) +HANDLE(ENOTIMPL) +HANDLE(EAFNOSUPPORT) +HANDLE(ENOTSOCK) +HANDLE(EADDRINUSE) +HANDLE(EWHYTHO) +HANDLE(ENOTEMPTY) +HANDLE(EDOM) +HANDLE(ECONNREFUSED) +HANDLE(EADDRNOTAVAIL) +HANDLE(EISCONN) +HANDLE(ECONNABORTED) +HANDLE(EALREADY) +HANDLE(ECONNRESET) +HANDLE(EDESTADDRREQ) +HANDLE(EHOSTUNREACH) +HANDLE(EILSEQ) +HANDLE(EMSGSIZE) +HANDLE(ENETDOWN) +HANDLE(ENETUNREACH) +HANDLE(ENETRESET) +HANDLE(ENOBUFS) +HANDLE(ENOLCK) +HANDLE(ENOMSG) +HANDLE(ENOPROTOOPT) +HANDLE(ENOTCONN) +HANDLE(EPROTONOSUPPORT) +HANDLE(EDEADLK) +HANDLE(ETIMEDOUT) +HANDLE(EPROTOTYPE) +HANDLE(EINPROGRESS) +HANDLE(ENOTHREAD) +HANDLE(EPROTO) +HANDLE(ENOTSUP) +HANDLE(EPFNOSUPPORT) +HANDLE(EDIRINTOSELF) +HANDLE(EDQUOT) +HANDLE(EMAXERRNO) +END_VALUES_TO_NAMES() + +VALUES_TO_NAMES(whence_name) +HANDLE(SEEK_SET) +HANDLE(SEEK_CUR) +HANDLE(SEEK_END) +END_VALUES_TO_NAMES() + static int g_pid = -1; #if ARCH(I386) @@ -36,24 +137,306 @@ static void handle_sigint(int) } } -static String format_syscall(syscall_arg_t syscall_index, syscall_arg_t arg1, syscall_arg_t arg2, syscall_arg_t arg3, syscall_arg_t res) +static void copy_from_process(const void* source_p, Bytes target) { - auto syscall_function = (Syscall::Function)syscall_index; - StringBuilder builder; - builder.append(Syscall::to_string(syscall_function)); - builder.append('('); + auto source = static_cast(source_p); + size_t offset = 0; + size_t left = target.size(); + while (left) { + int value = ptrace(PT_PEEK, g_pid, const_cast(source) + offset, 0); + size_t to_copy = min(sizeof(int), left); + target.overwrite(offset, &value, to_copy); + left -= to_copy; + offset += to_copy; + } +} - switch (syscall_function) { - default: - builder.appendff("{:#08x}, {:#08x}, {:#08x})\t={}", - arg1, - arg2, - arg3, - res); +static ByteBuffer copy_from_process(const void* source, size_t length) +{ + auto buffer = ByteBuffer::create_uninitialized(length).value(); + copy_from_process(source, buffer.bytes()); + return buffer; +} + +template +static T copy_from_process(const T* source) +{ + T value {}; + copy_from_process(source, Bytes { &value, sizeof(T) }); + return value; +} + +struct PointerArgument { + const void* value; +}; + +namespace AK { +template<> +struct Formatter : StandardFormatter { + Formatter() = default; + explicit Formatter(StandardFormatter formatter) + : StandardFormatter(formatter) + { } - builder.append('\n'); - return builder.to_string(); + void format(FormatBuilder& format_builder, PointerArgument const& value) + { + auto& builder = format_builder.builder(); + if (value.value == nullptr) + builder.append("null"); + else + builder.appendff("{}", value.value); + } +}; +} + +class FormattedSyscallBuilder { + +public: + FormattedSyscallBuilder(Syscall::Function syscall_function) + { + m_builder.append(Syscall::to_string(syscall_function)); + m_builder.append('('); + } + + template + void add_argument(CheckedFormatString format, T&& arg) + { + add_argument_separator(); + m_builder.appendff(format.view(), forward(arg)); + } + + template + void add_argument(T&& arg) + { + add_argument("{}", forward(arg)); + } + + void add_string_argument(Syscall::StringArgument const& string_argument) + { + if (string_argument.characters == nullptr) + add_argument("null"); + else { + auto string = copy_from_process(string_argument.characters, string_argument.length); + add_argument("\"{}\"", StringView(string.data(), string.size())); + } + } + + template + void add_arguments(Ts&&... args) + { + (add_argument(forward(args)), ...); + } + + template + void format_result_no_error(T res) + { + m_builder.appendff(") = {}\n", res); + } + + void format_result(Integral auto res) + { + m_builder.append(") = "); + if (res < 0) + m_builder.appendff("{} {}", res, errno_name(-(int)res)); + else + m_builder.appendff("{}", res); + m_builder.append('\n'); + } + + void format_result(void* res) + { + m_builder.appendff(") = {}\n", res); + } + + StringView string_view() + { + return m_builder.string_view(); + } + +private: + void add_argument_separator() + { + if (!m_first_arg) { + m_builder.append(", "); + } + m_first_arg = false; + } + + StringBuilder m_builder; + bool m_first_arg { true }; +}; + +static void format_open(FormattedSyscallBuilder& builder, Syscall::SC_open_params* params_p) +{ + auto params = copy_from_process(params_p); + + if (params.dirfd == AT_FDCWD) + builder.add_argument("AT_FDCWD"); + else + builder.add_argument(params.dirfd); + + builder.add_string_argument(params.path); + + Vector active_flags; + if (params.options & O_RDWR) + active_flags.append("O_RDWR"); + else if (params.options & O_RDONLY) + active_flags.append("O_RDONLY"); + else if (params.options & O_WRONLY) + active_flags.append("O_WRONLY"); + + if (params.options & O_APPEND) + active_flags.append("O_APPEND"); + if (params.options & O_CREAT) + active_flags.append("O_CREAT"); + // TODO: etc... + + // TODO: add to FormattedSyscallBuilder + StringBuilder sbuilder; + sbuilder.join(" | ", active_flags); + builder.add_argument(sbuilder.to_string()); + + if (params.options & O_CREAT) + builder.add_argument("{:04o}", params.mode); +} + +namespace AK { +template<> +struct Formatter : StandardFormatter { + void format(FormatBuilder& format_builder, struct timespec value) + { + auto& builder = format_builder.builder(); + builder.appendff("{{tv_sec={}, tv_nsec={}}}", value.tv_sec, value.tv_nsec); + } +}; + +template<> +struct Formatter : StandardFormatter { + void format(FormatBuilder& format_builder, struct timeval value) + { + auto& builder = format_builder.builder(); + builder.appendff("{{tv_sec={}, tv_usec={}}}", value.tv_sec, value.tv_usec); + } +}; + +template<> +struct Formatter : StandardFormatter { + void format(FormatBuilder& format_builder, struct stat value) + { + auto& builder = format_builder.builder(); + builder.appendff( + "{{st_dev={}, st_ino={}, st_mode={}, st_nlink={}, st_uid={}, st_gid={}, st_rdev={}, " + "st_size={}, st_blksize={}, st_blocks={}, st_atim={}, st_mtim={}, st_ctim={}}}", + value.st_dev, value.st_ino, value.st_mode, value.st_nlink, value.st_uid, value.st_gid, value.st_rdev, + value.st_size, value.st_blksize, value.st_blocks, value.st_atim, value.st_mtim, value.st_ctim); + } +}; +} + +static void format_fstat(FormattedSyscallBuilder& builder, int fd, struct stat* buf_p) +{ + auto buf = copy_from_process(buf_p); + builder.add_arguments(fd, buf); +} + +static void format_stat(FormattedSyscallBuilder& builder, Syscall::SC_stat_params* params_p) +{ + auto params = copy_from_process(params_p); + if (params.dirfd == AT_FDCWD) + builder.add_argument("AT_FDCWD"); + else + builder.add_argument(params.dirfd); + builder.add_string_argument(params.path); + builder.add_arguments(copy_from_process(params.statbuf), params.follow_symlinks); +} + +static void format_lseek(FormattedSyscallBuilder& builder, int fd, off_t offset, int whence) +{ + builder.add_arguments(fd, offset, whence_name(whence)); +} + +static void format_read(FormattedSyscallBuilder& builder, int fd, void* buf, size_t nbyte) +{ + builder.add_arguments(fd, buf, nbyte); +} + +static void format_write(FormattedSyscallBuilder& builder, int fd, void* buf, size_t nbyte) +{ + builder.add_arguments(fd, buf, nbyte); +} + +static void format_close(FormattedSyscallBuilder& builder, int fd) +{ + builder.add_arguments(fd); +} + +static void format_select(FormattedSyscallBuilder& builder, Syscall::SC_select_params* params_p) +{ + // TODO: format fds and sigmask properly + auto params = copy_from_process(params_p); + builder.add_arguments( + params.nfds, + PointerArgument { params.readfds }, + PointerArgument { params.writefds }, + PointerArgument { params.exceptfds }, + copy_from_process(params.timeout), + PointerArgument { params.sigmask }); +} + +static void format_syscall(FormattedSyscallBuilder& builder, Syscall::Function syscall_function, syscall_arg_t arg1, syscall_arg_t arg2, syscall_arg_t arg3, syscall_arg_t res) +{ + enum ResultType { + Int, + Ssize, + VoidP + }; + + ResultType result_type { Int }; + switch (syscall_function) { + case SC_open: + format_open(builder, (Syscall::SC_open_params*)arg1); + break; + case SC_fstat: + format_fstat(builder, (int)arg1, (struct stat*)arg2); + result_type = Ssize; + break; + case SC_stat: + format_stat(builder, (Syscall::SC_stat_params*)arg1); + break; + case SC_lseek: + format_lseek(builder, (int)arg1, (off_t)arg2, (int)arg3); + break; + case SC_read: + format_read(builder, (int)arg1, (void*)arg2, (size_t)arg3); + result_type = Ssize; + break; + case SC_write: + format_write(builder, (int)arg1, (void*)arg2, (size_t)arg3); + result_type = Ssize; + break; + case SC_close: + format_close(builder, (int)arg1); + break; + case SC_select: + format_select(builder, (Syscall::SC_select_params*)arg1); + break; + default: + builder.add_arguments((void*)arg1, (void*)arg2, (void*)arg3); + result_type = VoidP; + } + + switch (result_type) { + case Int: + builder.format_result((int)res); + break; + case Ssize: + builder.format_result((ssize_t)res); + break; + case VoidP: + builder.format_result((void*)res); + break; + } } int main(int argc, char** argv) @@ -186,9 +569,11 @@ int main(int argc, char** argv) u64 res = regs.rax; #endif - auto string = format_syscall(syscall_index, arg1, arg2, arg3, res); + auto syscall_function = (Syscall::Function)syscall_index; + FormattedSyscallBuilder builder(syscall_function); + format_syscall(builder, syscall_function, arg1, arg2, arg3, res); - if (!trace_file->write(string)) { + if (!trace_file->write(builder.string_view())) { warnln("write: {}", trace_file->error_string()); return 1; }