forked from OSchip/llvm-project
233 lines
9.3 KiB
ReStructuredText
233 lines
9.3 KiB
ReStructuredText
Remote Debugging
|
|
================
|
|
|
|
Remote debugging refers to the act of debugging a process which is running on a
|
|
different system, than the debugger itself. We shall refer to the system
|
|
running the debugger as the local system, while the system running the debugged
|
|
process will be the remote system.
|
|
|
|
To enable remote debugging, LLDB employs a client-server architecture. The
|
|
client part runs on the local system and the remote system runs the server. The
|
|
client and server communicate using the gdb-remote protocol, usually
|
|
transported over TCP/IP. More information on the protocol can be found here and
|
|
the LLDB-specific extensions are documented in docs/lldb-gdb-remote.txt file
|
|
inside LLDB source repository. Besides the gdb-remote stub, the server part of
|
|
LLDB also consists of a platform binary, which is responsible for performing
|
|
advanced debugging operations, like copying files from/to the remote system and
|
|
can be used to execute arbitrary shell commands on the remote system.
|
|
|
|
In order to reduce code complexity and improve remote debugging experience LLDB
|
|
on Linux and macOS uses the remote debugging stub even when debugging a process
|
|
locally. This is achieved by spawning a remote stub process locally and
|
|
communicating with it over the loopback interface. In the case of local
|
|
debugging this whole process is transparent to the user. The platform binary is
|
|
not used in this case, since no file transfers are needed.
|
|
|
|
.. contents::
|
|
:local:
|
|
|
|
Preparation for Remote Debugging
|
|
---------------------------------
|
|
|
|
While the process of actual debugging (stepping, backtraces, evaluating
|
|
expressions) is same as in the local case, in the case of remote debugging,
|
|
more preparation is needed as the required binaries cannot started on the
|
|
remote system automatically. Also, if the remote system runs a different OS or
|
|
architecture, the server component needs to be compiled separately.
|
|
|
|
Remote system
|
|
*************
|
|
|
|
On Linux and Android, all required remote functionality is contained in the
|
|
lldb-server binary. This binary combines the functionality of the platform and
|
|
gdb-remote stub. A single binary facilitates deployment and reduces code size,
|
|
since the two functions share a lot of code. The lldb-server binary is also
|
|
statically linked with the rest of LLDB (unlike lldb, which dynamically links
|
|
to liblldb.so by default), so it does not have any dependencies on the rest of
|
|
lldb. On macOS and iOS, the remote-gdb functionality is implemented by the
|
|
debugserver binary, which you will need to deploy alongside lldb-server.
|
|
|
|
The binaries mentioned above need to be present on the remote system to enable
|
|
remote debugging. You can either compile on the remote system directly or copy
|
|
them from the local machine. If compiling locally and the remote architecture
|
|
differs from the local one, you will need to cross-compile the correct version
|
|
of the binaries. More information on cross-compiling LLDB can be found on the
|
|
build page.
|
|
|
|
Once the binaries are in place, you just need to run the lldb-server in
|
|
platform mode and specify the port it should listen on. For example, the
|
|
command
|
|
|
|
::
|
|
|
|
remote% lldb-server platform --listen "*:1234" --server
|
|
|
|
will start the LLDB platform and wait for incoming connections from any address
|
|
to port 1234. Specifying an address instead of * will only allow connections
|
|
originating from that address. Adding a --server parameter to the command line
|
|
will fork off a new process for every incoming connection, allowing multiple
|
|
parallel debug sessions.
|
|
|
|
Local system
|
|
************
|
|
|
|
On the local system, you need to let LLDB know that you intend to do remote
|
|
debugging. This is achieved through the platform command and its sub-commands.
|
|
As a first step you need to choose the correct platform plug-in for your remote
|
|
system. A list of available plug-ins can be obtained through platform list.
|
|
|
|
::
|
|
|
|
local% lldb
|
|
(lldb) platform list
|
|
Available platforms:
|
|
host: Local macOS user platform plug-in.
|
|
remote-freebsd: Remote FreeBSD user platform plug-in.
|
|
remote-linux: Remote Linux user platform plug-in.
|
|
remote-netbsd: Remote NetBSD user platform plug-in.
|
|
remote-windows: Remote Windows user platform plug-in.
|
|
remote-android: Remote Android user platform plug-in.
|
|
remote-ios: Remote iOS platform plug-in.
|
|
remote-macosx: Remote macOS user platform plug-in.
|
|
ios-simulator: iOS simulator platform plug-in.
|
|
darwin-kernel: Darwin Kernel platform plug-in.
|
|
tvos-simulator: Apple TV simulator platform plug-in.
|
|
watchos-simulator: Apple Watch simulator platform plug-in.
|
|
remote-tvos: Remote Apple TV platform plug-in.
|
|
remote-watchos: Remote Apple Watch platform plug-in.
|
|
remote-gdb-server: A platform that uses the GDB remote protocol as the communication transport.
|
|
|
|
The default platform is the platform host which is used for local debugging.
|
|
Apart from this, the list should contain a number of plug-ins, for debugging
|
|
different kinds of systems. The remote plug-ins are prefixed with "remote-".
|
|
For example, to debug a remote Linux application:
|
|
|
|
::
|
|
|
|
(lldb) platform select remote-linux
|
|
|
|
After selecting the platform plug-in, you should receive a prompt which
|
|
confirms the selected platform, and states that you are not connected. This is
|
|
because remote plug-ins need to be connected to their remote platform
|
|
counterpart to operate. This is achieved using the platform connect command.
|
|
This command takes a number of arguments (as always, use the help command to
|
|
find out more), but normally you only need to specify the address to connect
|
|
to, e.g.:
|
|
|
|
::
|
|
|
|
(lldb) platform connect connect://remote:1234
|
|
Platform: remote-linux
|
|
Triple: x86_64-gnu-linux
|
|
Hostname: remote
|
|
Connected: yes
|
|
WorkingDir: /tmp
|
|
|
|
Note that the platform has a working directory of /tmp. This directory will be
|
|
used as the directory that executables will be uploaded to by default when
|
|
launching a process from local.
|
|
|
|
After this, you should be able to debug normally. You can use the process
|
|
attach to attach to an existing remote process or target create, process launch
|
|
to start a new one. The platform plugin will transparently take care of
|
|
uploading or downloading the executable in order to be able to debug. If your
|
|
application needs additional files, you can transfer them using the platform
|
|
commands: get-file, put-file, mkdir, etc. The environment can be prepared
|
|
further using the platform shell command.
|
|
|
|
Launching a locally built process on the remote machine
|
|
-------------------------------------------------------
|
|
|
|
Install and run in the platform working directory
|
|
*************************************************
|
|
|
|
To launch a locally built process on the remote system in the platform working
|
|
directory:
|
|
|
|
::
|
|
|
|
(lldb) file a.out
|
|
(lldb) run
|
|
|
|
This will cause LLDB to create a target with the "a.out" executable that you
|
|
cross built. The "run" command will cause LLDB to upload "a.out" to the
|
|
platform's current working directory only if the file has changed. The platform
|
|
connection allows us to transfer files, but also allows us to get the MD5
|
|
checksum of the file on the other end and only upload the file if it has
|
|
changed. LLDB will automatically launch a lldb-server in gdbremote mode to
|
|
allow you to debug this executable, connect to it and start your debug session
|
|
for you.
|
|
|
|
Changing the platform working directory
|
|
***************************************
|
|
|
|
You can change the platform working directory while connected to the platform
|
|
with:
|
|
|
|
::
|
|
|
|
(lldb) platform settings -w /usr/local/bin
|
|
|
|
And you can verify it worked using "platform status":
|
|
|
|
::
|
|
|
|
(lldb) platform status
|
|
Platform: remote-linux
|
|
Triple: x86_64-gnu-linux
|
|
Hostname: remote
|
|
Connected: yes
|
|
WorkingDir: /usr/local/bin
|
|
|
|
If we run again, the program will be installed into ``/usr/local/bin``.
|
|
|
|
Install and run by specifying a remote install path
|
|
***************************************************
|
|
|
|
If you want the "a.out" executable to be installed into "/bin/a.out" instead of
|
|
the platform's current working directory, we can set the platform file
|
|
specification using python:
|
|
|
|
::
|
|
|
|
(lldb) file a.out
|
|
(lldb) script lldb.target.module['a.out'].SetPlatformFileSpec("/bin/a.out")
|
|
(lldb) run
|
|
|
|
Now when you run your program, the program will be uploaded to "/bin/a.out"
|
|
instead of the platform current working directory. Only the main executable is
|
|
uploaded to the remote system by default when launching the application. If you
|
|
have shared libraries that should also be uploaded, then you can add the
|
|
locally build shared library to the current target and set its platform file
|
|
specification:
|
|
|
|
::
|
|
|
|
(lldb) file a.out
|
|
(lldb) target module add /local/build/libfoo.so
|
|
(lldb) target module add /local/build/libbar.so
|
|
(lldb) script lldb.target.module['libfoo.so'].SetPlatformFileSpec("/usr/lib/libfoo.so")
|
|
(lldb) script lldb.target.module['libbar.so'].SetPlatformFileSpec("/usr/local/lib/libbar.so")
|
|
(lldb) run
|
|
|
|
Attaching to a remote process
|
|
*****************************
|
|
|
|
If you want to attach to a remote process, you can first list the processes on
|
|
the remote system:
|
|
|
|
::
|
|
|
|
(lldb) platform process list
|
|
223 matching processes were found on "remote-linux"
|
|
PID PARENT USER TRIPLE NAME
|
|
====== ====== ========== ======================== ============================
|
|
68639 90652 x86_64-apple-macosx lldb
|
|
...
|
|
|
|
Then attaching is as simple as specifying the remote process ID:
|
|
|
|
::
|
|
|
|
(lldb) attach 68639
|