blob: 55be81edfbc56135ed78f7e2cf7d06ae5e51b6bb [file] [log] [blame] [view]
# zxdb: Fuchsia native debugger setup and troubleshooting
## Overview
The debugger is for C, C++, and Rust code running on Fuchsia for either 64-bit
ARM or 64-bit x86 architectures.
This is the very detailed setup guide. Please see:
* The [user guide](debugger_usage.md) for help on debugger commands.
The debugger runs remotely only (you can't do self-hosted debug).
## Binary location (for SDK users)
The binary is `tools/zxdb` in the Fuchsia SDK. SDK users will have to do an
extra step to set up your symbols. See "Running out-of-tree" below for more.
## Compiling (for Fuchsia team members)
A Fuchsia "core" build includes (as of this writing) the necessary targets
for the debugger. So this build configuration is sufficient:
```sh
fx --dir=out/x64 set core.x64
```
If you're compiling with another product, you may not get it by default. If you
don't have the debugger in your build, add `//bundles:tools` to your
"universe", either with:
```
fx <normal_stuff_you_use> --with //bundles:tools
```
Or you can edit your GN args directly by editing `<build_dir>/args.gn` and
adding to the bottom:
```
universe_package_labels += [ "//bundles:tools" ]
```
## Running
### Preparation: Boot with networking
Boot the target system with networking support:
* Hardware devices: use the device instructions.
* AEMU: `fx emu -N`
* QEMU: `fx qemu -N`
(If using x64 with an emulator on a Linux host, we also recommend the "-k" flag
which will make it run faster).
To manually validate network connectivity run `fx shell` or `fx get-device-addr`.
### Simple method
You can use the fx utility to start the debug agent and connect automatically.
For most build configurations, the debug agent will be in the "universe" (i.e.
"available to use") but not in the base build so won't be on the system before
boot. You will need to run:
```sh
fx serve
```
to make the debug agent's package available for serving to the system. Otherwise
you will get the message "Timed out trying to find the Debug Agent".
Once the server is running, launch the debugger in another terminal window:
```sh
fx debug
```
To manually validate packages can be loaded, run "ls" from within the Fuchsia
shell (for most setups this requires "fx serve" to be successfully serving
packages).
### Manual method
In some cases you may want to run the debug agent and connect manually. To do
so, follow these steps:
#### 1. Run the debug agent on the target
On the target system pick a port and run the debug agent:
```sh
run fuchsia-pkg://fuchsia.com/debug_agent#meta/debug_agent.cmx --port=2345
```
If you get an error "Cannot create child process: ... failed to resolve ..."
it means the debug agent can't be loaded. You may need to run `fx serve` or its
equivalent in your environment to make it available.
You will want to note the target's IP address. Run `ifconfig` _on the target_
to see this, or run `fx get-device-addr` on the host.
#### 2. Run the client and connect
On the host system (where you do the build), run the client. Use the IP
address of the target and the port you picked above in the `connect` command.
If running in-tree, `fx get-device-addr` will tell you this address.
For QEMU, we recommend using IPv6 and link local addresses. These addresses
have to be annotated with the interface they apply to, so make sure the address
you use includes the appropriate interface (should be the name of the bridge
device).
The address should look like `fe80::5054:4d:fe63:5e7a%br0`
```sh
fx zxdb
or
out/<out_dir>/host_x64/zxdb
[zxdb] connect [fe80::5054:4d:fe63:5e7a%br0]:2345
```
(Substitute your build directory as-needed).
If you're connecting or running many times, there are command-line switches:
```sh
zxdb -c [fe80::5054:4d:fe63:5e7a%br0]:2345
```
* The `status` command will give you a summary of the current state of the
debugger.
* See `help connect` for more examples, including IPv6 syntax.
### Read the user guide
Once you're connected, the [user guide](debugger_usage.md) has detailed
instructions!
## Tips
### Running out-of-tree
You can run with kernels or user programs compiled elsewhere with some extra
steps. We hope this will become easier over time.
Be aware that we aren't yet treating the protocol as frozen. Ideally the
debugger will be from the same build as the operating system itself (more
precisely, it needs to match the debug\_agent). But the protocol does not
change very often so there is some flexibility.
When you run out-of-tree, you will need to tell zxdb where your symbols and
source code are on the local development box (Linux or Mac). Zxdb can not use
symbols in the binary that you pushed to the Fuchsia target device.
See [Diagnosing symbol problems](#diagnosing-symbol-problems).
#### Set the symbol location
To specify new symbol locations for zxdb, use the `-s` command-line flag:
```sh
zxdb -s path/to/my_binary -s some/other_location
```
Or add it to the `symbol_paths` list option in the interactive UI:
```
[zxdb] set symbol-paths += /my/new/symbol/path
```
It's best if your build makes a ".build-id" directory. You then pass the parent
directory as a symbol dir. For example, the Fuchsia build itself makes a
".build-id" directory inside the build directory. For example, if your build
directory is `out/x64`:
```sh
out/x64/host_x64/zxdb -s out/x64
```
Some builds produce a file called "ids.txt" that lists build IDs and local
paths to the corresponding binaries. This is the second-best option.
If you don't have that, you can just list the name of the file you're debugging
directly. You can pass multiple "-s" flags to list multiple symbol locations.
The `-s` flag accepts three possible things:
* Directory names. If the given directory contains a ".build-id"
subdirectory that will be used. Otherwise all ELF files in the given
directory will be indexed.
* File names ending in ".txt". Zxdb will treat this as a "ids.txt" file
mapping build IDs to binaries.
* Any other file name will be treated as an ELF file with symbols.
#### Set the source code location {#set-source-code-location}
The Fuchsia build generates symbols relative to the build directory so relative
paths look like `../../src/my_component/file.cc`).
If your files are not being found with the default build directories, you will
need to provide a build directory to locate the files. This build directory does
not need have been used to build, it just needs to produce correct absolute paths
when concatenated with the relative paths from the symbol file.
You can add additional build directories on the command line:
```sh
zxdb -b /home/me/fuchsia/out/x64
```
Or interactively from within the debugger:
```
[zxdb] set build-dirs += /home/me/fuchsia/out/x64
```
If debugger is finding the wrong file, you can replace the entire build
directory list by omitting the `+=`:
```
[zxdb] set build-dirs /home/me/fuchsia/out/x64
```
If your build produces DWARF symbols with absolute file paths the files must be
in that location on the local system. Absolute file paths in the symbols are not
affected by the build search path. Clang users should use the
`-fdebug-prefix-map` which will also help with build hermeticity.
### Diagnosing symbol problems
#### Variable values are unavailable
Usually this is related to the optimization level of the program:
_Optimized out_ Indicates that the program symbols declare a variable with the given name, but
that it has no value or location. This means the compiler has entirely optimized out the variable
and the debugger can not show it. If you need to see it, use a less-optimized build setting.
_Unavailable_ indicates that the variable is not valid at the current address, but that its value
is known at other addresses. In optimized code, the compiler will often re-use registers, clobbering
previous values which become unavailable.
You can see the valid ranges for a variable with the "sym-info" command:
```
[zxdb] sym-info my_variable
Variable: my_variable
Type: int
DWARF tag: 0x05
DWARF location (address range + DWARF expression bytes):
[0x3e0d0a3e05b, 0x3e0d0a3e0b2): 0x70 0x88 0x78
[0x3e0d0a3e0b2, 0x3e0d0a3eb11): 0x76 0x48 0x10 0xf8 0x07 0x1c 0x06
```
Under "DWARF location" it will give a list of address ranges where the value of the variable is
known (inclusive at the beginning of the range, non-inclusive at the end). Run to one of these
addresses to see the value of the variable (use "di" to see the current address).
You can ignore the "DWARF expression bytes" which are the internal instructions for finding the
variable.
#### Can't find symbols
The `sym-stat` command will tell you status for symbols. With no running
process, it will give information on the different symbol locations you have
specified. If your symbols aren't found, make sure this matches your
expectations:
```
[zxdb] sym-stat
Symbol index status
Indexed Source path
(folder) /home/me/.build-id
(folder) /home/me/build/out/x64
0 my_dir/my_file
```
If you see "0" in the "Indexed" column of the "Symbol index status" that means
that the debugger could not find where your symbols are. Try the `-s` flag (see
"Running out-of-tree" above) to specify where your symbols are.
Symbol sources using the ".build-id" hierarchy will list "(folder)" for the
indexed symbols since this type of source does not need to be indexed. To check
if your hierarchy includes a given build ID, go to ".build-id" inside it, then
to the folder with the first to characters of the build ID to see if there is a
matching file.
When you have a running program, `sym-stat` will additionally print symbol
information for each binary loaded into the process. If you're not getting
symbols, find the entry for the binary or shared library in this list. If it
says:
```
Symbols loaded: No
```
then that means it couldn't find the symbolized binary on the local computer
for the given build ID in any of the locations listed in "Symbol index status".
You may need to add a new location with `-s`.
If instead it says something like this:
```
Symbols loaded: Yes
Symbol file: /home/foo/bar/...
Source files indexed: 1
Symbols indexed: 0
```
where "Source files indexed" and "Symbols indexed" is 0 or a very low integer,
that means that the debugger found a symbolized file but there are few or no
symbols in it. Normally this means the binary was not built with symbols
enabled or the symbols were stripped. Check your build, you should be passing
the path to the unstripped binary and the original compile line should have a
`-g` in it to get symbols.
#### Mismatched source lines
Sometimes the source file listings may not match the code. The most common
reason is that the build is out-of-date and no longer matches the source. The
debugger will check that the symbol file modification time is newer than the
source file, but it will only print the warning the first time the file is
displayed. Check for this warning if you suspect a problem.
Some people have multiple checkouts. If it's finding a file in the wrong one,
override the `build-dirs` option as described above in [Set the source code
location](#set-source-code-location).
To display the file name of the file it found from `list`, use the `-f` option:
```
[zxdb] list -f
/home/me/fuchsia/out/x64/../../src/foo/bar.cc
... <source code> ...
```
You can also set the `show-file-paths` option. This will increase file path
information:
* It will show the full resolved path in source listings as in `list -f`.
* It will show the full path instead of just the file name in other
places such as backtraces.
```
[zxdb] set show-file-paths true
```
You may notice a mismatch when setting a breakpoint on a specific line where
the displayed breakpoint location doesn't match the line number you typed. In
most cases, this is because this symbols did not identify any code on the
specified line so the debugger used the next line. It can happen even in
unoptimized builds, and is most common for variable declarations.
```
[zxdb] b file.cc:138
Breakpoint 1 (Software) @ file.cc:138
138 int my_value = 0; <- Breakpoint was requested here.
◉ 139 DoSomething(&my_value); <- But ended up here.
140 if (my_value > 0) {
```
## Debugging the debugger and running the tests
### Client
For developers working on the debugger, you can activate the `--debug-mode` flag
that will activate many logging statements for the debugger:
```
zxdb --debug-mode
```
You can also debug the client on GDB or LLDB on your host machine.
* Use the unstripped binary in `host_x64/exe.unstripped` to get symbols.
* The Fuchsia build generates symbols relative to your build directory
(`out/x64` or similar), so you must run GDB/LLDB with that as the
current directory.
* Launching zxdb from the debugger with the right flags to connect can be
tricky. To debug initialization, copy the command-line from "ps". Otherwise,
it's easiest to attach after starting the debugger in the normal fashion.
```sh
cd out/x64 # Substitute your build directory as needed.
sudo gdb host_x64/exe.unstripped/zxdb
... GDB startup messages ...
(gdb) attach 12345 # Use the PID of the zxdb already running.
... the program will be stopped when GDB attaches ...
(gdb) continue
```
There are tests for the debugger that run on the host. These are relevant
if you're working on the debugger client.
```sh
cd out/x64 # Substitute your build directory as needed.
host_x64/zxdb_tests
```
To run the unit tests in the debugger:
```sh
cd out/x64
cp host_x64/exe.unstripped/zxdb_tests host_x64/
gdb host_x64/zxdb_tests
```
Most tests can be debugged by omitting the copy step and debugging the
symbolized file in `exe_unstripped` directly, but some tests require data files
at a certain place relative to the test binary and these will fail.
### Debug Agent
Similar as with the client, the debug agent is programmed to log many debug
statements when run with the `--debug-mode` flag:
```
run fuchsia-pkg://fuchsia.com/debug_agent#meta/debug_agent.cmx --debug-mode
```
It is also possible to attach the debugger to the debugger. The preferred way to
do this is to make zxdb catch the debugger on launch using the filtering
feature. This is done frequently by the debugger team. See the
[user guide](debugger_usage.md) for more details:
```
// Run the debugger that will attach to the "to-be-debugged" debug agent.
fx debug
// * Within zxdb.
[zxdb] attach debug_agent
// Launch another debug agent manually
// * Within the target (requires another port).
run fuchsia-pkg://fuchsia.com/debug_agent#meta/debug_agent.cmx --port=5000 --debug-mode
// * Within the first zxdb:
Attached Process 1 [Running] koid=12345 debug_agent.cmx
The process is currently in an initializing state. You can set pending
breakpoints (symbols haven't been loaded yet) and "continue".
[zxdb] continue
// Now there is a running debug agent that is attached by the first zxdb run.
// You can also attach to it using another client (notice the port):
fx zxdb --connect [<IPv6 to target>]:5000 --debug-mode
// Now you have two running instances of the debugger!
```
Note: Only one debugger can be attached to the main job in order to auto-attach
to new processes. Since you're using it for the first debugger, you won't be
able to launch components with the second one, only attach to them.
To run the debug agent tests:
```
fx test debug_agent_tests
```
## Bugs
To report a new zxdb bug, see [Report a new zxdb bug](https://bugs.fuchsia.dev/p/fuchsia/issues/entry?components=DeveloperExperience%3Ezxdb).
## Other Languages
C, C++, and Rust are supported. Go is not supported but may work to some degree
if you compile with DWARF symbols (please file bugs if you try). Dart and
JavaScript will not work because they're interpreted languages that do not
generate compiled code with DWARF symbols.