logo

mstrace

Small Linux strace(1) implementationgit clone https://anongit.hacktivis.me/git/mstrace.git/
commit: 08b2728ab76bc29230377dcbb445a05028f63ee0
parent a9bc1a93890ac28224fd423a2f0cc580108c7ddd
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Wed, 15 Oct 2025 07:52:17 +0200

Add support for -o option

Diffstat:

Mmstrace.c67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Amstrace.h6++++++
Mprint_syscall.c18++++++++++++------
Msyscalls.sh2+-
4 files changed, 76 insertions(+), 17 deletions(-)

diff --git a/mstrace.c b/mstrace.c @@ -3,6 +3,8 @@ #define _DEFAULT_SOURCE +#include "mstrace.h" + #include <errno.h> #include <inttypes.h> // PRIu64 #include <linux/ptrace.h> // ptrace_syscall_info, __u64 @@ -18,6 +20,7 @@ extern void print_syscall_entry(__u64 nr, __u64 args[6]); extern void print_syscall_exit(__u64 nr, __u64 args[6]); pid_t child = -1; +FILE *out; static int wait_for_syscall(int *status) @@ -33,18 +36,61 @@ wait_for_syscall(int *status) } } +static void +usage() +{ + fputs("Usage: mstrace [-o output] <command> [args...]\n", stderr); +} + int main(int argc, char *argv[]) { - argc -= 1; - argv += 1; + char *outname = NULL; + out = stderr; + + for(int c = -1; (c = getopt(argc, argv, ":o:")) != -1;) + { + switch(c) + { + case 'o': + if(outname) + { + fprintf(stderr, "mstrace: error: Multiple -o options unsupported\n"); + usage(); + return 1; + } + outname = optarg; + break; + case ':': + fprintf(stderr, "mstrace: error: Missing operand for option: '-%c'\n", optopt); + usage(); + return 1; + case '?': + fprintf(stderr, "mstrace: error: Unknown option '-%c'\n", optopt); + usage(); + return 1; + } + } + + argc -= optind; + argv += optind; if(argc < 1) { - fprintf(stderr, "Usage: mstrace <command> [args...]\n"); + usage(); return 1; } + if(outname) + { + out = fopen(outname, "wb"); + if(!out) + { + fprintf(stderr, "mstrace: error: Failed opening file \"%s\": %s\n", outname, strerror(errno)); + return 1; + } + } + child = fork(); if(child < 0) { @@ -113,14 +159,14 @@ main(int argc, char *argv[]) if(syscall_info.exit.is_error) { - fprintf(stderr, + fprintf(out, " = (errno: %" PRIi64 ") %s\n", (int64_t)(-syscall_info.exit.rval), strerror(-syscall_info.exit.rval)); } else { - fprintf(stderr, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval); + fprintf(out, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval); } entry.nr = 0; break; @@ -128,22 +174,23 @@ main(int argc, char *argv[]) print_syscall_exit(syscall_info.seccomp.nr, syscall_info.seccomp.args); // TODO: Check how strace manages this - fprintf(stderr, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data); + fprintf(out, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data); break; case PTRACE_SYSCALL_INFO_NONE: - fprintf(stderr, "[syscall-info] none\n"); + fprintf(out, "[syscall-info] none\n"); break; } } - if(entry.nr != 0) - print_syscall_exit(entry.nr, entry.args); + if(entry.nr != 0) print_syscall_exit(entry.nr, entry.args); if(WIFEXITED(status)) { - fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status)); + fprintf(out, "+++ exited with %d +++\n", WEXITSTATUS(status)); return WEXITSTATUS(status); } + fflush(out); + return 0; } diff --git a/mstrace.h b/mstrace.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: 2024 Haelwenn (lanodan) Monnier <contact+mstrace@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#include <stdio.h> + +extern FILE *out; diff --git a/print_syscall.c b/print_syscall.c @@ -3,6 +3,7 @@ #define _DEFAULT_SOURCE +#include "mstrace.h" #include "strsyscall.h" #include <assert.h> @@ -78,14 +79,19 @@ print_syscall_entry(__u64 nr, __u64 args[6]) switch(nr) { case SYS_execve: - fprintf(stderr, "<%s("Pri_str ", " Pri_x ", " Pri_x ")\n", str_syscalls[nr], read_str(args[0], PATH_MAX), (int64_t)args[1], (int64_t)args[2]); + fprintf(out, + "<%s(" Pri_str ", " Pri_x ", " Pri_x ")\n", + str_syscalls[nr], + read_str(args[0], PATH_MAX), + (int64_t)args[1], + (int64_t)args[2]); return; case SYS_nanosleep: { struct timespec *rqtp = read_timespec(args[0]); struct timespec *rmtp = read_timespec(args[1]); - fprintf(stderr, + fprintf(out, "<%s({tv_sec = " Pri_i ", tv_nsec = " Pri_i "}, {tv_sec = " Pri_i ", tv_nsec = " Pri_i "})\n", str_syscalls[nr], @@ -109,7 +115,7 @@ print_syscall_exit(__u64 nr, __u64 args[6]) struct timespec *rqtp = read_timespec(args[0]); struct timespec *rmtp = read_timespec(args[1]); - fprintf(stderr, + fprintf(out, ">%s({tv_sec = " Pri_i ", tv_nsec = " Pri_i "}, {tv_sec = " Pri_i ", tv_nsec = " Pri_i "})", str_syscalls[nr], @@ -124,14 +130,14 @@ print_syscall_exit(__u64 nr, __u64 args[6]) assert(nr < SYSCALLS_MAX); if(str_syscalls[nr] != NULL) { - fprintf(stderr, ">%s(", str_syscalls[nr]); + fprintf(out, ">%s(", str_syscalls[nr]); } else { - fprintf(stderr, ">syscall(%" PRIu64 ", ", (uint64_t)nr); + fprintf(out, ">syscall(%" PRIu64 ", ", (uint64_t)nr); } - fprintf(stderr, + fprintf(out, "0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ")", (uint64_t)args[0], diff --git a/syscalls.sh b/syscalls.sh @@ -51,6 +51,6 @@ do fmt="${fmt}"' ")"' printf ' case SYS_%s:\n' "$syscall" - printf ' fprintf(stderr, %s, %s);\n' "$fmt" "$fmt_args" + printf ' fprintf(out, %s, %s);\n' "$fmt" "$fmt_args" printf ' return;\n' done <syscalls.txt >syscalls_cases.h