Skip to content
This repository was archived by the owner on May 21, 2019. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 16a8e36

Browse files
author
Tamas Berghammer
committedDec 8, 2015
Modify "platform connect" to connect to processes as well
The standard remote debugging workflow with gdb is to start the application on the remote host under gdbserver (e.g.: gdbserver :5039 a.out) and then connect to it with gdb. The same workflow is supported by debugserver/lldb-gdbserver with a very similar syntax but to access all features of lldb we need to be connected also to an lldb-platform instance running on the target. Before this change this had to be done manually with starting a separate lldb-platform on the target machine and then connecting to it with lldb before connecting to the process. This change modifies the behavior of "platform connect" with automatically connecting to the process instance if it was started by the remote platform. With this command replacing gdbserver in a gdb based worflow is usually as simple as replacing the command to execute gdbserver with executing lldb-platform. Differential revision: http://reviews.llvm.org/D14952 git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@255016 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent a868c1c commit 16a8e36

25 files changed

+576
-151
lines changed
 

‎docs/lldb-gdb-remote.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,3 +1597,29 @@ On MacOSX with debugserver, we expedite the frame pointer backchain for a thread
15971597
(up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for
15981598
the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and
15991599
iOS now don't require us to read any memory!
1600+
1601+
//----------------------------------------------------------------------
1602+
// "qQueryGDBServer"
1603+
//
1604+
// BRIEF
1605+
// Ask the platform for the list of gdbservers we have to connect
1606+
//
1607+
// PRIORITY TO IMPLEMENT
1608+
// Low. The packet is required to support connecting to gdbserver started
1609+
// by the platform instance automatically.
1610+
//----------------------------------------------------------------------
1611+
1612+
If the remote platform automatically started one or more gdbserver instance (without
1613+
lldb asking it) then it have to return the list of port number or socket name for
1614+
each of them what can be used by lldb to connect to those instances.
1615+
1616+
The data in this packet is a JSON array of JSON objects with the following keys:
1617+
"port": <the port number to connect> (optional)
1618+
"socket_name": <the name of the socket to connect> (optional)
1619+
1620+
Example packet:
1621+
[
1622+
{ "port": 1234 },
1623+
{ "port": 5432 },
1624+
{ "socket_name": "foo" }
1625+
]

‎include/lldb/Target/Platform.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,13 @@ class ModuleCache;
487487
Target *target, // Can be nullptr, if nullptr create a new target, else use existing one
488488
Error &error);
489489

490+
virtual lldb::ProcessSP
491+
ConnectProcess (const char* connect_url,
492+
const char* plugin_name,
493+
lldb_private::Debugger &debugger,
494+
lldb_private::Target *target,
495+
lldb_private::Error &error);
496+
490497
//------------------------------------------------------------------
491498
/// Attach to an existing process using a process ID.
492499
///
@@ -1034,6 +1041,25 @@ class ModuleCache;
10341041
virtual Error
10351042
UnloadImage (lldb_private::Process* process, uint32_t image_token);
10361043

1044+
//------------------------------------------------------------------
1045+
/// Connect to all processes waiting for a debugger to attach
1046+
///
1047+
/// If the platform have a list of processes waiting for a debugger
1048+
/// to connect to them then connect to all of these pending processes.
1049+
///
1050+
/// @param[in] debugger
1051+
/// The debugger used for the connect.
1052+
///
1053+
/// @param[out] error
1054+
/// If an error occurred during the connect then this object will
1055+
/// contain the error message.
1056+
///
1057+
/// @return
1058+
/// The number of processes we are succesfully connected to.
1059+
//------------------------------------------------------------------
1060+
virtual size_t
1061+
ConnectToWaitingProcesses(lldb_private::Debugger& debugger, lldb_private::Error& error);
1062+
10371063
protected:
10381064
bool m_is_host;
10391065
// Set to true when we are able to actually set the OS version while

‎packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,21 +1115,23 @@ def wrapper(*args, **kwargs):
11151115
# @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386
11161116

11171117
# TODO: refactor current code, to make skipIfxxx functions to call this function
1118-
def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None):
1118+
def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None, remote=None):
11191119
def fn(self):
11201120
oslist_passes = oslist is None or self.getPlatform() in oslist
11211121
compiler_passes = compiler is None or (compiler in self.getCompiler() and self.expectedCompilerVersion(compiler_version))
11221122
arch_passes = self.expectedArch(archs)
11231123
debug_info_passes = debug_info is None or self.debug_info in debug_info
11241124
swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version))
11251125
py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info)
1126+
remote_passes = (remote is None) or (remote == (lldb.remote_platform is not None))
11261127

