Understanding Output - The Renacer Book - Pure Rust System Call Tracing
Description: A comprehensive guide to renacer: syscall tracing, DWARF correlation, and performance analysis

# Understanding Output

Once you've traced a program with Renacer, you'll see lines showing system calls. This chapter explains how to read and interpret that output.

## Output Format

Every traced system call follows this format:

    syscall_name(arg1, arg2, arg3, ...) = return_value

### Example Breakdown

    openat(AT_FDCWD, "/etc/passwd", O_RDONLY|O_CLOEXEC) = 3

Parts:

- `openat`: Syscall name (opens a file)
- `AT_FDCWD`: First argument (use current working directory)
- `"/etc/passwd"`: Second argument (file path to open)
- `O_RDONLY|O_CLOEXEC`: Third argument (flags - read-only + close-on-exec)
- `= 3`: Return value (new file descriptor number)

## Common Syscalls and Their Arguments

### File Operations

#### `openat` - Open a File

    openat(AT_FDCWD, "/path/to/file", O_RDONLY) = 3

- arg1: Directory FD (`AT_FDCWD` = current directory)
- arg2: Path to open
- arg3: Flags (`O_RDONLY`, `O_WRONLY`, `O_CREAT`, etc.)
- return: New file descriptor, or `-ENOENT` on error

#### `read` - Read from File

    read(3, "file contents...", 4096) = 42

- arg1: File descriptor to read from
- arg2: Buffer (contents shown as string if printable)
- arg3: Maximum bytes to read
- return: Actual bytes read

#### `write` - Write to File

    write(1, "Hello\n", 6) = 6

- arg1: File descriptor (1 = stdout, 2 = stderr)
- arg2: Buffer contents to write
- arg3: Number of bytes
- return: Bytes actually written

#### `close` - Close File

    close(3) = 0

- arg1: File descriptor to close
- return: 0 on success, -errno on error

### Memory Operations

#### `mmap` - Memory Mapping

    mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8a2c000000

- arg1: Preferred address (NULL = let kernel choose)
- arg2: Size in bytes
- arg3: Protection flags (read/write/execute)
- arg4: Mapping type (private/shared, file/anonymous)
- arg5: File descriptor (or -1 for anonymous)
- arg6: Offset in file
- return: Address of mapped memory

#### `brk` - Change Data Segment Size

    brk(0x55e8f1a2d000) = 0x55e8f1a2d000

- arg1: New end address of data segment
- return: New break address on success

### Process Operations

#### `clone` - Create Child Process/Thread

    clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD) = 12345

- child_stack: Stack for new process/thread
- flags: Control behavior (process vs thread, signal handling, etc.)
- return: PID of child (in parent), 0 (in child)

#### `wait4` - Wait for Process

    wait4(12345, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 12345

- arg1: PID to wait for (-1 = any child)
- arg2: Status information (exit code, signal)
- arg3: Options (e.g., `WNOHANG`)
- arg4: Resource usage (or NULL)
- return: PID of terminated child

#### `execve` - Execute Program

    execve("/bin/ls", ["ls", "-la"], [/* 48 vars */]) = 0

- arg1: Path to executable
- arg2: Argument array
- arg3: Environment variables
- return: Doesn't return on success (replaces process)

### Network Operations

#### `socket` - Create Socket

    socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3

- arg1: Address family (`AF_INET` = IPv4, `AF_INET6` = IPv6)
- arg2: Socket type (`SOCK_STREAM` = TCP, `SOCK_DGRAM` = UDP)
- arg3: Protocol (usually 0 for default)
- return: Socket file descriptor

#### `connect` - Connect to Address

    connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("93.184.216.34")}, 16) = 0

- arg1: Socket file descriptor
- arg2: Address structure (IP + port)
- arg3: Address structure size
- return: 0 on success, -errno on error

#### `sendto` / `recvfrom` - Send/Receive Data

    sendto(3, "GET / HTTP/1.1\r\n", 16, 0, NULL, 0) = 16
    recvfrom(3, "HTTP/1.1 200 OK\r\n...", 4096, 0, NULL, NULL) = 1234

## Return Values

### Success

Positive numbers or zero indicate success. The meaning depends on the syscall:

Return Value | Meaning
--- | ---
`= 0` | Success with no data (e.g., `close`, `execve`)
`= 3` | File descriptor number (e.g., `open`, `socket`)
`= 42` | Bytes read/written (e.g., `read`, `write`)
`= 12345` | Process ID (e.g., `fork`, `clone`)
`= 0x7f8a...` | Memory address (e.g., `mmap`)

### Errors

Negative values are errno codes (errors):

Error Code | Meaning | Common Causes
--- | --- | ---
`-ENOENT` | No such file/directory | File doesn't exist
`-EACCES` | Permission denied | Insufficient permissions
`-EAGAIN` | Try again | Resource temporarily unavailable
`-EINTR` | Interrupted | Signal interrupted syscall
`-ENOMEM` | Out of memory | System out of RAM
`-ECONNREFUSED` | Connection refused | Server not listening

