strace
Strace is a diagnostic, debugging, and instructional userspace utility for Linux that intercepts and records interactions between processes and the Linux kernel, including system calls, signal deliveries, and changes in process state.[1] It operates by leveraging the kernel's ptrace mechanism to attach to running processes or trace new ones from execution, logging details such as system call names, arguments, return values, and signal information to standard error or a specified file.[2] This capability makes strace invaluable for system administrators, developers, and troubleshooters seeking to diagnose issues, analyze performance, or understand program behavior without access to source code.[2] Originally developed by Paul Kranenburg in 1991 for SunOS 4.1, strace was ported to Linux by Branko Lankester in 1992 and has since evolved through contributions from maintainers including Rick Sladkey, Wichert Akkerman, Roland McGrath, and Dmitry V. Levin, who has led development since 2009.[2] The tool's open-source nature is reflected in its active GitHub repository, where ongoing enhancements ensure compatibility with modern Linux kernels and architectures.[3] Key features include filtering traces by specific system calls or categories (e.g., file operations or network activity), fault injection for testing error handling, and summarization of call counts, timings, and errors, all configurable via command-line options like-e, -f, and -c.[2] These functionalities enable targeted debugging, such as isolating failures in binary applications or monitoring child processes in complex executions.[1]
Introduction
Definition and Purpose
strace is a command-line utility designed to intercept and record the system calls invoked by a running process and the signals it receives, offering detailed visibility into the interactions between user-space applications and the Linux kernel.[2] By leveraging the kernel's ptrace interface, strace captures the names of system calls, their arguments, return values, and any errors, printing this information to standard error or a specified file for analysis.[3] This functionality makes it an essential tool for examining low-level kernel interactions without requiring access to the source code of the traced program.[1] The primary purpose of strace is to aid in debugging, diagnostics, and instructional use within Linux environments, enabling users to identify unexpected system call behaviors, diagnose program hangs or crashes, and analyze resource utilization patterns.[2] It is particularly valuable for troubleshooting applications where source code is unavailable, as it reveals the precise sequence of kernel requests made by the process, helping to isolate bugs at the user-kernel boundary or verify the sanity of program execution.[3] Developers and system administrators employ strace to capture subtle issues like race conditions or misconfigured interactions with the operating system, all without altering the target program's behavior.[1] One of its core benefits is non-intrusive tracing, allowing attachment to existing processes or launching new ones under supervision, which facilitates detailed logging of system call arguments and results in real-time or post-execution review.[3] This approach provides a transparent window into kernel operations, supporting educational exploration of system programming concepts alongside practical problem-solving.[1]Key Features
Strace provides robust signal tracing capabilities, capturing signals delivered to the traced process along with decoded signal information, such as signal numbers and codes (e.g.,SIGINT {si_signo=SIGINT, si_code=SI_USER}), and monitoring changes to signal handlers and dispositions through related system calls.[2] This feature aids in diagnosing issues related to signal handling in applications, revealing how processes respond to interrupts or terminations without requiring source code access.[2]
A standout aspect of strace is its argument decoding, which translates raw system call parameters into human-readable formats, such as displaying file paths for open() invocations (e.g., open("xyzzy", O_WRONLY|O_APPEND|O_CREAT, 0666)) instead of mere pointers, and symbolically dereferencing structures like stat.[2] This decoding enhances interpretability for users analyzing process behavior at the kernel interface.[2]
Filtering options in strace allow precise control over tracing, enabling users to focus on specific system calls (e.g., -e trace=open), exclude others to minimize output noise (e.g., -e trace=!open), or target particular processes and paths.[2] These mechanisms are essential for isolating relevant diagnostic data in complex environments.[2]
Output customization further bolsters strace's utility, with support for verbose mode (-v) to reveal full structure details, timestamping options like wall-clock (-t) or relative timing (-r), and redirection to files (-o file) for post-analysis.[2] In terms of compatibility, strace handles multi-threaded applications by tracing system calls on a per-thread basis (e.g., via -f -p [PID](/page/PID)), and it integrates seamlessly with debuggers like GDB for combined system-level and source-level inspection.[2]
History and Development
Origins and Initial Release
Strace originated as a diagnostic utility for the SunOS operating system, developed by Paul Kranenburg in 1991 to monitor system calls and signals, drawing inspiration from SunOS's built-in trace utility.[2] This initial version, released as strace 1.5, addressed the need for a user-space tool to observe process interactions with the kernel in Unix-like environments, where such capabilities were limited without invasive kernel modifications.[4] The tool's adaptation to Linux came shortly thereafter, with Branko Lankester porting and enhancing the SunOS version for the emerging Linux kernel in 1992, basing his work on Kranenburg's 1.5 release despite a later 2.5 version for SunOS.[2] This port aligned with the early development of Linux, coinciding with kernel version 0.12 released in March 1992, and filled a critical gap in open-source debugging tools during Linux's formative years when developers relied on rudimentary tracing methods.[5] The motivations stemmed from the open-source community's demand for accessible, non-proprietary utilities akin to Solaris's truss, enabling better understanding of system call behaviors without proprietary constraints.[4] By the mid-1990s, strace had gained widespread integration into major Linux distributions, such as those based on Debian and Slackware, solidifying its role as a standard component in GNU/Linux environments for debugging and instruction.[2] Early enhancements, including Rick Sladkey's 1993 merger of SunOS and Linux variants to incorporate SVR4 truss features, further propelled its adoption among developers navigating the rapid evolution of Linux architectures.[4]Version History and Major Updates
Strace's development has seen incremental enhancements since its early versions, with significant milestones marking improvements in functionality and platform support. In version 2.x, the introduction of the -e trace=set option enabled selective tracing of specific system calls, allowing users to focus on subsets of interactions for more targeted debugging without overwhelming output.[6] Version 4.0, released around 2005, introduced refinements to stat structure handling on 64-bit systems and better personality support for diverse execution environments.[6] Subsequent releases built on this foundation; version 4.5 in 2008 added multi-architecture support, extending compatibility to platforms such as PowerPC, SPARC, and ARM, and enhanced signal handling capabilities, providing more accurate tracing for setuid programs and multi-threaded processes, which improved reliability in complex process scenarios.[6] A major leap came with version 5.0 in 2019, along with expanded syscall decoding for features like bpf and execveat.[6] Support for seccomp filters, introduced in later versions such as 5.3 in 2019, allowed strace to interact more effectively with kernel-level syscall restrictions, enabling finer-grained control and observation in secured environments post-2010.[6] As of 2023, version 6.2 brought refinements to ARM64 support and syscall filtering, including better collision resolution for overlapping ioctl commands, enhancing performance and precision on modern ARM-based systems.[6] By 2025, version 6.17 further advanced decoding for syscalls like file_getattr and file_setattr, as well as socket options such as SO_INQ, reflecting ongoing alignment with Linux kernel evolutions up to version 6.17.[7] The project continues active maintenance through its GitHub repository, with frequent releases focusing on syscall decoding, filtering options, and architecture-specific fixes, under the leadership of Dmitry V. Levin since 2009, following contributions from maintainers including Wichert Akkerman and Roland McGrath.[3] Regarding compatibility, early versions like 3.99 provided adaptations for non-Linux Unix systems including FreeBSD/i386, IRIX5, Solaris, and UnixWare, but by version 4.7 in 2012, non-Linux support was removed to concentrate efforts on Linux, maintaining its primary focus while preserving backward compatibility within Linux ecosystems.[6]Technical Implementation
Underlying Mechanism with ptrace
Strace relies on the ptrace system call, a fundamental Linux kernel interface that enables one process—the tracer—to observe and control the execution of another process, known as the tracee. This mechanism allows strace to attach to a target process, pause its execution at key points such as system call entry and exit, and inspect or modify its state without altering the overall program flow. By invoking ptrace, strace gains the ability to monitor interactions between the tracee and the kernel, capturing details like system call invocations for diagnostic purposes.[8] The attachment process begins when strace calls ptrace with the PTRACE_ATTACH request, specifying the process ID of the target tracee. This action sets a PT_PTRACED flag on the tracee in the kernel, immediately stopping it via a SIGSTOP signal and transferring control to the tracer. Strace then uses waitpid to synchronize and confirm the stop before proceeding. To initiate syscall tracing, strace issues PTRACE_SYSCALL, which resumes the tracee until it reaches the next system call entry or exit; the kernel delivers a SIGTRAP to halt execution at these points, allowing strace to intervene. During these stops, strace accesses the tracee's registers using PTRACE_GETREGS to retrieve the system call number—stored in the %rax register on x86_64 architectures—and arguments, typically held in %rdi, %rsi, %rdx, %r10, %r8, and %r9 for the first six parameters. On syscall exit, it similarly reads the return value from %rax.[8][9][8] For detachment and cleanup, strace invokes PTRACE_DETACH once tracing is complete, which clears the PT_PTRACED flag, resumes normal execution of the tracee, and delivers any pending signals. This ensures the target process continues without interference after the tracing session. Strace also handles child processes spawned by the tracee during a fork or clone system call; by monitoring these events through ptrace, it can automatically attach to the children using PTRACE_ATTACH, maintaining comprehensive tracing across process hierarchies.[8][9] Strace adapts its ptrace implementation to different CPU architectures to account for varying syscall conventions. On x86_64, the kernel's syscall entry code in entry_64.S checks the %rax register for the syscall number upon invocation via the syscall instruction. In contrast, on ARM64 (AArch64), the syscall number resides in the x8 register, with arguments in x0 through x7, and entry occurs via the svc instruction; strace employs architecture-specific syscall tables and register offsets defined in kernel headers like arch/arm64/include/asm/unistd.h to decode these correctly. This portability is achieved through conditional compilation in strace's source code, ensuring accurate interception across supported platforms without architecture-specific user intervention.[9]Tracing System Calls and Signals
Strace captures system calls by intercepting both their entry and exit points during execution. Upon entry, it logs the system call name along with its arguments, such as file descriptors, paths, or buffer sizes for operations likeread or write. On exit, it records the return value, which may indicate success (e.g., a file descriptor) or failure through the errno value, enabling diagnosis of errors like permission denied or no such file. This tracing extends to various syscall categories, including file I/O functions like open, read, and close; process management calls such as fork and execve; as well as network, inter-process communication, and memory allocation routines.[2]
In addition to system calls, strace intercepts and logs signals received by the traced process, providing insight into asynchronous events that may interrupt normal execution. It records details such as the signal type (e.g., SIGSEGV for segmentation faults or SIGINT for interrupt signals), the timing of delivery relative to other events, and whether a signal handler is invoked if one is set. Strace distinguishes between pending signals, which are queued but not yet delivered, and blocked signals, which are masked by the process's signal disposition, helping to trace issues like race conditions or unhandled interruptions.[2]
The tool's processing logic involves decoding the raw data from kernel interfaces into human-readable output. It translates low-level arguments, such as pointer values, into interpretable strings by reading the tracee's memory using ptrace requests like PTRACE_PEEKDATA to fetch and decode paths or strings passed to syscalls. This decoding occurs in real-time, ensuring that the logged information reflects the actual parameters and outcomes without requiring manual interpretation of kernel-level details.[2]
For multi-process and multi-threaded applications, strace handles tracing across process forks and thread creations by following child processes and threads. It prefixes log entries with the process ID (PID) or thread ID (TID) to clarify which entity generated each event, maintaining chronological order while marking any unfinished syscalls that span across these creations. This capability ensures comprehensive visibility into complex execution flows without losing context in concurrent environments.[2]
Usage Guide
Command-Line Options
The basic syntax for invoking strace isstrace [options] command [args], where the command and its arguments are traced, or alternatively strace -p [pid](/page/PID) [options] to attach to an existing process without specifying a command.[2] Key options include -p [pid](/page/PID), which attaches strace to the process with the specified process ID (PID) for tracing; multiple -p options can be used to attach to several processes, and the command is optional if at least one -p is provided.[2] The -f option enables tracing of child processes as they are created via fork, vfork, or clone system calls, allowing observation of process hierarchies.[2] Additionally, -o file directs the trace output to the specified file instead of standard error, with the option to use a format like filename.[pid](/page/PID) when combined with -ff for multi-process tracing.[2]
Filtering options provide fine-grained control over what is traced. The -e trace=qualifier option restricts tracing to specific categories of system calls, such as network for network-related calls, or excludes them with a leading !; common qualifiers include file for file operations (e.g., open, read, write) and ipc for inter-process communication calls (e.g., msgsnd, semop).[2] Similarly, -e signal=set controls signal tracing, where none excludes all signals or specific ones like !SIGIO can be omitted; by default, all signals are traced.[2]
Advanced flags enhance output analysis and customization. The -c option counts the occurrences of each system call, along with their total time and error rates, printing a summary upon exit to identify frequently used or problematic calls.[2] For timing precision, -r prints relative timestamps in microseconds (default) between consecutive system calls, aiding in performance bottleneck identification.[2] The -s strsize flag limits the displayed length of strings passed to system calls, defaulting to 32 characters, to prevent verbose output from long arguments.[2] Credentials can be adjusted with -u username to run the traced command with the user ID, group ID, and supplementary groups of the specified user (requiring root privileges); alternatively, -u UID:GID sets exact user and group IDs without supplementary groups for more granular control.[2]
Error handling and injection options support debugging edge cases. The -E var=val option unsets or sets environment variables for the traced command, useful for isolating variable influences on behavior.[2] For simulating concurrency issues, -I level adjusts signal interruptibility during tracing: level 1 allows interrupts anywhere, level 2 (default) only during waits, level 3 prevents delivery except for SIGKILL, and level 4 also blocks SIGTSTP; higher levels help test race conditions by injecting controlled delays.[2]
Interpreting Output
The output of strace is a detailed log of system calls and signals, presented in a line-by-line format that captures the interaction between a traced process and the kernel. Each line typically begins with an optional timestamp, followed by the process ID (if multiple processes are traced with -f), the system call name, its arguments enclosed in parentheses, and the return value after an equals sign. For instance, a successful file open might appear asopenat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3, indicating the file descriptor 3 was returned, while an error could show openat(AT_FDCWD, "/nonexistent", O_RDONLY) = -1 ENOENT (No such file or directory), where the negative return value signals failure and the errno symbol with its descriptive string provides the specific reason.[2]
Timestamps enhance the interpretability of the log by revealing timing information, which is crucial for identifying performance bottlenecks such as slow I/O operations or excessive context switches. The -t option adds absolute wall-clock timestamps to each line in seconds since the epoch, while -tt provides higher precision with microseconds; for example, 14:23:45.123456 open(...) = 3. To focus on delays between calls, the -r option prepends relative timestamps (defaulting to microseconds resolution) before and after each system call, allowing users to spot latencies, such as a long pause during a read from a network socket. These timing features help correlate slow syscalls with application behavior, though they introduce slight overhead due to the tracing mechanism.[2]
Errors in the output are decoded systematically to aid diagnosis, with failed syscalls returning -1 followed by the errno constant and its human-readable explanation, drawn from the system's error definitions; for example, access("/protected", F_OK) = -1 EACCES (Permission denied) clearly indicates an access control issue without requiring manual lookup of numeric codes. Unfinished syscalls, often due to signals interrupting execution, are marked with <unfinished ...> on one line and resumed on a subsequent line with ... <resumed> ... = return_value. Signal deliveries interrupt the trace and are prefixed with ---, followed by the signal name and a decoded structure containing details like the signal number, code, and source process ID, such as --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=1234} ---, enabling analysis of how signals affect program flow.[2]
For efficient parsing and analysis of large logs, strace offers options to aggregate data rather than reviewing every line manually. The -c flag produces a summary upon process exit, listing each syscall's invocation count, total time spent (in milliseconds), average time per call, and error counts, sorted by time descending—for example, revealing that poll accounted for 80% of execution time in a network-heavy application—while suppressing the verbose trace. Decoding enhancements like -y (to resolve file paths from descriptors) and -X verbose (to print both raw numbers and named constants for arguments) make the raw output more interpretable without external tools, particularly for complex structures like socket addresses. These features support both human review and scripted post-processing for deeper insights into syscall patterns.[2]
Practical Examples
Basic Debugging Scenarios
Strace serves as an essential tool for diagnosing common issues in program execution by tracing system calls at the kernel interface. In basic debugging scenarios, it allows users to pinpoint problems without needing to modify the source code, focusing on straightforward, single-process interactions. By selectively tracing specific system calls or attaching to running processes, developers can observe file operations, process lifecycle events, and potential blocking behaviors that cause failures or hangs.[2] One frequent application is identifying file access issues, such as missing files or permission errors, where a program unexpectedly fails to read or write data. For instance, runningstrace -e trace=file ls limits output to file-related system calls like open, stat, and read, revealing errors such as open("missing.txt", O_RDONLY) = -1 ENOENT (No such file or directory). This approach helps isolate whether the issue stems from incorrect paths, insufficient permissions, or absent dependencies without sifting through extraneous traces.[2]
To detect hangs or freezes in a running process, strace can attach to an existing process ID using strace -p PID, capturing ongoing system calls and highlighting prolonged or unfinished operations. A common indicator is a system call like poll([{fd=3, events=POLLIN}], 1, -1) <unfinished ...> that remains unresolved, suggesting an infinite wait for input on a file descriptor, such as a network socket or pipe. This real-time attachment enables quick diagnosis of resource contention or external dependencies causing the stall.[2]
Environment variable problems, often leading to misconfigured program behavior, can be debugged by tracing process-related system calls with strace -e trace=process ./program. The output displays the execve call, including the argument vector and environment pointer, for example: execve("./program", ["./program"], 0x7ffd1234 /* 42 vars */) = 0, allowing verification of passed variables like PATH or custom settings that might be omitted or malformed. Such traces confirm if the program receives the expected runtime context during invocation.[2]
Startup failures, particularly those involving dynamic library loading, are effectively uncovered by tracing from process launch with strace ./binary. This captures initial system calls, such as repeated open attempts for shared objects like open("libxyz.so", O_RDONLY) = -1 ENOENT (No such file or directory), indicating unresolved dependencies or linker errors without delving into application logic. By focusing on the early execution phase, users can resolve issues like missing libraries or incorrect loader paths swiftly.[2]
Advanced Analysis Cases
Strace enables advanced analysis in complex debugging environments by leveraging targeted options to profile performance, trace multithreaded behaviors, integrate with debuggers, and audit security-related interactions. These capabilities allow users to dissect intricate application behaviors that go beyond basic tracing, such as identifying bottlenecks in high-load scenarios or verifying compliance with security policies. For performance profiling, the-c option counts the number of calls, errors, and total time spent in each system call, providing a summary report upon program exit that helps pinpoint resource-intensive operations. Combining -c with -T, which displays the time spent inside each system call (in microseconds by default), reveals I/O bottlenecks; for instance, executing strace -c -T ./[program](/page/Program) on a file-heavy application might show that read and write syscalls consume over 80% of execution time, indicating potential disk or network delays. This approach is particularly useful for tuning applications where kernel interactions dominate runtime, as the summary output sorts syscalls by time or calls to highlight outliers.[2]
In multi-threaded applications, the -f option traces all child processes and threads created via fork, vfork, or clone, ensuring comprehensive visibility into concurrent execution. To focus on thread scheduling and contention issues like locks, use -e trace=sched to filter for scheduling-related syscalls such as sched_yield or futex; for example, strace -f -e trace=sched ./multi_thread_app can expose frequent context switches or waiting patterns in a producer-consumer setup, aiding in optimization of synchronization primitives. This tracing reveals how threads interact with the kernel scheduler, often uncovering inefficiencies in lock contention without requiring source code access.[2][10]
Strace integrates effectively with GDB for syscall-aware debugging, but due to ptrace limitations allowing only one tracer per process, direct simultaneous attachment to the same PID is not possible and will result in conflicts. Instead, users can trace sequentially—running strace first to observe syscalls, then attaching GDB—or employ extensions like an enhanced gdbserver that supports both GDB and strace clients concurrently. For example, with such a setup, strace output can inform GDB breakpoints on specific kernel interactions, such as halting during unexpected open calls, providing valuable context for user-space debugging in scenarios requiring both syscall monitoring and code-level inspection.[11][12]
For security auditing, filtering with -e trace=network,ipc limits output to network and inter-process communication syscalls, facilitating inspection of potential unauthorized accesses. Executing strace -e trace=network,ipc ./app on a networked service might reveal unintended connect calls to external hosts or shmget operations for shared memory, helping detect exfiltration attempts or privilege escalations. This targeted tracing supports compliance checks by logging only relevant kernel interfaces, such as socket creations or semaphore operations, without overwhelming output from unrelated syscalls.[2]
Limitations and Alternatives
Performance and Security Considerations
Strace introduces significant performance overhead when tracing processes, primarily due to its reliance on the ptrace system call, which causes the tracee to stop and resume execution for each system call and signal, resulting in slowdowns ranging from 10x to over 100x in severe cases.[13][14] This overhead arises from the frequent context switches and user-kernel interactions inherent to ptrace, making strace unsuitable for high-throughput or real-time applications without careful limitation.[8] To mitigate this, users can employ the-e trace=set option to restrict tracing to a minimal subset of system calls relevant to the analysis, or leverage the --seccomp-bpf feature in modern versions, which filters syscalls at the kernel level using Berkeley Packet Filter programs, substantially reducing the number of ptrace interventions.[2]
In terms of resource consumption, verbose strace traces on long-running processes can lead to high CPU utilization from continuous monitoring and output generation, as well as elevated memory usage for buffering extensive logs, potentially exacerbating system load during prolonged sessions.[14] For this reason, it is recommended to apply strace in short bursts targeted at specific phases of execution rather than continuously on production workloads, allowing for analysis without overwhelming system resources.[2]
Security considerations with strace stem from its ptrace-based attachment, which requires appropriate permissions—typically root privileges or same-user access—and can inadvertently expose sensitive data, such as passwords passed as command-line arguments in system calls like execve or open, directly in the trace output.[15][16] Users must handle logs securely, avoiding storage or sharing without redaction, especially in environments where confidential information might be traced.[17]
On modern Linux kernels, ptrace permissions for strace are governed by the Yama security module's /proc/sys/kernel/yama/[ptrace_scope](/page/ptrace_scope) setting, which restricts attachments to child processes or processes with the same effective user ID by default (value 1), requiring administrative intervention to broaden access via echo 0 > /proc/sys/kernel/yama/[ptrace_scope](/page/ptrace_scope) or equivalent sysctl configuration.[18] Misuse of these privileges, such as indiscriminate tracing, can enable denial-of-service attacks by halting target processes indefinitely through ptrace stops, underscoring the need for Yama enforcement in production systems to limit exposure.[19][20]
Comparable Tools
On Linux, ltrace serves as an alternative to strace by intercepting and recording dynamic library calls made by executed processes, rather than focusing on system calls.[21] This makes ltrace particularly useful for debugging issues related to user-space library interactions, such as those involving libc functions, without delving into kernel-level operations.[21] Another Linux option is perf trace, a component of the perf tools suite that provides kernel-integrated tracing of system events, including system calls, using tracepoints, kprobes, and uprobes.[22] Unlike strace, which relies on ptrace for per-process attachment, perf trace offers lower overhead through direct access to kernel instrumentation, enabling efficient capture of events like scheduler activity or I/O with timestamps and stack traces.[22][23] On Unix variants, truss functions similarly to strace in Solaris and illumos environments by tracing system calls, received signals, and machine faults for a specified command or process.[24][25] It supports options for filtering specific calls or faults and counting occurrences, aiding in diagnostics for hung processes or unexpected behaviors.[24] In BSD systems like FreeBSD and NetBSD, ktrace enables kernel-level tracing of system calls, namei translations, signal processing, and I/O for targeted processes, logging data to a file for later analysis with kdump.[26][27] Cross-platform tools extend tracing capabilities beyond basic system call logging. DTrace, originally developed for Solaris and ported to FreeBSD, provides a comprehensive dynamic tracing framework with scriptable probes that instrument kernel and user-space code without recompilation or rebooting.[28][29] It supports aggregations, predicates, and actions for complex analysis, such as performance bottleneck identification across the stack.[28] On modern Linux, eBPF-based tools like bpftrace offer efficient, high-level tracing via a scripting language that compiles to eBPF programs, allowing probes on kernel functions, tracepoints, and user-space events with minimal runtime overhead.[30] This enables one-liners for tasks like monitoring file opens or network packets, surpassing strace in scalability for production environments.[30]| Tool | Scope | Key Strengths | When to Choose Over strace |
|---|---|---|---|
| ltrace | Dynamic library calls | Focuses on user-space library debugging | For library-specific issues, not kernel interactions |
| perf trace | Kernel events via tracepoints | Lower overhead, integrated stack traces | High-volume event tracing with efficiency |
| truss | System calls, signals, faults | Platform-specific filtering and counts | Solaris/illumos diagnostics |
| ktrace | Kernel operations (calls, I/O) | File-based logging for post-analysis | BSD process monitoring |
| DTrace | Scriptable kernel/user probes | Flexible aggregations and predicates | Complex, cross-layer analysis in Solaris/FreeBSD |
| bpftrace | eBPF probes on events/functions | Efficient scripting, production-safe | Scalable Linux tracing without high overhead |