11271128
return (oslist_passes and
11281129
compiler_passes and
11291130
arch_passes and
11301131
debug_info_passes and
11311132
swig_version_passes and
1132-
py_version_passes)
1133+
py_version_passes and
1134+
remote_passes)
11331135

11341136
local_vars = locals()
11351137
args = [x for x in inspect.getargspec(skipIf).args]

‎packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -147,29 +147,30 @@ def get_stub_port_from_named_socket(self, read_timeout_seconds=5):
147147

148148
return stub_port
149149

150+
def run_shell_cmd(self, cmd):
151+
platform = self.dbg.GetSelectedPlatform()
152+
shell_cmd = lldb.SBPlatformShellCommand(cmd)
153+
err = platform.Run(shell_cmd)
154+
if err.Fail() or shell_cmd.GetStatus():
155+
m = "remote_platform.RunShellCommand('%s') failed:\n" % cmd
156+
m += ">>> return code: %d\n" % shell_cmd.GetStatus()
157+
if err.Fail():
158+
m += ">>> %s\n" % str(err).strip()
159+
m += ">>> %s\n" % (shell_cmd.GetOutput() or
160+
"Command generated no output.")
161+
raise Exception(m)
162+
return shell_cmd.GetOutput().strip()
163+
150164
def init_llgs_test(self, use_named_pipe=True):
151165
if lldb.remote_platform:
152-
def run_shell_cmd(cmd):
153-
platform = self.dbg.GetSelectedPlatform()
154-
shell_cmd = lldb.SBPlatformShellCommand(cmd)
155-
err = platform.Run(shell_cmd)
156-
if err.Fail() or shell_cmd.GetStatus():
157-
m = "remote_platform.RunShellCommand('%s') failed:\n" % cmd
158-
m += ">>> return code: %d\n" % shell_cmd.GetStatus()
159-
if err.Fail():
160-
m += ">>> %s\n" % str(err).strip()
161-
m += ">>> %s\n" % (shell_cmd.GetOutput() or
162-
"Command generated no output.")
163-
raise Exception(m)
164-
return shell_cmd.GetOutput().strip()
165166
# Remote platforms don't support named pipe based port negotiation
166167
use_named_pipe = False
167168

168169
# Grab the ppid from /proc/[shell pid]/stat
169-
shell_stat = run_shell_cmd("cat /proc/$$/stat")
170+
shell_stat = self.run_shell_cmd("cat /proc/$$/stat")
170171
# [pid] ([executable]) [state] [*ppid*]
171172
pid = re.match(r"^\d+ \(.+\) . (\d+)", shell_stat).group(1)
172-
ls_output = run_shell_cmd("ls -l /proc/%s/exe" % pid)
173+
ls_output = self.run_shell_cmd("ls -l /proc/%s/exe" % pid)
173174
exe = ls_output.split()[-1]
174175

