debugger icon indicating copy to clipboard operation
debugger copied to clipboard

Implement module list parsing for GDB MI adapter

Open Copilot opened this issue 1 month ago • 8 comments
trafficstars

Summary

Implemented GetModuleList() for the GDB MI adapter to parse info proc mappings output and return loaded modules. Also added process exit detection and event handling. Fixed synchronization issue with console output buffering. Fixed command serialization to prevent concurrent GDB commands.

Changes

  • [x] Understand the repository structure and GDB MI adapter
  • [x] Implement parsing of info proc mappings output in GdbMiAdapter::GetModuleList()
  • [x] Handle duplicate module names by appending -1, -2, etc. (logic prepared)
  • [x] Fix parsing to properly skip anonymous memory mappings
  • [x] Test the implementation with standalone test (all tests pass)
  • [x] Run security checks with codeql_checker (no vulnerabilities found)
  • [x] Clean up code
  • [x] Add process exit detection and event handling
  • [x] Fix synchronization issue with console output collection
  • [x] Fix command serialization to prevent interleaved GDB commands

Implementation Details

Module List Parsing

The implementation:

  • Uses -interpreter-exec console "info proc mappings" to get memory mappings
  • Acquires m_gdbCommandMutex to serialize GDB commands
  • Buffers console output asynchronously to avoid race conditions
  • Increases timeout to 5 seconds to handle large output
  • Parses tabular output to extract address ranges and object file paths
  • Consolidates multiple segments for the same object file
  • Filters out special mappings ([stack], [heap], [vvar], [vdso], etc.)
  • Filters out anonymous memory mappings
  • Returns proper DebugModule structures with name, address, and size

Command Serialization

The m_gdbCommandMutex is now acquired in GetModuleList():

  • Prevents other threads from sending GDB commands while waiting for response
  • Eliminates command interleaving (e.g., ReadMemory sent before GetModuleList completes)
  • Ensures responses arrive in the expected order
  • Consistent with how other adapter methods serialize MI traffic

Console Output Buffering

Added a mechanism to temporarily buffer console output:

  • m_consoleBuffer: Stores console output when collection is enabled
  • m_collectConsoleOutput: Flag to enable/disable buffering
  • m_consoleBufferMutex: Protects access to the buffer
  • Console output (~ records) is buffered in AsyncRecordHandler when enabled
  • This prevents console output from being lost or arriving after timeout

Process Exit Handling

When the target process exits after resuming:

  • Detects ProcessExited stop reason in async stopped events
  • Parses the exit-code field if available from GDB MI output
  • Posts a TargetExitedEventType event to notify the debugger UI
  • Stores and returns the exit code via ExitCode() method
  • Skips state refresh when process has exited

Root Causes of Synchronization Issues

Issue 1: Console Output Race

The original implementation had a race condition:

  1. info proc mappings generates console output as async ~ records
  2. These records were sent to the event system, not collected
  3. The command timeout (1 second) was too short for large output
  4. Console output arrived after timeout, causing subsequent commands to fail

Issue 2: Command Interleaving

The console buffering fix didn't fully resolve the issue because:

  1. GetModuleList() didn't acquire m_gdbCommandMutex
  2. Other threads (e.g., UI thread calling ReadMemory) could send commands concurrently
  3. Commands were interleaved while waiting for info proc mappings response
  4. GDB responses arrived out of order, causing timeouts and failures

The final fix ensures both proper console buffering AND command serialization.

Testing

Created standalone tests that validate:

  • Basic parsing of sample GDB output
  • Correct handling of empty output
  • Proper filtering of special mappings
  • Single and multiple module scenarios

All tests pass successfully.

Original prompt

This section details on the original issue you should resolve

<issue_title>GDB MI adapter should read the list of loaded modules</issue_title> <issue_description>Parse the result of info proc mappings and return its result. A sample output is provided:

(gdb) info proc mappings
process 25443
Mapped address spaces:

          Start Addr           End Addr       Size     Offset  Perms  objfile
      0x555555554000     0x555555555000     0x1000        0x0  r--p   /home/xusheng/debugger_build/debugger/test/binaries/Linux-x86_64/helloworld
      0x555555555000     0x555555556000     0x1000     0x1000  r-xp   /home/xusheng/debugger_build/debugger/test/binaries/Linux-x86_64/helloworld
      0x555555556000     0x555555557000     0x1000     0x2000  r--p   /home/xusheng/debugger_build/debugger/test/binaries/Linux-x86_64/helloworld
      0x555555557000     0x555555558000     0x1000     0x2000  r--p   /home/xusheng/debugger_build/debugger/test/binaries/Linux-x86_64/helloworld
      0x555555558000     0x555555559000     0x1000     0x3000  rw-p   /home/xusheng/debugger_build/debugger/test/binaries/Linux-x86_64/helloworld
      0x7ffff7c00000     0x7ffff7c28000    0x28000        0x0  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7c28000     0x7ffff7db0000   0x188000    0x28000  r-xp   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7db0000     0x7ffff7dff000    0x4f000   0x1b0000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7dff000     0x7ffff7e03000     0x4000   0x1fe000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7e03000     0x7ffff7e05000     0x2000   0x202000  rw-p   /usr/lib/x86_64-linux-gnu/libc.so.6
      0x7ffff7e05000     0x7ffff7e12000     0xd000        0x0  rw-p   
      0x7ffff7fa0000     0x7ffff7fa3000     0x3000        0x0  rw-p   
      0x7ffff7fbd000     0x7ffff7fbf000     0x2000        0x0  rw-p   
      0x7ffff7fbf000     0x7ffff7fc1000     0x2000        0x0  r--p   [vvar]
      0x7ffff7fc1000     0x7ffff7fc3000     0x2000        0x0  r--p   [vvar_vclock]
      0x7ffff7fc3000     0x7ffff7fc5000     0x2000        0x0  r-xp   [vdso]
      0x7ffff7fc5000     0x7ffff7fc6000     0x1000        0x0  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7fc6000     0x7ffff7ff1000    0x2b000     0x1000  r-xp   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7ff1000     0x7ffff7ffb000     0xa000    0x2c000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7ffb000     0x7ffff7ffd000     0x2000    0x36000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x38000  rw-p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0  rw-p   [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0  --xp   [vsyscall]

When there are multiple object file with the same name, the one that appears first should take the original name, and later ones should have -1, -2 appended to the name so as to differentiate them</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes Vector35/debugger#900


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot avatar Oct 22 '25 05:10 Copilot