diff --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h --- a/lldb/include/lldb/Utility/ProcessInfo.h +++ b/lldb/include/lldb/Utility/ProcessInfo.h @@ -73,6 +73,10 @@ void Dump(Stream &s, Platform *platform) const; + void SetBundleID(llvm::StringRef bundle_id) { m_bundle_id = bundle_id; } + + llvm::StringRef GetBundleID() const { return m_bundle_id; } + Args &GetArguments() { return m_arguments; } const Args &GetArguments() const { return m_arguments; } @@ -90,6 +94,9 @@ protected: FileSpec m_executable; + std::string m_bundle_id; // On platforms like Darwin or Android, some + // programs are based on bundles, like Darwin App Bundles or Android apks, + // whose identifiers are not their actual paths on disk. std::string m_arg0; // argv[0] if supported. If empty, then use m_executable. // Not all process plug-ins support specifying an argv[0] that differs from // the resolved platform executable (which is in m_executable) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestPlatformClient.py b/lldb/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestPlatformClient.py --- a/lldb/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestPlatformClient.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/gdb_remote_client/TestPlatformClient.py @@ -5,9 +5,11 @@ from lldbsuite.test.decorators import * from gdbclientutils import * + def hexlify(string): return binascii.hexlify(string.encode()).decode() + class TestPlatformClient(GDBRemoteTestBase): def test_process_list_with_all_users(self): @@ -16,31 +18,42 @@ class MyResponder(MockGDBServerResponder): def __init__(self): MockGDBServerResponder.__init__(self) - self.currentQsProc = 0 + self.currentProc = 0 self.all_users = False + def _processInfo0(self): + name = hexlify("/a/test_process") + args = "-".join(map(hexlify, + ["/system/bin/sh", "-c", "/data/local/tmp/lldb-server"])) + return "pid:10;ppid:1;uid:2;gid:3;euid:4;egid:5;name:" + name + ";args:" + args + ";" + + def _processInfo1(self): + name = hexlify("/b/another_test_process") + # This intentionally has a badly encoded argument + args = "X".join(map(hexlify, + ["/system/bin/ls", "--help"])) + return "pid:11;ppid:2;uid:3;gid:4;euid:5;egid:6;name:" + name + ";args:" + args + ";" + + def _processInfo2(self): + # a process without args but with bundle id, which can happen on android + bundle_id = hexlify("com.test.app") + return "pid:12;ppid:3;uid:4;gid:5;euid:6;egid:7;name:;args:;bundle_id:" + bundle_id + ";" + def qfProcessInfo(self, packet): - if "all_users:1" in packet: - self.all_users = True - name = hexlify("/a/test_process") - args = "-".join(map(hexlify, - ["/system/bin/sh", "-c", "/data/local/tmp/lldb-server"])) - return "pid:10;ppid:1;uid:2;gid:3;euid:4;egid:5;name:" + name + ";args:" + args + ";" - else: - self.all_users = False - return "E04" + self.all_users = "all_users:1" in packet + self.currentProc = 1 + return self._processInfo0() def qsProcessInfo(self): if self.all_users: - if self.currentQsProc == 0: - self.currentQsProc = 1 - name = hexlify("/b/another_test_process") - # This intentionally has a badly encoded argument - args = "X".join(map(hexlify, - ["/system/bin/ls", "--help"])) - return "pid:11;ppid:2;uid:3;gid:4;euid:5;egid:6;name:" + name + ";args:" + args + ";" - elif self.currentQsProc == 1: - self.currentQsProc = 0 + if self.currentProc == 1: + self.currentProc = 2 + return self._processInfo1() + elif self.currentProc == 2: + self.currentProc = 3 + return self._processInfo2() + else: + self.currentProc = 0 return "E04" else: return "E04" @@ -53,15 +66,15 @@ self.server.port) self.assertTrue(self.dbg.GetSelectedPlatform().IsConnected()) self.expect("platform process list -x", - substrs=["2 matching processes were found", "test_process", "another_test_process"]) + substrs=["3 matching processes were found", "test_process", "another_test_process", "com.test.app"]) self.expect("platform process list -xv", substrs=[ "PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE ARGUMENTS", "10 1 2 3 4 5 /system/bin/sh -c /data/local/tmp/lldb-server", "11 2 3 4 5 6"]) - self.expect("platform process list -xv", substrs=["/system/bin/ls"], matching=False) + self.expect("platform process list -xv", + substrs=["/system/bin/ls"], matching=False) self.expect("platform process list", - error=True, - substrs=["error: no processes were found on the \"remote-linux\" platform"]) + substrs=["1 matching process was found"]) finally: self.dbg.GetSelectedPlatform().DisconnectRemote() diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -212,6 +212,16 @@ GetExePathAndArch(pid, process_info); GetProcessArgs(pid, process_info); +#if defined(__ANDROID__) + // On android, when /proc//exe is not readable, then /proc//cmdline + // contains the apk package name, which we use as bundle id. + // If /proc//cmdline is not readable, then it coressponds to an operating + // system process, which is undebuggable unless the device is rooted. + if (process_info.GetNameAsStringRef().empty() && + !process_info.GetArg0().empty()) { + process_info.SetBundleID(process_info.GetArg0()); + } +#endif GetProcessEnviron(pid, process_info); // Get User and Group IDs and get tracer pid. diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -1947,6 +1947,11 @@ process_info.GetArguments().AppendArgument(arg); is_arg0 = false; } + } else if (name.equals("bundle_id")) { + StringExtractor extractor(value); + std::string bundle_id; + extractor.GetHexByteString(bundle_id); + process_info.SetBundleID(bundle_id); } else if (name.equals("cputype")) { value.getAsInteger(0, cpu); } else if (name.equals("cpusubtype")) { diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -1182,14 +1182,16 @@ response.PutCString("name:"); response.PutStringAsRawHex8(proc_info.GetExecutableFile().GetCString()); - response.PutChar(';'); - response.PutCString("args:"); + response.PutCString(";args:"); response.PutStringAsRawHex8(proc_info.GetArg0()); for (auto &arg : proc_info.GetArguments()) { response.PutChar('-'); response.PutStringAsRawHex8(arg.ref()); } + response.PutCString(";bundle_id:"); + response.PutStringAsRawHex8(proc_info.GetBundleID()); + response.PutChar(';'); const ArchSpec &proc_arch = proc_info.GetArchitecture(); if (proc_arch.IsValid()) { diff --git a/lldb/source/Utility/ProcessInfo.cpp b/lldb/source/Utility/ProcessInfo.cpp --- a/lldb/source/Utility/ProcessInfo.cpp +++ b/lldb/source/Utility/ProcessInfo.cpp @@ -38,11 +38,18 @@ m_pid = LLDB_INVALID_PROCESS_ID; } +// On systems like android, there are cases in which a process name can +// correspond to a bundle id (e.g. com.test.app), which is not an executable +// path. const char *ProcessInfo::GetName() const { + if (m_executable.GetFilename().IsEmpty() && !m_bundle_id.empty()) + return m_bundle_id.c_str(); return m_executable.GetFilename().GetCString(); } llvm::StringRef ProcessInfo::GetNameAsStringRef() const { + if (m_executable.GetFilename().IsEmpty() && !m_bundle_id.empty()) + return m_bundle_id; return m_executable.GetFilename().GetStringRef(); }