175176
# If the binary has been deleted, the link name has " (deleted)" appended.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
LEVEL = ../../../make
2+
3+
CXX_SOURCES := main.cpp
4+
5+
include $(LEVEL)/Makefile.rules
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from __future__ import print_function
2+
3+
import gdbremote_testcase
4+
from lldbsuite.test.lldbtest import *
5+
import lldbsuite.test.lldbutil as lldbutil
6+
7+
class TestPlatformProcessConnect(gdbremote_testcase.GdbRemoteTestCaseBase):
8+
mydir = TestBase.compute_mydir(__file__)
9+
10+
@llgs_test
11+
@no_debug_info_test
12+
@skipIf(remote=False)
13+
def test_platform_process_connect(self):
14+
self.build()
15+
self.init_llgs_test(False)
16+
17+
working_dir = lldb.remote_platform.GetWorkingDirectory()
18+
err = lldb.remote_platform.Put(lldb.SBFileSpec(os.path.join(os.getcwd(), "a.out")),
19+
lldb.SBFileSpec(os.path.join(working_dir, "a.out")))
20+
if err.Fail():
21+
raise RuntimeError("Unable copy '%s' to '%s'.\n>>> %s" % (f, wd, err.GetCString()))
22+
23+
port_file = "%s/port" % working_dir
24+
commandline_args = ["platform", "--listen", "*:0", "--socket-file", port_file, "--", "%s/a.out" % working_dir, "foo"]
25+
self.spawnSubprocess(self.debug_monitor_exe, commandline_args, install_remote=False)
26+
self.addTearDownHook(self.cleanupSubprocesses)
27+
new_port = self.run_shell_cmd("while [ ! -f %s ]; do sleep 0.25; done && cat %s" % (port_file, port_file))
28+
29+
new_debugger = lldb.SBDebugger.Create()
30+
new_debugger.SetAsync(False)
31+
def del_debugger():
32+
del new_debugger
33+
self.addTearDownHook(del_debugger)
34+
35+
new_platform = lldb.SBPlatform(lldb.remote_platform.GetName())
36+
new_debugger.SetSelectedPlatform(new_platform)
37+
new_interpreter = new_debugger.GetCommandInterpreter()
38+
39+
m = re.search("(.*):[0-9]+", configuration.lldb_platform_url)
40+
command = "platform connect %s:%s" % (m.group(1), new_port)
41+
result = lldb.SBCommandReturnObject()
42+
new_interpreter.HandleCommand(command, result)
43+
self.assertTrue(result.Succeeded(), "platform process connect failed: %s" % result.GetOutput())
44+
45+
target = new_debugger.GetSelectedTarget()
46+
process = target.GetProcess()
47+
thread = process.GetThreadAtIndex(0)
48+
49+
breakpoint = target.BreakpointCreateByName("main")
50+
process.Continue()
51+
52+
frame = thread.GetFrameAtIndex(0)
53+
self.assertEqual(frame.GetFunction().GetName(), "main")
54+
self.assertEqual(frame.FindVariable("argc").GetValueAsSigned(), 2)
55+
process.Continue()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <cstdio>
2+
3+
int main (int argc, char **argv)
4+
{
5+
printf("argc: %d\n", argc);
6+
return argv[0][0];
7+
}

‎source/Commands/CommandObjectPlatform.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,12 +409,19 @@ class CommandObjectPlatformConnect : public CommandObjectParsed
409409
if (error.Success())
410410
{
411411
platform_sp->GetStatus (ostrm);
412-
result.SetStatus (eReturnStatusSuccessFinishResult);
412+
result.SetStatus (eReturnStatusSuccessFinishResult);
413+
414+
platform_sp->ConnectToWaitingProcesses(m_interpreter.GetDebugger(), error);
415+
if (error.Fail())
416+
{
417+
result.AppendError (error.AsCString());
418+
result.SetStatus (eReturnStatusFailed);
419+
}
413420
}
414421
else
415422
{
416423
result.AppendErrorWithFormat ("%s\n", error.AsCString());
417-
result.SetStatus (eReturnStatusFailed);
424+
result.SetStatus (eReturnStatusFailed);
418425
}
419426
}
420427
else

‎source/Commands/CommandObjectProcess.cpp

Lines changed: 29 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,78 +1051,46 @@ class CommandObjectProcessConnect : public CommandObjectParsed
10511051
bool
10521052
DoExecute (Args& command, CommandReturnObject &result) override
10531053
{
1054-
1055-
TargetSP target_sp (m_interpreter.GetDebugger().GetSelectedTarget());
1056-
Error error;
1057-
Process *process = m_exe_ctx.GetProcessPtr();
1058-
if (process)
1054+
if (command.GetArgumentCount() != 1)
10591055
{
1060-
if (process->IsAlive())
1061-
{
1062-
result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n",
1063-
process->GetID());
1064-
result.SetStatus (eReturnStatusFailed);
1065-
return false;
1066-
}
1056+
result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n",
1057+
m_cmd_name.c_str(),
1058+
m_cmd_syntax.c_str());
1059+
result.SetStatus (eReturnStatusFailed);
1060+
return false;
10671061
}
1062+
10681063

