funwithlinux blog

How to Show Complete Arguments in strace (Even with Curly Brackets) When -s Isn't Enough

strace is an indispensable tool for debugging and understanding how programs interact with the Linux kernel. It traces system calls (syscalls) and signals, revealing critical details like file access, network requests, and process execution. However, a common frustration arises when strace truncates long arguments—especially those containing special characters like curly brackets ({})—making it hard to debug complex commands or scripts.

By default, strace limits the length of printed strings to 32 characters, truncating longer ones with .... While the -s flag lets you increase this limit, there are scenarios where even -s feels insufficient: nested syscalls, fork/exec workflows, or brace-expanded shell commands. This blog dives into why truncation happens, why -s might not be enough, and actionable solutions to capture complete arguments—including curly brackets.

2026-01

Table of Contents#

  1. Understanding strace’s Default Argument Truncation
  2. Why -s Might Not Be Enough (Or Seem That Way)
  3. Solutions to Show Complete Arguments
  4. Special Case: Capturing Brace Expansion and Curly Brackets
  5. Advanced: Patching strace (For Edge Cases)
  6. Conclusion
  7. References

1. Understanding strace’s Default Argument Truncation#

What is strace?#

strace intercepts and logs syscalls made by a process (e.g., open(), read(), execve()). For string arguments (like file paths or command-line args), it prints a truncated version by default to keep output manageable.

Default String Truncation: The -s Flag#

By default, strace truncates strings to 32 characters. This is controlled by the -s (string limit) flag, which defaults to 32. For example, if a process calls write(STDOUT_FILENO, "Hello {world}, this is a long argument...", ...), strace will print:

write(1, "Hello {world}, this is a long arg"..., 56) = 56

Here, the string is cut off at 32 characters, with ... indicating truncation.

Example: Truncation with Curly Brackets#

Curly brackets are common in shell scripting (e.g., brace expansion: {a,b,c}) or configuration files. If an argument contains {}, truncation can hide critical context. Consider this Python script (long_arg.py):

import os
os.system('echo "Brace expansion: {apple,banana,cherry} and a very long string that exceeds 32 characters..."')

Running strace python3 long_arg.py (without -s) shows truncated output for the execve syscall (which launches the echo command):

execve("/bin/sh", ["sh", "-c", "echo \"Brace expansion: {apple,banana,ch"..., ...]) = 0

The full argument—including the {} and the rest of the string—is hidden!

2. Why -s Might Not Be Enough (Or Seem That Way)#

At first glance, increasing -s (e.g., -s 1024) should fix truncation. But in practice, users often hit walls due to:

  • Forked Processes: If the target process forks a child (common in shells, daemons, or scripts), the child may execute the syscall with the full argument, while the parent does not. strace without -f (follow forks) misses the child’s syscalls.
  • Brace Expansion: Shells like bash expand {a,b,c} into a b c before executing the command. If you trace the shell itself, you’ll see the unexpanded {}; to see the expanded arguments, you need to trace the child process (e.g., echo or mkdir).
  • Nested Syscalls: Some programs pass arguments via multiple syscalls (e.g., execveexecvp). Tracing only the top-level syscall may miss the full argument.

3. Solutions to Show Complete Arguments#

3.1 Increase -s to a Higher Value#

The simplest fix is to increase -s to a value larger than your longest expected argument. Use -s 1024, -s 4096, or even -s 10000 for extreme cases.

Example:#

Rerun the earlier Python script with -s 4096:

strace -s 4096 python3 long_arg.py

Now the execve syscall shows the full argument, including curly brackets:

execve("/bin/sh", ["sh", "-c", "echo \"Brace expansion: {apple,banana,cherry} and a very long string that exceeds 32 characters...\""], 0x7ffd6b8a2b80 /* 24 vars */) = 0

Pro Tip: There’s no strict upper limit for -s, but values above 10000 may slow down strace or clutter output. Use the smallest value that captures your arguments.

3.2 Follow Forks with -f to Capture Child Processes#

Many programs (e.g., shells, systemd, or scripts) fork child processes to execute commands. By default, strace only traces the parent. Use -f (follow forks) to trace child processes too.

Example: Tracing Brace Expansion#

Suppose you run bash -c 'mkdir {apple,banana,cherry}' and want to see the expanded mkdir arguments. Without -f, strace traces bash but not the mkdir child:

