llvm-project/lldb/www/remote.html

270 lines
14 KiB
HTML
Raw Normal View History

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link href="style.css" rel="stylesheet" type="text/css" />
<title>Remote debugging with LLDB</title>
</head>
<body>
<div class="www_title">
The <strong>LLDB</strong> Debugger
</div>
<div id="container">
<div id="content">
<!--#include virtual="sidebar.incl"-->
<div id="middle">
<h1 class="postheader">Remote debugging</h1>
<div class="postcontent">
<p>
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 <em>local</em> system, while the system running the debugged
process will be the <em>remote</em> system.
</p>
<p>
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
<a href="https://sourceware.org/gdb/current/onlinedocs/gdb/Remote-Protocol.html">here</a>
and the LLDB-specific extensions are documented in
<code>docs/lldb-gdb-remote.txt</code> file inside LLDB source repository. Besides the
gdb-remote stub, the server part of LLDB also consists of a <em>platform</em> 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.
</p>
<p>
In order to reduce code complexity and improve remote debugging experience LLDB on
Linux and OSX 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.
</p>
</div>
<div class="postfooter"></div>
<div class="post">
<h1 class="postheader">Preparation for remote debugging</h1>
<div class="postcontent">
<p>
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.
</p>
<h2>Remote system</h2>
<p>
On Linux and Android, all required remote functionality is contained in the
<code>lldb-server</code> 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
<code>lldb-server</code> binary is also statically linked with the rest of LLDB
(unlike <code>lldb</code>, which dynamically links to <code>liblldb.so</code> by
default), so it does not have any dependencies on the rest of lldb. On Mac OSX and
iOS, the remote-gdb functionality is implemented by the <code>debugserver</code>
binary, which you will need to deploy alongside <code>lldb-server</code>.
</p>
<p>
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
<a href="build.html#cross-compilation">build</a> page.
</p>
<p>
Once the binaries are in place, you just need to run the <code>lldb-server</code>
in platform mode and specify the port it should listen on. For example, the command
</p>
<code>
remote% <b>lldb-server platform --listen "*:1234" --server</b>
</code>
<p>
will start the LLDB platform and wait for incoming connections from any address to
port 1234. Specifying an address instead of <code>*</code> will only allow
connections originating from that address. Adding a <code>--server</code> parameter
to the command line will fork off a new process for every incoming connection,
allowing multiple parallel debug sessions.
</p>
<h2>Local system</h2>
<p>
On the local system, you need to let LLDB know that you intend to do remote
debugging. This is achieved through the <code>platform</code> 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
<code>platform list</code>.
</p>
<code>
local% <b>lldb</b>
<br>(lldb) <b>platform list</b>
<br>Available platforms:
<br>host: Local Mac OS X user platform plug-in.
<br>remote-freebsd: Remote FreeBSD user platform plug-in.
<br>remote-linux: Remote Linux user platform plug-in.
<br>remote-netbsd: Remote NetBSD user platform plug-in.
<br>remote-windows: Remote Windows user platform plug-in.
<br>remote-android: Remote Android user platform plug-in.
<br>remote-ios: Remote iOS platform plug-in.
<br>remote-macosx: Remote Mac OS X user platform plug-in.
<br>ios-simulator: iOS simulator platform plug-in.
<br>darwin-kernel: Darwin Kernel platform plug-in.
<br>tvos-simulator: Apple TV simulator platform plug-in.
<br>watchos-simulator: Apple Watch simulator platform plug-in.
<br>remote-tvos: Remote Apple TV platform plug-in.
<br>remote-watchos: Remote Apple Watch platform plug-in.
<br>remote-gdb-server: A platform that uses the GDB remote protocol as the communication transport.
</code>
<p>
The default platform is the platform <code>host</code> 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
"<code>remote-</code>". For example, to debug a remote Linux application:
<br>
<code>
<br>(lldb) <b>platform select remote-linux</b>
</code>
<p>
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 <code>platform connect</code> command. This
command takes a number of arguments (as always, use the <code>help</code> command
to find out more), but normally you only need to specify the address to connect to,
e.g.:
<br>
<code>
<br>(lldb) <b>platform connect connect://remote:1234</b>
<br>&nbsp;&nbsp;Platform: remote-linux
<br>&nbsp;&nbsp;&nbsp;&nbsp;Triple: x86_64-gnu-linux
<br>&nbsp;&nbsp;Hostname: remote
<br>&nbsp;Connected: yes
<br>WorkingDir: /tmp
</code>
<p>
Note that the platform has a working directory of <code>/tmp</code>. This directory
will be used as the directory that executables will be uploaded to by default when
launching a process from <em>local</em>.
<p>
After this, you should be able to debug normally. You can use the
<code>process attach</code> to attach to an existing remote process or
<code>target create</code>, <code>process launch</code> 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: <code>get-file</code>,
<code>put-file</code>, <code>mkdir</code>, etc. The environment can be prepared
further using the <code>platform shell</code> command.
</p>
<h3>Launching a locally built process on the remote machine</h3>
<h4>Install and run in the platform working directory</h4>
<p>
To launch a locally built process on the remote system in the
platform working directory:
<br>
<code>
<br>(lldb) <b>file a.out</b>
<br>(lldb) <b>run</b>
</code>
<p>
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.
</p>
<h4>Changing the platform working directory</h4>
<p>
You can change the platform working directory while connected to
the platform with:
<br>
<code>
<br>(lldb) <b>platform settings -w /usr/local/bin</b>
</code>
<p>
And you can verify it worked using "platform status":
<br>
<code>
<br>(lldb) <b>platform status</b>
<br>&nbsp;&nbsp;Platform: remote-linux
<br>&nbsp;&nbsp;&nbsp;&nbsp;Triple: x86_64-gnu-linux
<br>&nbsp;&nbsp;Hostname: remote
<br>&nbsp;Connected: yes
<br>WorkingDir: /usr/local/bin
</code>
<p>
If we run again, the program will be installed into /usr/local/bin.
</p>
<h4>Install and run by specifying a remote install path</h4>
<p>
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:
<br>
<code>
<br>(lldb) <b>file a.out</b>
<br>(lldb) <b>script lldb.target.module['a.out'].SetPlatformFileSpec("/bin/a.out")</b>
<br>(lldb) <b>run</b>
</code>
<p>
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:
</p>
<code>
<br>(lldb) <b>file a.out</b>
<br>(lldb) <b>target module add /local/build/libfoo.so</b>
<br>(lldb) <b>target module add /local/build/libbar.so</b>
<br>(lldb) <b>script lldb.target.module['libfoo.so'].SetPlatformFileSpec("/usr/lib/libfoo.so")</b>
<br>(lldb) <b>script lldb.target.module['libbar.so'].SetPlatformFileSpec("/usr/local/lib/libbar.so")</b>
<br>(lldb) <b>run</b>
</code>
<h4>Attaching to a remote process</h4>
<p>
If you want to attach to a remote process, you can first list the
processes on the remote system:
</p>
<code>
<br>(lldb) <b>platform process list</b>
<br>223 matching processes were found on "remote-linux"
<br>PID&nbsp;&nbsp;&nbsp;&nbsp;PARENT&nbsp;USER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TRIPLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NAME
<br>======&nbsp;======&nbsp;==========&nbsp;========================&nbsp;============================
<br>68639&nbsp;&nbsp;90652&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x86_64-apple-macosx&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lldb
<br>...
</code>
<p>
Then attaching is as simple as specifying the remote process ID:
</p>
<code>
<br>(lldb) <b>attach 68639</b>
</code>
</div>
<div class="postfooter"></div>
</div>
</div>
</div>
</div>
</body>
</html>