1069-
if (!target_sp)
1064+
Process *process = m_exe_ctx.GetProcessPtr();
1065+
if (process && process->IsAlive())
10701066
{
1071-
// If there isn't a current target create one.
1072-
1073-
error = m_interpreter.GetDebugger().GetTargetList().CreateTarget (m_interpreter.GetDebugger(),
1074-
NULL,
1075-
NULL,
1076-
false,
1077-
NULL, // No platform options
1078-
target_sp);
1079-
if (!target_sp || error.Fail())
1080-
{
1081-
result.AppendError(error.AsCString("Error creating target"));
1082-
result.SetStatus (eReturnStatusFailed);
1083-
return false;
1084-
}
1085-
m_interpreter.GetDebugger().GetTargetList().SetSelectedTarget(target_sp.get());
1067+
result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n",
1068+
process->GetID());
1069+
result.SetStatus (eReturnStatusFailed);
1070+
return false;
10861071
}
1087-
1088-
if (command.GetArgumentCount() == 1)
1089-
{
1090-
const char *plugin_name = NULL;
1091-
if (!m_options.plugin_name.empty())
1092-
plugin_name = m_options.plugin_name.c_str();
10931072

1094-
const char *remote_url = command.GetArgumentAtIndex(0);
1095-
process = target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get();
1096-
1097-
if (process)
1098-
{
1099-
error = process->ConnectRemote (process->GetTarget().GetDebugger().GetOutputFile().get(), remote_url);
1073+
const char *plugin_name = nullptr;
1074+
if (!m_options.plugin_name.empty())
1075+
plugin_name = m_options.plugin_name.c_str();
11001076

1101-
if (error.Fail())
1102-
{
1103-
result.AppendError(error.AsCString("Remote connect failed"));
1104-
result.SetStatus (eReturnStatusFailed);
1105-
target_sp->DeleteCurrentProcess();
1106-
return false;
1107-
}
1108-
}
1109-
else
1110-
{
1111-
result.AppendErrorWithFormat ("Unable to find process plug-in for remote URL '%s'.\nPlease specify a process plug-in name with the --plugin option, or specify an object file using the \"file\" command.\n",
1112-
remote_url);
1113-
result.SetStatus (eReturnStatusFailed);
1114-
}
1115-
}
1116-
else
1077+
Error error;
1078+
Debugger& debugger = m_interpreter.GetDebugger();
1079+
PlatformSP platform_sp = m_interpreter.GetPlatform(true);
1080+
ProcessSP process_sp = platform_sp->ConnectProcess(command.GetArgumentAtIndex(0),
1081+
plugin_name,
1082+
debugger,
1083+
debugger.GetSelectedTarget().get(),
1084+
error);
1085+
if (error.Fail() || process_sp == nullptr)
11171086
{
1118-
result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n",
1119-
m_cmd_name.c_str(),
1120-
m_cmd_syntax.c_str());
1087+
result.AppendError(error.AsCString("Error connecting to the process"));
11211088
result.SetStatus (eReturnStatusFailed);
1089+
return false;
11221090
}
1123-
return result.Succeeded();
1091+
return true;
11241092
}
1125-
1093+
11261094
CommandOptions m_options;
11271095
};
11281096

‎source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,53 @@ PlatformAndroidRemoteGDBServer::MakeConnectURL(const lldb::pid_t pid,
223223

224224
return error;
225225
}
226+
227+
lldb::ProcessSP
228+
PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url,
229+
const char* plugin_name,
230+
lldb_private::Debugger &debugger,
231+
lldb_private::Target *target,
232+
lldb_private::Error &error)
233+
{
234+
// We don't have the pid of the remote gdbserver when it isn't started by us but we still want
235+
// to store the list of port forwards we set up in our port forward map. Generate a fake pid for
236+
// these cases what won't collide with any other valid pid on android.
237+
static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL;
238+
239+
int remote_port;
240+
std::string scheme, host, path;
241+
if (!UriParser::Parse(connect_url, scheme, host, remote_port, path))
242+
{
243+
error.SetErrorStringWithFormat("Invalid URL: %s", connect_url);
244+
return nullptr;
245+
}
246+
247+
std::string new_connect_url;
248+
error = MakeConnectURL(s_remote_gdbserver_fake_pid--,
249+
(remote_port < 0) ? 0 : remote_port,
250+
path.c_str(),
251+
new_connect_url);
252+
if (error.Fail())
253+
return nullptr;
254+
255+
return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(),
256+
plugin_name,
257+
debugger,
258+
target,
259+
error);
260+
}
261+
262+
size_t
263+
PlatformAndroidRemoteGDBServer::ConnectToWaitingProcesses(Debugger& debugger, Error& error)
264+
{
265+
std::vector<std::string> connection_urls;
266+
GetPendingGdbServerList(connection_urls);
267+
268+
for (size_t i = 0; i < connection_urls.size(); ++i)
269+
{
270+
ConnectProcess(connection_urls[i].c_str(), nullptr, debugger, nullptr, error);
271+
if (error.Fail())
272+
return i; // We already connected to i process succsessfully
273+
}
274+
return connection_urls.size();
275+
}

0 commit comments

Comments
 (0)
This repository has been archived.