Example:

    openat(AT_FDCWD, "/nonexistent", O_RDONLY) = -ENOENT

This means: Attempted to open "/nonexistent", but file doesn't exist.

## Flags and Constants

### File Open Flags

Flag | Meaning
--- | ---
`O_RDONLY` | Open read-only
`O_WRONLY` | Open write-only
`O_RDWR` | Open read-write
`O_CREAT` | Create if doesn't exist
`O_TRUNC` | Truncate to zero length
`O_APPEND` | Append to end
`O_NONBLOCK` | Non-blocking I/O
`O_CLOEXEC` | Close on exec()

Combined with OR (`|`):

    O_RDWR|O_CREAT|O_TRUNC  = open for read/write, create if missing, truncate if exists

### Memory Protection Flags

Flag | Meaning
--- | ---
`PROT_READ` | Pages may be read
`PROT_WRITE` | Pages may be written
`PROT_EXEC` | Pages may be executed
`PROT_NONE` | Pages may not be accessed

### Special File Descriptors

FD | Standard Name | Purpose
--- | --- | ---
0 | stdin | Standard input
1 | stdout | Standard output
2 | stderr | Standard error
3+ | - | Opened files/sockets

## Data Representation

### Strings

Readable strings are shown in quotes:

    write(1, "Hello, World!\n", 14) = 14

Binary data is shown in hex or abbreviated:

    read(3, "\x7fELF\x02\x01\x01...", 4096) = 4096

### Structs

Complex structures are abbreviated:

    fstat(3, {st_mode=S_IFREG|0644, st_size=1234, ...}) = 0

Use `-v` (verbose) flag for full struct details (not yet implemented in v0.4.1).

### Arrays

Arrays and buffers are shown as:

    getdents64(3, [{d_ino=123, d_name="file1.txt"}, ...], 32768) = 1024

Large arrays are abbreviated with `[...]`.

## Timing Information

With statistics mode (`-c`), see timing:

    System Call Summary:
    ====================
    Syscall          Calls    Errors    Total Time    Avg Time
    openat           5        1         2.345ms       0.469ms
    read             150      0         45.123ms      0.301ms
    write            150      0         12.456ms      0.083ms

Columns:

- Calls: Number of times called
- Errors: Number of failed calls
- Total Time: Cumulative time spent
- Avg Time: Average per call

## Source Correlation (with --source)

When tracing Rust binaries with debug symbols:

    renacer --source -- ./my-program

Enhanced output:

    read(3, buf, 1024) = 42          [src/main.rs:15 in my_function]
    write(1, "result", 6) = 6        [src/main.rs:20 in my_function]

The `[filename:line in function]` shows where in your source code the syscall originated.

## Filtering Output

Showing only certain syscalls makes output more readable:

### File Operations Only

    renacer -e 'trace=file' -- ls

Output:

    openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
    close(3) = 0

### Specific Syscalls

    renacer -e 'trace=open,read,write' -- cat file.txt

Only shows `open`, `read`, and `write` calls.

## Multi-Process Output

With `-f` (follow forks):

    [pid 12345] clone(...) = 12346
    [pid 12346] execve("/bin/ls", ...) = 0
    [pid 12346] openat(...) = 3
    [pid 12345] wait4(12346, ...) = 12346

Each line is prefixed with `[pid XXXXX]` to distinguish processes.

## Common Patterns

### Successful File Read

    openat(AT_FDCWD, "/path/file", O_RDONLY) = 3
    read(3, "contents...", 4096) = 1234
    close(3) = 0

Interpretation: Opened file successfully (fd=3), read 1234 bytes, closed cleanly.

### Failed File Access

    openat(AT_FDCWD, "/missing/file", O_RDONLY) = -ENOENT

Interpretation: Tried to open file, but it doesn't exist.

### Memory Allocation

    brk(NULL) = 0x55e8f1a00000
    brk(0x55e8f1a21000) = 0x55e8f1a21000

Interpretation: Check current heap end, then extend it by ~132KB.

### Network Connection

    socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
    connect(3, {sa_family=AF_INET, sin_port=htons(80), ...}, 16) = 0
    sendto(3, "GET / HTTP/1.1\r\n", 16, 0, NULL, 0) = 16
    recvfrom(3, "HTTP/1.1 200 OK\r\n...", 4096, 0, NULL, NULL) = 512
    close(3) = 0

Interpretation: Created TCP socket, connected to port 80, sent HTTP request, received response, closed connection.

## Tips for Reading Output

1. Start from the top: Syscalls are sequential - read chronologically
2. Look for patterns: Repeated sequences often indicate loops
3. Check return values: Negative values are errors
4. Note file descriptors: Track which FDs are open/closed
5. Use filtering: Too much output? Filter to what matters
6. Enable source correlation: `--source` helps understand "why"

## Next Steps

- Filtering Syscalls - Focus on specific syscalls
- Statistics Mode - Aggregate analysis
- DWARF Source Correlation - Map to source code
- Examples - Real-world usage patterns