Controlling thread execution in zxdb

Threads

To list the current process’ threads (see “Interaction model” above for more):

[zxdb] thread
  # State   Koid Name
▶ 1 Blocked 1323 initial-thread
  2 Running 3462 worker-thread

Often when you attach to a process the thread will be “blocked”, meaning it is stopped on a system call. For asynchronous programs this will typically be some kind of wait.

Most thread control and introspection commands only work when a thread is suspended (not blocked or running). A thread will be suspended when it is stopped at a breakpoint or crashes. You can explicitly suspend a thread with the pause command:

[zxdb] thread 2 pause
🛑 syscalls-x86-64.S:67
   65 m_syscall zx_port_create 60 2 1
   66 m_syscall zx_port_queue 61 2 1
 ▶ 67 m_syscall zx_port_wait 62 3 0
   68 m_syscall zx_port_cancel 63 3 1
   69 m_syscall zx_timer_create 64 3 1

When a thread is paused the debugger will show the current source code location. Often threads will be in a system call that will resolve to the location in the assembly-language macro file that generated the system call as shown in the above example.

Running pause by itself with no context will pause all threads of all processes currently attached:

[zxdb] pause

Unpause a thread with continue. As before, continue with no context will resume all threads:

[zxdb] continue

Or continue a specific thread:

[zxdb] thread 1 continue

Stack frames

A stack frame is a function call. When a function calls another function, a new nested frame is created. So listing the frames of a thread tells you the call stack. You can only see the stack frames when a thread is suspended (see “Working with threads” above).

To list the current thread’s stack frames (the f abbreviation will also work).

[zxdb] frame
▶ 0 fxl::CommandLineFromIterators<const char *const *>() • command_line.h:203
  1 fxl::CommandLineFromArgcArgv() • command_line.h:224
  2 main() • main.cc:174

And to select a given frame as the default:

[zxdb] frame 2

Frames are numbered with “0” being the top of the stack. Increasing numbers go backwards in time.

You can use the up and down commands to navigate the frame list:

[zxdb] up
  1 fxl::CommandLineFromIterators<const char *const *>() • command_line.h:204

[zxdb] down
  0 fxl::CommandLineFromIteratorsFindFirstPositionalArg<const char *const *>() • command_line.h:185

For more context, you can use the backtrace command. This is identical to frame but gives more detailed address information as well as function parameters. This command can be abbreviated bt:

[zxdb] bt
▶ 0 fxl::CommandLineFromIteratorsFindFirstPositionalArg<const char *const *>() • command_line.h:185
      IP = 0x10f982cf2ad0, BP = 0x66b45a01af50, SP = 0x66b45a01af38
      first = (const char* const*) 0x59f4e1268dc0
      last = (const char* const*) 0x59f4e1268dc8
      first_positional_arg = (const char* const**) 0x0
  1 fxl::CommandLineFromIterators<const char *const *>() • command_line.h:204
      IP = 0x10f982cf2ac0, BP = 0x66b45a01af50, SP = 0x66b45a01af40
      first = <'first' is not available at this address. >
      last = <'last' is not available at this address. >
...

Each stack frame has a code location. Use the list command to look at source code. By itself, it lists the source code around the current stack frame’s instruction pointer:

[zxdb] list
   183 inline CommandLine CommandLineFromIteratorsFindFirstPositionalArg(
   184     InputIterator first, InputIterator last,
 ▶ 185     InputIterator* first_positional_arg) {
   186   if (first_positional_arg)
   187     *first_positional_arg = last;

You can list code around the current instruction pointer of other stack frames, too:

[zxdb] frame 3 list

Or you can list specific things like functions:

[zxdb] list MyClass::MyFunc

File/line numbers:

[zxdb] list foo.cc:43

Or whole files:

[zxdb] list --all myfile.cc:1

Stepping a thread

When a thread is suspended (see “Threads” above) you can control its execution:

next / n: Advances to the next line, stepping over function calls.

[zxdb] n

step / s: Advances to the next line. If a function call happens before the next line, that function will be stepped into and execution will stop at the beginning of it. You can also supply an argument, which is a substring to match of a specific function call. Function names not containing this substring will be skipped and only matching ones will be stepped into:

[zxdb] s
[zxdb] s MyFunction

ss: List function calls on the current line and step in to the call selected, automatically completing any of the other calls that happen to occur first.

[zxdb] ss
  1 std::string::string
  2 MyClass::MyClass
  3 HelperFunctionCall
  4 MyClass::~MyClass
  5 std::string::~string
  quit
>

finish / fi: Exits the function and stops right after the call.

[zxdb] finish

until / u: Given a location (the same as breakpoints, see above), continues the thread until execution gets there. For example, to run until line 45 of the current file:

[zxdb] u 45

jump: Move the instruction pointer to a new address.

[zxdb] jump 22  // Line number
[zxdb] jump 0x87534123  // Address

To run until execution gets back to a given stack frame:

[zxdb] frame 2 until