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

# Regex Patterns

Regex patterns provide powerful pattern matching for syscall filtering. Instead of listing individual syscalls or using predefined classes, you can use regular expressions to match syscall names flexibly.

## Why Use Regex?

### Without Regex

    # Manually list all *at variants
    renacer -e 'trace=openat,fstatat,renameat,linkat,symlinkat,readlinkat,unlinkat' -- ls

Problem: Tedious, error-prone, easy to miss some syscalls.

### With Regex

    # Match all syscalls ending with "at"
    renacer -e 'trace=/.*at$/' -- ls

Result: Automatically matches all *at syscalls.

## Regex Syntax

### Pattern Delimiters

Regex patterns must be enclosed in forward slashes: `/pattern/`

    renacer -e 'trace=/pattern/' -- command

Valid:

    renacer -e 'trace=/^open.*/' -- ls

Invalid:

    renacer -e 'trace=^open.*' -- ls  # Missing slashes

### Supported Regex Features

Renacer uses the Rust `regex` crate (Perl-compatible):

- Anchors: `^` (start), `$` (end)
- Wildcards: `.` (any char), `.*` (any chars)
- Character classes: `[abc]`, `[0-9]`, `[a-z]`
- Quantifiers: `*` (0+), `+` (1+), `?` (0-1), `{n}`, `{n,m}`
- Groups: `(...)`, `(?:...)` (non-capturing)
- Alternation: `|` (OR)
- Case-insensitive: `(?i)pattern`

## Basic Patterns

### Prefix Matching

Match syscalls starting with a pattern:

    $ renacer -e 'trace=/^open.*/' -- ls
    openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
    # Matches: open, openat, open_by_handle_at

Regex: `/^open.*/`

- `^` - Start of string
- `open` - Literal "open"
- `.*` - Any characters (0 or more)

### Suffix Matching

Match syscalls ending with a pattern:

    $ renacer -e 'trace=/.*at$/' -- ls
    openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
    fstatat(AT_FDCWD, "file.txt", {...}, 0) = 0
    renameat(AT_FDCWD, "old.txt", AT_FDCWD, "new.txt") = 0
    # Matches: openat, fstatat, renameat, linkat, etc.

Regex: `/.*at$/`

- `.*` - Any characters (0 or more)
- `at` - Literal "at"
- `$` - End of string

### Exact Match

Match syscalls exactly:

    $ renacer -e 'trace=/^read$/' -- cat file.txt
    read(3, "contents...\n", 131072) = 13
    # Matches only "read", not "readv", "pread64", etc.

Regex: `/^read$/`

- `^` - Start
- `read` - Literal "read"
- `$` - End

### Substring Match

Match syscalls containing a pattern:

    $ renacer -e 'trace=/stat/' -- ls
    fstat(3, {...}) = 0
    fstatat(AT_FDCWD, "file.txt", {...}, 0) = 0
    # Matches any syscall with "stat" anywhere: stat, fstat, lstat, fstatat, etc.

## OR Patterns

### Multiple Alternatives

    $ renacer -e 'trace=/read|write/' -- cat file.txt > output.txt
    read(3, "contents...\n", 131072) = 13
    write(4, "contents...\n", 13) = 13
    # Matches: read, readv, pread64, write, writev, pwrite64, etc.

Regex: `/read|write/`

- `read` - First alternative
- `|` - OR operator
- `write` - Second alternative

### Specific Alternatives

    $ renacer -e 'trace=/^(read|write|close)$/' -- ./app
    read(3, ...) = 42
    write(4, ...) = 42
    close(3) = 0
    # Matches ONLY: read, write, close (exact matches)

Regex: `/^(read|write|close)$/`

- `^` - Start
- `(read|write|close)` - Exact alternatives
- `$` - End

## Advanced Patterns

### Case-Insensitive

    $ renacer -e 'trace=/(?i)OPEN/' -- ls
    open("/etc/ld.so.cache", ...) = 3
    openat(AT_FDCWD, ".", ...) = 3
    # Matches: open, OPEN, Open, oPeN, etc.

Regex: `/(?i)OPEN/`

- `(?i)` - Case-insensitive flag
- `OPEN` - Pattern (matches any case)

### Character Classes

    $ renacer -e 'trace=/^[rw].*/' -- cat file.txt > output.txt
    read(3, ...) = 42
    write(4, ...) = 42
    # Matches syscalls starting with 'r' or 'w'

Regex: `/^[rw].*/`

- `^` - Start
- `[rw]` - 'r' OR 'w'
- `.*` - Any characters

### Quantifiers

    $ renacer -e 'trace=/^.{4,6}$/' -- ./app
    read(3, ...) = 42    # 4 chars
    write(4, ...) = 42   # 5 chars
    close(3) = 0         # 5 chars
    # Matches syscalls with 4-6 character names

Regex: `/^.{4,6}$/`

- `^` - Start
- `.{4,6}` - Any 4-6 characters
- `$` - End

## Combining Regex with Other Filters

### Regex + Classes

    $ renacer -e 'trace=file,/^recv.*/' -- wget https://example.com
    # Matches all file operations + recv-related syscalls
    openat(...) = 3        # From 'file' class
    read(...) = 1024       # From 'file' class
    recvfrom(...) = 512    # From regex /^recv.*/

### Regex + Literals

    $ renacer -e 'trace=/^open.*/,close,read' -- ./app
    # Matches: open*, close (exact), read (exact)
    openat(...) = 3        # From regex
    open(...) = 4          # From regex
    close(3) = 0           # Literal
    read(4, ...) = 42      # Literal