# Without -f: Only traces bash, shows unexpanded {apple,banana,cherry}
strace -s 4096 bash -c 'mkdir {apple,banana,cherry}'

With -f, strace follows the mkdir child and shows the expanded arguments:

# With -f: Traces both bash and the mkdir child
strace -f -s 4096 bash -c 'mkdir {apple,banana,cherry}'

Look for the execve syscall in the child process (marked with [pid 12345]):

[pid 12345] execve("/usr/bin/mkdir", ["mkdir", "apple", "banana", "cherry"], 0x7ffd6b8a2b80 /* 24 vars */) = 0

3.3 Focus on Key Syscalls (e.g., execve)#

Most arguments (command-line args, file paths) are passed via execve (or its variants like execvp). Tracing only execve with -e execve reduces noise and ensures you capture critical arguments.

Example:#

To trace only execve syscalls for the Python script:

strace -e execve -s 4096 python3 long_arg.py

Output is simplified to show only execve calls, making it easier to spot arguments:

execve("/usr/bin/python3", ["python3", "long_arg.py"], 0x7ffd6b8a2d00 /* 24 vars */) = 0
execve("/bin/sh", ["sh", "-c", "echo \"Brace expansion: {apple,banana,cherry} and a very long string that exceeds 32 characters...\""], 0x7f3c2a3a8e00 /* 24 vars */) = 0

3.4 Use Verbose Mode (-v) for Additional Context#

The -v (verbose) flag increases output detail, including environment variables and hidden syscall arguments. While it doesn’t directly affect string truncation (that’s still controlled by -s), it helps when arguments are passed via environment variables or complex data structures.

Example:#

Combine -v with -s to see environment variables alongside full arguments:

strace -v -s 4096 python3 long_arg.py

Output includes environment variables (e.g., PATH, HOME) and full syscall details, which can be critical for debugging environment-dependent issues.

4. Special Case: Capturing Brace Expansion and Curly Brackets#

Brace expansion (e.g., {a,b,c}) is handled by the shell, not the kernel. When you run mkdir {a,b,c}, the shell first expands {a,b,c} to a b c, then executes mkdir a b c. To capture the expanded arguments:

  1. Trace the child process (use -f to follow forks).
  2. Focus on execve (the syscall that launches mkdir with expanded args).

Step-by-Step Example:#

# Trace bash with -f and -e execve to capture the mkdir child
strace -f -e execve -s 4096 bash -c 'mkdir {apple,banana,cherry}'

Output shows the bash parent and mkdir child:

execve("/usr/bin/bash", ["bash", "-c", "mkdir {apple,banana,cherry}"], 0x7ffd6b8a2f00 /* 24 vars */) = 0
[pid 12345] execve("/usr/bin/mkdir", ["mkdir", "apple", "banana", "cherry"], 0x55f8d3b0a200 /* 24 vars */) = 0

Here, the child mkdir process has the expanded arguments apple, banana, cherry—no curly brackets in sight, because the shell already expanded them!

5. Advanced: Patching strace (For Edge Cases)#

In rare cases (e.g., proprietary software with extremely long arguments), even -s 100000 may not suffice. For these edge cases, you can patch strace to remove string truncation entirely by modifying its source code.

How to Patch:#

  1. Download the strace source:
    git clone https://github.com/strace/strace.git && cd strace
  2. Edit src/print_args.c to set default_strsize to a very high value (e.g., 1000000):
    // Change from:
    #define DEFAULT_STRSIZE 32
    // To:
    #define DEFAULT_STRSIZE 1000000
  3. Compile and install:
    ./configure && make && sudo make install

Warning: This is not recommended for most users, as it may slow down strace and produce unmanageably large output. Use only as a last resort.

6. Conclusion#

strace is a powerful tool, but its default string truncation can hide critical arguments—especially those with curly brackets or from child processes. To capture complete arguments:

  • Increase -s to a value larger than your longest argument (e.g., -s 4096).
  • Use -f to follow forked child processes (critical for brace expansion or scripts).
  • Focus on execve with -e execve to reduce noise and target command-line arguments.
  • Combine with -v for verbose context when needed.

With these techniques, you’ll never miss a curly bracket or truncated argument again!

7. References#