Go to file
2025-10-28 21:20:56 +00:00
src restrict to 4-8 byte integer types only 2025-10-28 21:20:56 +00:00
tests add integration tests part 2 2025-10-28 21:20:56 +00:00
vcpkg@3b9d086009 Initial commit 2025-10-24 10:40:03 +01:00
.gitignore refactor structure and add basic test 2025-10-24 10:43:58 +01:00
.gitmodules Initial commit 2025-10-24 10:40:03 +01:00
autotest.sh Simply autotest script 2025-10-28 21:20:56 +00:00
CMakeLists.txt update CMake to build test suite 2025-10-28 21:20:56 +00:00
CMakePresets.json Initial commit 2025-10-24 10:40:03 +01:00
README.md update README to reflect working single-stepping implementation 2025-10-28 21:20:56 +00:00
sample_program.c add sample test program and autotest script 2025-10-28 21:20:56 +00:00
test_access.c remove debug output and add volatile test program 2025-10-28 21:20:56 +00:00
test_with_args.c add support for passing arguments to target program 2025-10-28 21:20:56 +00:00
vcpkg.json add libdwarf dependency 2025-10-28 21:20:56 +00:00

gwatch - Global Variable Watcher

A tool to monitor writes to global integer variables in Linux binaries using ptrace single-stepping.

Features

  • Monitors specific global integer variables in a running program
  • Detects and reports all writes to the watched variable
  • Supports PIE (Position Independent Executable) binaries
  • Passes command-line arguments to the target program
  • Uses DWARF debug information to locate variables
  • Tab-delimited output format for easy parsing

Building

cmake -B build
cmake --build build

Usage

gwatch --var <symbol> --exec <path> [-- arg1 arg2 ... argN]

Arguments:

  • --var <symbol>: Name of the global variable to watch (required)
  • --exec <path>: Path to the executable to monitor (required)
  • -- arg1 arg2 ...: Optional arguments to pass to the target program

Note: The target binary must be compiled with debug symbols (-g flag).

Examples:

# Watch global_counter in test_access
./build/gwatch --var global_counter --exec ./test_access

# Watch with program arguments
./build/gwatch --var global_counter --exec ./test_with_args -- hello world 123

Output Format

When watchpoints trigger, gwatch outputs access events in the following format:

For writes:

<symbol>\twrite\t<old_value>-><new_value>

For reads:

<symbol>\tread\t<value>

Example output:

global_counter	read	0
global_counter	write	0->42
global_counter	read	42
global_counter	write	42->52

Implementation Details

Variable Detection

  • Uses libdwarf to parse DWARF debug information
  • Identifies global integer variables (int, long, short, char, unsigned variants)
  • Handles const/volatile type qualifiers
  • Retrieves variable address and size from debug info

Single-Stepping Approach

The tool uses PTRACE_SINGLESTEP to monitor memory access:

  • Executes the target program one instruction at a time
  • After each instruction, reads the watched variable's value from memory
  • Detects writes by comparing current value to previous value
  • Reports changes in the specified tab-delimited format

PIE Binary Support

For Position Independent Executables, the tool:

  1. Reads /proc/[pid]/maps to find the base load address (lowest mapping)
  2. Adds the load address to the DWARF-provided offset
  3. Monitors the calculated runtime address

Write Detection

When monitoring the variable:

  1. The current value is read from the traced process memory using ptrace(PTRACE_PEEKDATA)
  2. If the value has changed since the last check, it's reported as a write
  3. Values are formatted according to the variable size (1, 2, 4, or 8 bytes)
  4. Sign extension is applied for smaller integer types

Performance Considerations

The tool uses single-stepping which executes one CPU instruction at a time. This is:

  • Very thorough: Catches every write to the watched variable
  • Slow: Adds significant overhead compared to native execution
  • Reliable: Works consistently across different systems and configurations

For programs with many instructions (100K+ steps), there will be noticeable slowdown.

Testing

Test programs are provided:

  • test_access.c: Program that reads and writes to global_counter
  • test_with_args.c: Program that accepts command-line arguments and modifies global_counter

Compile test programs with:

gcc -g -O0 -o test_access test_access.c
gcc -g -O0 -o test_with_args test_with_args.c

Run tests:

# Basic test
./build/gwatch --var global_counter --exec ./test_access

# Test with arguments
./build/gwatch --var global_counter --exec ./test_with_args -- hello world 123

Future Work

  • Optimize performance (possibly using hardware watchpoints if system supports it)
  • Add support for watching non-integer types (floats, structs, etc.)
  • Support multiple simultaneous variables
  • Add read detection (currently only writes are reported)
  • Output instruction pointer (RIP) for each access
  • Better error reporting and diagnostics
  • Optional: Skip library code and focus on main program only