### Regex + Negation

    $ renacer -e 'trace=/^open.*/,!/openat/' -- ls
    open("/etc/ld.so.cache", ...) = 3
    # Matches open* EXCEPT openat

Process:

1. `/^open.*/` - Include all syscalls starting with "open"
2. `!/openat/` - Exclude "openat" specifically

## Common Use Cases

### 1. All Variants of a Syscall

Problem: Want all read variants (read, readv, pread64, etc.)

    $ renacer -e 'trace=/^read/' -- ./app
    read(3, ...) = 1024
    readv(4, ...) = 2048
    pread64(5, ..., 0) = 512

### 2. Modern *at Syscalls

Problem: Focus on *at syscalls (modern POSIX API)

    $ renacer -e 'trace=/.*at$/' -- ./app
    openat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3
    fstatat(AT_FDCWD, "file.txt", {...}, 0) = 0
    renameat(AT_FDCWD, "old", AT_FDCWD, "new") = 0

### 3. Short Syscall Names

Problem: Filter to simple syscalls (short names)

    $ renacer -e 'trace=/^.{1,5}$/' -- ./app
    open(...) = 3   # 4 chars
    read(...) = 42  # 4 chars
    write(...) = 42 # 5 chars
    close(...) = 0  # 5 chars
    # Excludes: openat (6), fstatat (7), etc.

### 4. Network Send/Receive

Problem: All network send and receive operations

    $ renacer -e 'trace=/send|recv/' -- curl https://api.example.com
    sendto(...) = 120
    recvfrom(...) = 1024
    sendmsg(...) = 256
    recvmsg(...) = 512

## Troubleshooting

### Issue: Regex Not Matching

Symptoms:

    $ renacer -e 'trace=^open.*' -- ls
    # No output or error

Cause: Missing slashes around regex pattern.

Fix:

    $ renacer -e 'trace=/^open.*/' -- ls

### Issue: Too Many Matches

Symptoms:

    $ renacer -e 'trace=/stat/' -- ls
    # Matches stat, fstat, lstat, fstatat, newfstatat, statfs, etc.

Cause: Pattern too broad.

Fix: Be more specific:

    $ renacer -e 'trace=/^stat$/' -- ls
    # Matches ONLY "stat" (exact)

### Issue: Invalid Regex Error

Symptoms:

    $ renacer -e 'trace=/[/' -- ls
    Error: Invalid regex pattern: unclosed character class

Cause: Malformed regex syntax.

Fix: Check regex syntax:

    $ renacer -e 'trace=/\[/' -- ls  # Escape special chars

## Performance Considerations

### Regex Compilation Cost

Renacer compiles regex patterns once at startup. No per-syscall regex cost.

    # Fast: Regex compiled once
    renacer -e 'trace=/^open.*/' -- ./app

### Specific vs. Broad Patterns

    # Faster: Specific pattern
    renacer -e 'trace=/^openat$/' -- ./app

    # Slower: Broad pattern matching many syscalls
    renacer -e 'trace=/.*/' -- ./app  # Matches everything

Tip: Use specific patterns when possible.

## Best Practices

### 1. Start Simple

    # Start with simple substring match
    renacer -e 'trace=/read/' -- ./app

    # Refine to prefix if needed
    renacer -e 'trace=/^read/' -- ./app

    # Make exact if too broad
    renacer -e 'trace=/^read$/' -- ./app

### 2. Test Regex Separately

    # Test your regex pattern
    $ echo "openat" | grep -E '^open.*'
    openat

    $ echo "read" | grep -E '^open.*'
    # No match

### 3. Quote Your Patterns

    # Always quote filter expressions
    renacer -e 'trace=/^open.*/' -- ./app

Why: Prevents shell from interpreting regex special characters.

### 4. Use Negation for Exclusion

    # Include broad pattern, exclude specific
    renacer -e 'trace=/^open.*/,!/openat/' -- ./app

Why: More maintainable than complex negative lookaheads.

## Examples Gallery

### Match All File Descriptors Operations

    $ renacer -e 'trace=/^(dup|fcntl|ioctl)/' -- ./app
    dup2(3, 4) = 4
    fcntl(5, F_GETFD) = 0
    ioctl(6, TIOCGWINSZ, {...}) = 0

### Match Memory Operations

    $ renacer -e 'trace=/^m(map|unmap|protect|advise)/' -- ./app
    mmap(NULL, 262144, PROT_READ|PROT_WRITE, ...) = 0x7f...
    munmap(0x7f..., 262144) = 0
    mprotect(0x7f..., 4096, PROT_READ) = 0

### Match Asynchronous I/O

    $ renacer -e 'trace=/^(poll|select|epoll)/' -- ./app
    poll([{fd=3, events=POLLIN}], 1, 1000) = 1
    epoll_wait(4, [{...}], 128, -1) = 1

## Summary

Regex patterns enable flexible syscall filtering:

- Syntax: `/pattern/` (slashes required)
- Anchors: `^` (start), `$` (end)
- Wildcards: `.` (any), `.*` (any sequence)
- OR: `|` for alternatives
- Case-insensitive: `(?i)pattern`

Common Patterns:

- Prefix: `/^open.*/` - Matches open, openat, etc.
- Suffix: `/.*at$/` - Matches *at syscalls
- OR: `/read|write/` - Matches read OR write
- Exact: `/^read$/` - Matches only "read"

Combine with:

- Classes: `trace=file,/^recv.*/`
- Literals: `trace=/^open.*/,close`
- Negation: `trace=/^open.*/,!/openat/`

Next Steps:

- Negation Operator - Exclude patterns
- Syscall Classes - Predefined groups
- Filtering Syscalls - Main filtering guide