## Summary
Fix `FindProcesses` to respect Android's `hidepid=2` security model and
enable name matching for Android apps.
## Problem
1. Called `adb shell pidof` or `adb shell ps` directly, bypassing
Android's process visibility restrictions
2. Name matching failed for Android apps - searched for
`com.example.myapp` but GDB Remote Protocol reports `app_process64`
Android apps fork from Zygote, so `/proc/PID/exe` points to
`app_process64` for all apps. The actual package name is only in
`/proc/PID/cmdline`. The previous implementation applied name filters
without supplementing with cmdline, so searches failed.
## Fix
- Delegate to lldb-server via GDB Remote Protocol (respects `hidepid=2`)
- Get all visible processes, supplement zygote/app_process entries with
cmdline, then apply name matching
- Only fetch cmdline for zygote apps (performance), parallelize with
`xargs -P 8`
- Remove redundant code (GDB Remote Protocol already provides GID/arch)
## Test Results
### Before this fix:
```
(lldb) platform process list
error: no processes were found on the "remote-android" platform
(lldb) platform process list -n com.example.hellojni
1 matching process was found on "remote-android"
PID PARENT USER TRIPLE NAME
====== ====== ========== ============================== ============================
5276 359 u0_a192 com.example.hellojni
^^^^^^^^ Missing triple!
```
### After this fix:
```
(lldb) platform process list
PID PARENT USER TRIPLE NAME
====== ====== ========== ============================== ============================
1 0 root aarch64-unknown-linux-android init
2 0 root [kthreadd]
359 1 system aarch64-unknown-linux-android app_process64
5276 359 u0_a192 aarch64-unknown-linux-android com.example.hellojni
5357 5355 u0_a192 aarch64-unknown-linux-android sh
5377 5370 u0_a192 aarch64-unknown-linux-android lldb-server
^^^^^^^^ User-space processes now have triples!
(lldb) platform process list -n com.example.hellojni
1 matching process was found on "remote-android"
PID PARENT USER TRIPLE NAME
====== ====== ========== ============================== ============================
5276 359 u0_a192 aarch64-unknown-linux-android com.example.hellojni
(lldb) process attach -n com.example.hellojni
Process 5276 stopped
* thread #1, name = 'example.hellojni', stop reason = signal SIGSTOP
```
## Test Plan
With an Android device/emulator connected:
1. Start lldb-server on device:
```bash
adb push lldb-server /data/local/tmp/
adb shell chmod +x /data/local/tmp/lldb-server
adb shell /data/local/tmp/lldb-server platform --listen 127.0.0.1:9500 --server
```
2. Connect from LLDB:
```
(lldb) platform select remote-android
(lldb) platform connect connect://127.0.0.1:9500
(lldb) platform process list
```
3. Verify:
- `platform process list` returns all processes with triple information
- `platform process list -n com.example.app` finds Android apps by
package name
- `process attach -n com.example.app` successfully attaches to Android
apps
## Impact
Restores `platform process list` on Android with architecture
information and package name lookup. All name matching modes now work
correctly.
Fixes https://github.com/llvm/llvm-project/issues/164192
617 lines
20 KiB
C++
617 lines
20 KiB
C++
//===-- PlatformAndroid.cpp -----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Host/HostInfo.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "lldb/Utility/UriParser.h"
|
|
#include "lldb/ValueObject/ValueObject.h"
|
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "AdbClient.h"
|
|
#include "PlatformAndroid.h"
|
|
#include "PlatformAndroidRemoteGDBServer.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include <optional>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::platform_android;
|
|
using namespace std::chrono;
|
|
|
|
LLDB_PLUGIN_DEFINE(PlatformAndroid)
|
|
|
|
namespace {
|
|
|
|
#define LLDB_PROPERTIES_android
|
|
#include "PlatformAndroidProperties.inc"
|
|
|
|
enum {
|
|
#define LLDB_PROPERTIES_android
|
|
#include "PlatformAndroidPropertiesEnum.inc"
|
|
};
|
|
|
|
class PluginProperties : public Properties {
|
|
public:
|
|
PluginProperties() {
|
|
m_collection_sp = std::make_shared<OptionValueProperties>(
|
|
PlatformAndroid::GetPluginNameStatic(false));
|
|
m_collection_sp->Initialize(g_android_properties);
|
|
}
|
|
};
|
|
|
|
static PluginProperties &GetGlobalProperties() {
|
|
static PluginProperties g_settings;
|
|
return g_settings;
|
|
}
|
|
|
|
uint32_t g_initialize_count = 0;
|
|
const unsigned int g_android_default_cache_size =
|
|
2048; // Fits inside 4k adb packet.
|
|
|
|
} // end of anonymous namespace
|
|
|
|
void PlatformAndroid::Initialize() {
|
|
PlatformLinux::Initialize();
|
|
|
|
if (g_initialize_count++ == 0) {
|
|
#if defined(__ANDROID__)
|
|
PlatformSP default_platform_sp(new PlatformAndroid(true));
|
|
default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture());
|
|
Platform::SetHostPlatform(default_platform_sp);
|
|
#endif
|
|
PluginManager::RegisterPlugin(
|
|
PlatformAndroid::GetPluginNameStatic(false),
|
|
PlatformAndroid::GetPluginDescriptionStatic(false),
|
|
PlatformAndroid::CreateInstance, PlatformAndroid::DebuggerInitialize);
|
|
}
|
|
}
|
|
|
|
void PlatformAndroid::Terminate() {
|
|
if (g_initialize_count > 0) {
|
|
if (--g_initialize_count == 0) {
|
|
PluginManager::UnregisterPlugin(PlatformAndroid::CreateInstance);
|
|
}
|
|
}
|
|
|
|
PlatformLinux::Terminate();
|
|
}
|
|
|
|
PlatformSP PlatformAndroid::CreateInstance(bool force, const ArchSpec *arch) {
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
if (log) {
|
|
const char *arch_name;
|
|
if (arch && arch->GetArchitectureName())
|
|
arch_name = arch->GetArchitectureName();
|
|
else
|
|
arch_name = "<null>";
|
|
|
|
const char *triple_cstr =
|
|
arch ? arch->GetTriple().getTriple().c_str() : "<null>";
|
|
|
|
LLDB_LOGF(log, "PlatformAndroid::%s(force=%s, arch={%s,%s})", __FUNCTION__,
|
|
force ? "true" : "false", arch_name, triple_cstr);
|
|
}
|
|
|
|
bool create = force;
|
|
if (!create && arch && arch->IsValid()) {
|
|
const llvm::Triple &triple = arch->GetTriple();
|
|
switch (triple.getVendor()) {
|
|
case llvm::Triple::PC:
|
|
create = true;
|
|
break;
|
|
|
|
#if defined(__ANDROID__)
|
|
// Only accept "unknown" for the vendor if the host is android and if
|
|
// "unknown" wasn't specified (it was just returned because it was NOT
|
|
// specified).
|
|
case llvm::Triple::VendorType::UnknownVendor:
|
|
create = !arch->TripleVendorWasSpecified();
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (create) {
|
|
switch (triple.getEnvironment()) {
|
|
case llvm::Triple::Android:
|
|
break;
|
|
|
|
#if defined(__ANDROID__)
|
|
// Only accept "unknown" for the OS if the host is android and it
|
|
// "unknown" wasn't specified (it was just returned because it was NOT
|
|
// specified)
|
|
case llvm::Triple::EnvironmentType::UnknownEnvironment:
|
|
create = !arch->TripleEnvironmentWasSpecified();
|
|
break;
|
|
#endif
|
|
default:
|
|
create = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (create) {
|
|
LLDB_LOGF(log, "PlatformAndroid::%s() creating remote-android platform",
|
|
__FUNCTION__);
|
|
return PlatformSP(new PlatformAndroid(false));
|
|
}
|
|
|
|
LLDB_LOGF(
|
|
log, "PlatformAndroid::%s() aborting creation of remote-android platform",
|
|
__FUNCTION__);
|
|
|
|
return PlatformSP();
|
|
}
|
|
|
|
void PlatformAndroid::DebuggerInitialize(Debugger &debugger) {
|
|
if (!PluginManager::GetSettingForPlatformPlugin(debugger,
|
|
GetPluginNameStatic(false))) {
|
|
PluginManager::CreateSettingForPlatformPlugin(
|
|
debugger, GetGlobalProperties().GetValueProperties(),
|
|
"Properties for the Android platform plugin.",
|
|
/*is_global_property=*/true);
|
|
}
|
|
}
|
|
|
|
PlatformAndroid::PlatformAndroid(bool is_host)
|
|
: PlatformLinux(is_host), m_sdk_version(0) {}
|
|
|
|
llvm::StringRef PlatformAndroid::GetPluginDescriptionStatic(bool is_host) {
|
|
if (is_host)
|
|
return "Local Android user platform plug-in.";
|
|
return "Remote Android user platform plug-in.";
|
|
}
|
|
|
|
Status PlatformAndroid::ConnectRemote(Args &args) {
|
|
m_device_id.clear();
|
|
|
|
if (IsHost())
|
|
return Status::FromErrorString(
|
|
"can't connect to the host platform, always connected");
|
|
|
|
if (!m_remote_platform_sp)
|
|
m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer());
|
|
|
|
const char *url = args.GetArgumentAtIndex(0);
|
|
if (!url)
|
|
return Status::FromErrorString("URL is null.");
|
|
std::optional<URI> parsed_url = URI::Parse(url);
|
|
if (!parsed_url)
|
|
return Status::FromErrorStringWithFormat("Invalid URL: %s", url);
|
|
if (parsed_url->hostname != "localhost")
|
|
m_device_id = parsed_url->hostname.str();
|
|
|
|
auto error = PlatformLinux::ConnectRemote(args);
|
|
if (error.Success()) {
|
|
auto resolved_device_id_or_error = AdbClient::ResolveDeviceID(m_device_id);
|
|
if (!resolved_device_id_or_error)
|
|
return Status::FromError(resolved_device_id_or_error.takeError());
|
|
m_device_id = *resolved_device_id_or_error;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
Status PlatformAndroid::GetFile(const FileSpec &source,
|
|
const FileSpec &destination) {
|
|
if (IsHost() || !m_remote_platform_sp)
|
|
return PlatformLinux::GetFile(source, destination);
|
|
|
|
FileSpec source_spec(source.GetPath(false), FileSpec::Style::posix);
|
|
if (source_spec.IsRelative())
|
|
source_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent(
|
|
source_spec.GetPathAsConstString(false).GetStringRef());
|
|
|
|
Status error;
|
|
auto sync_service = GetSyncService(error);
|
|
|
|
// If sync service is available, try to use it
|
|
if (error.Success() && sync_service) {
|
|
uint32_t mode = 0, size = 0, mtime = 0;
|
|
error = sync_service->Stat(source_spec, mode, size, mtime);
|
|
if (error.Success()) {
|
|
if (mode != 0)
|
|
return sync_service->PullFile(source_spec, destination);
|
|
|
|
// mode == 0 can signify that adbd cannot access the file due security
|
|
// constraints - fall through to try "cat ..." as a fallback.
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
LLDB_LOGF(log, "Got mode == 0 on '%s': try to get file via 'shell cat'",
|
|
source_spec.GetPath(false).c_str());
|
|
}
|
|
}
|
|
|
|
// Fallback to shell cat command if sync service failed or returned mode == 0
|
|
std::string source_file = source_spec.GetPath(false);
|
|
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
LLDB_LOGF(log, "Using shell cat fallback for '%s'", source_file.c_str());
|
|
|
|
if (strchr(source_file.c_str(), '\'') != nullptr)
|
|
return Status::FromErrorString(
|
|
"Doesn't support single-quotes in filenames");
|
|
|
|
AdbClientUP adb(GetAdbClient(error));
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
char cmd[PATH_MAX];
|
|
snprintf(cmd, sizeof(cmd), "%scat '%s'", GetRunAs().c_str(),
|
|
source_file.c_str());
|
|
|
|
return adb->ShellToFile(cmd, minutes(1), destination);
|
|
}
|
|
|
|
Status PlatformAndroid::PutFile(const FileSpec &source,
|
|
const FileSpec &destination, uint32_t uid,
|
|
uint32_t gid) {
|
|
if (IsHost() || !m_remote_platform_sp)
|
|
return PlatformLinux::PutFile(source, destination, uid, gid);
|
|
|
|
FileSpec destination_spec(destination.GetPath(false), FileSpec::Style::posix);
|
|
if (destination_spec.IsRelative())
|
|
destination_spec = GetRemoteWorkingDirectory().CopyByAppendingPathComponent(
|
|
destination_spec.GetPath(false));
|
|
|
|
// TODO: Set correct uid and gid on remote file.
|
|
Status error;
|
|
auto sync_service = GetSyncService(error);
|
|
if (error.Fail())
|
|
return error;
|
|
return sync_service->PushFile(source, destination_spec);
|
|
}
|
|
|
|
const char *PlatformAndroid::GetCacheHostname() { return m_device_id.c_str(); }
|
|
|
|
Status PlatformAndroid::DownloadModuleSlice(const FileSpec &src_file_spec,
|
|
const uint64_t src_offset,
|
|
const uint64_t src_size,
|
|
const FileSpec &dst_file_spec) {
|
|
std::string source_file = src_file_spec.GetPath(false);
|
|
if (source_file.empty())
|
|
return Status::FromErrorString("Source file path cannot be empty");
|
|
|
|
std::string destination_file = dst_file_spec.GetPath(false);
|
|
if (destination_file.empty())
|
|
return Status::FromErrorString("Destination file path cannot be empty");
|
|
|
|
// In Android API level 23 and above, dynamic loader is able to load .so
|
|
// file directly from APK. In that case, src_offset will be an non-zero.
|
|
if (src_offset == 0) // Use GetFile for a normal file.
|
|
return GetFile(src_file_spec, dst_file_spec);
|
|
|
|
if (source_file.find('\'') != std::string::npos)
|
|
return Status::FromErrorString(
|
|
"Doesn't support single-quotes in filenames");
|
|
|
|
// For zip .so file, src_file_spec will be "zip_path!/so_path".
|
|
// Extract "zip_path" from the source_file.
|
|
static constexpr llvm::StringLiteral k_zip_separator("!/");
|
|
size_t pos = source_file.find(k_zip_separator);
|
|
if (pos != std::string::npos)
|
|
source_file.resize(pos);
|
|
|
|
Status error;
|
|
AdbClientUP adb(GetAdbClient(error));
|
|
if (error.Fail())
|
|
return error;
|
|
|
|
// Use 'shell dd' to download the file slice with the offset and size.
|
|
char cmd[PATH_MAX];
|
|
snprintf(cmd, sizeof(cmd),
|
|
"%sdd if='%s' iflag=skip_bytes,count_bytes "
|
|
"skip=%" PRIu64 " count=%" PRIu64 " status=none",
|
|
GetRunAs().c_str(), source_file.c_str(), src_offset, src_size);
|
|
|
|
return adb->ShellToFile(cmd, minutes(1), dst_file_spec);
|
|
}
|
|
|
|
Status PlatformAndroid::DisconnectRemote() {
|
|
Status error = PlatformLinux::DisconnectRemote();
|
|
if (error.Success()) {
|
|
m_device_id.clear();
|
|
m_sdk_version = 0;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
uint32_t PlatformAndroid::GetDefaultMemoryCacheLineSize() {
|
|
return g_android_default_cache_size;
|
|
}
|
|
|
|
uint32_t PlatformAndroid::GetSdkVersion() {
|
|
if (!IsConnected())
|
|
return 0;
|
|
|
|
if (m_sdk_version != 0)
|
|
return m_sdk_version;
|
|
|
|
std::string version_string;
|
|
Status error;
|
|
AdbClientUP adb(GetAdbClient(error));
|
|
if (error.Fail())
|
|
return 0;
|
|
error =
|
|
adb->Shell("getprop ro.build.version.sdk", seconds(5), &version_string);
|
|
version_string = llvm::StringRef(version_string).trim().str();
|
|
|
|
if (error.Fail() || version_string.empty()) {
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
LLDB_LOGF(log, "Get SDK version failed. (error: %s, output: %s)",
|
|
error.AsCString(), version_string.c_str());
|
|
return 0;
|
|
}
|
|
|
|
// FIXME: improve error handling
|
|
llvm::to_integer(version_string, m_sdk_version);
|
|
return m_sdk_version;
|
|
}
|
|
|
|
Status PlatformAndroid::DownloadSymbolFile(const lldb::ModuleSP &module_sp,
|
|
const FileSpec &dst_file_spec) {
|
|
// For oat file we can try to fetch additional debug info from the device
|
|
llvm::StringRef extension = module_sp->GetFileSpec().GetFileNameExtension();
|
|
if (extension != ".oat" && extension != ".odex")
|
|
return Status::FromErrorString(
|
|
"Symbol file downloading only supported for oat and odex files");
|
|
|
|
// If we have no information about the platform file we can't execute oatdump
|
|
if (!module_sp->GetPlatformFileSpec())
|
|
return Status::FromErrorString("No platform file specified");
|
|
|
|
// Symbolizer isn't available before SDK version 23
|
|
if (GetSdkVersion() < 23)
|
|
return Status::FromErrorString(
|
|
"Symbol file generation only supported on SDK 23+");
|
|
|
|
// If we already have symtab then we don't have to try and generate one
|
|
if (module_sp->GetSectionList()->FindSectionByName(ConstString(".symtab")) !=
|
|
nullptr)
|
|
return Status::FromErrorString("Symtab already available in the module");
|
|
|
|
Status error;
|
|
AdbClientUP adb(GetAdbClient(error));
|
|
if (error.Fail())
|
|
return error;
|
|
std::string tmpdir;
|
|
error = adb->Shell("mktemp --directory --tmpdir /data/local/tmp", seconds(5),
|
|
&tmpdir);
|
|
if (error.Fail() || tmpdir.empty())
|
|
return Status::FromErrorStringWithFormat(
|
|
"Failed to generate temporary directory on the device (%s)",
|
|
error.AsCString());
|
|
tmpdir = llvm::StringRef(tmpdir).trim().str();
|
|
|
|
// Create file remover for the temporary directory created on the device
|
|
std::unique_ptr<std::string, std::function<void(std::string *)>>
|
|
tmpdir_remover(&tmpdir, [&adb](std::string *s) {
|
|
StreamString command;
|
|
command.Printf("rm -rf %s", s->c_str());
|
|
Status error = adb->Shell(command.GetData(), seconds(5), nullptr);
|
|
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
if (log && error.Fail())
|
|
LLDB_LOGF(log, "Failed to remove temp directory: %s",
|
|
error.AsCString());
|
|
});
|
|
|
|
FileSpec symfile_platform_filespec(tmpdir);
|
|
symfile_platform_filespec.AppendPathComponent("symbolized.oat");
|
|
|
|
// Execute oatdump on the remote device to generate a file with symtab
|
|
StreamString command;
|
|
command.Printf("oatdump --symbolize=%s --output=%s",
|
|
module_sp->GetPlatformFileSpec().GetPath(false).c_str(),
|
|
symfile_platform_filespec.GetPath(false).c_str());
|
|
error = adb->Shell(command.GetData(), minutes(1), nullptr);
|
|
if (error.Fail())
|
|
return Status::FromErrorStringWithFormat("Oatdump failed: %s",
|
|
error.AsCString());
|
|
|
|
// Download the symbolfile from the remote device
|
|
return GetFile(symfile_platform_filespec, dst_file_spec);
|
|
}
|
|
|
|
bool PlatformAndroid::GetRemoteOSVersion() {
|
|
m_os_version = llvm::VersionTuple(GetSdkVersion());
|
|
return !m_os_version.empty();
|
|
}
|
|
|
|
llvm::StringRef
|
|
PlatformAndroid::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
|
|
SymbolContextList matching_symbols;
|
|
std::vector<const char *> dl_open_names = {"__dl_dlopen", "dlopen"};
|
|
const char *dl_open_name = nullptr;
|
|
Target &target = process->GetTarget();
|
|
for (auto *name : dl_open_names) {
|
|
target.GetImages().FindFunctionSymbols(
|
|
ConstString(name), eFunctionNameTypeFull, matching_symbols);
|
|
if (matching_symbols.GetSize()) {
|
|
dl_open_name = name;
|
|
break;
|
|
}
|
|
}
|
|
// Older platform versions have the dl function symbols mangled
|
|
if (dl_open_name == dl_open_names[0])
|
|
return R"(
|
|
extern "C" void* dlopen(const char*, int) asm("__dl_dlopen");
|
|
extern "C" void* dlsym(void*, const char*) asm("__dl_dlsym");
|
|
extern "C" int dlclose(void*) asm("__dl_dlclose");
|
|
extern "C" char* dlerror(void) asm("__dl_dlerror");
|
|
)";
|
|
|
|
return PlatformPOSIX::GetLibdlFunctionDeclarations(process);
|
|
}
|
|
|
|
PlatformAndroid::AdbClientUP PlatformAndroid::GetAdbClient(Status &error) {
|
|
AdbClientUP adb = std::make_unique<AdbClient>(m_device_id);
|
|
error = adb->Connect();
|
|
return adb;
|
|
}
|
|
|
|
llvm::StringRef PlatformAndroid::GetPropertyPackageName() {
|
|
return GetGlobalProperties().GetPropertyAtIndexAs<llvm::StringRef>(
|
|
ePropertyPlatformPackageName, "");
|
|
}
|
|
|
|
std::string PlatformAndroid::GetRunAs() {
|
|
llvm::StringRef run_as = GetPropertyPackageName();
|
|
if (!run_as.empty()) {
|
|
// When LLDB fails to pull file from a package directory due to security
|
|
// constraint, user needs to set the package name to
|
|
// 'platform.plugin.remote-android.package-name' property in order to run
|
|
// shell commands as the package user using 'run-as' (e.g. to get file with
|
|
// 'cat' and 'dd').
|
|
// https://cs.android.com/android/platform/superproject/+/master:
|
|
// system/core/run-as/run-as.cpp;l=39-61;
|
|
// drc=4a77a84a55522a3b122f9c63ef0d0b8a6a131627
|
|
return std::string("run-as '") + run_as.str() + "' ";
|
|
}
|
|
return run_as.str();
|
|
}
|
|
|
|
static bool NeedsCmdlineSupplement(const ProcessInstanceInfo &proc_info) {
|
|
llvm::StringRef name =
|
|
proc_info.GetExecutableFile().GetFilename().GetStringRef();
|
|
return name.contains("app_process") || name.contains("zygote");
|
|
}
|
|
|
|
// Fetch /proc/PID/cmdline for processes to get actual package names.
|
|
// Android apps often show as "zygote" or "app_process" without this.
|
|
static void SupplementWithCmdlineInfo(ProcessInstanceInfoList &proc_infos,
|
|
AdbClient *adb) {
|
|
if (proc_infos.empty())
|
|
return;
|
|
|
|
llvm::DenseMap<lldb::pid_t, ProcessInstanceInfo *> pid_map;
|
|
std::string pid_list;
|
|
for (auto &proc_info : proc_infos) {
|
|
if (NeedsCmdlineSupplement(proc_info)) {
|
|
lldb::pid_t pid = proc_info.GetProcessID();
|
|
pid_map[pid] = &proc_info;
|
|
if (!pid_list.empty())
|
|
pid_list += " ";
|
|
pid_list += std::to_string(pid);
|
|
}
|
|
}
|
|
|
|
if (pid_list.empty())
|
|
return;
|
|
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
|
|
// Use xargs -P to parallelize cmdline fetching (up to 8 concurrent reads)
|
|
StreamString cmd;
|
|
cmd.Printf(
|
|
"echo '%s' | xargs -n 1 -P 8 sh -c "
|
|
"'echo \"$1:$(cat /proc/$1/cmdline 2>/dev/null | tr \"\\0\" \" \")\"' sh",
|
|
pid_list.c_str());
|
|
|
|
std::string cmdline_output;
|
|
Status error = adb->Shell(cmd.GetData(), seconds(5), &cmdline_output);
|
|
|
|
if (error.Fail() || cmdline_output.empty())
|
|
return;
|
|
|
|
llvm::SmallVector<llvm::StringRef, 256> lines;
|
|
llvm::StringRef(cmdline_output).split(lines, '\n', -1, false);
|
|
|
|
for (llvm::StringRef line : lines) {
|
|
line = line.trim();
|
|
auto [pid_str, cmdline] = line.split(':');
|
|
if (pid_str.empty() || cmdline.empty())
|
|
continue;
|
|
|
|
cmdline = cmdline.trim();
|
|
|
|
lldb::pid_t pid;
|
|
if (!llvm::to_integer(pid_str, pid) || cmdline.empty())
|
|
continue;
|
|
|
|
auto it = pid_map.find(pid);
|
|
if (it == pid_map.end())
|
|
continue;
|
|
|
|
ProcessInstanceInfo *proc_info = it->second;
|
|
llvm::SmallVector<llvm::StringRef, 16> args;
|
|
cmdline.split(args, ' ', -1, false);
|
|
|
|
if (!args.empty()) {
|
|
proc_info->GetExecutableFile().SetFile(args[0], FileSpec::Style::posix);
|
|
|
|
if (args.size() > 1) {
|
|
Args process_args;
|
|
for (size_t i = 1; i < args.size(); ++i) {
|
|
if (!args[i].empty())
|
|
process_args.AppendArgument(args[i]);
|
|
}
|
|
proc_info->SetArguments(process_args, false);
|
|
}
|
|
|
|
LLDB_LOGF(log,
|
|
"PlatformAndroid::%s supplemented PID %llu with cmdline: %s",
|
|
__FUNCTION__, static_cast<unsigned long long>(pid),
|
|
cmdline.str().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
|
|
ProcessInstanceInfoList &proc_infos) {
|
|
proc_infos.clear();
|
|
|
|
if (IsHost())
|
|
return PlatformLinux::FindProcesses(match_info, proc_infos);
|
|
|
|
if (!m_remote_platform_sp)
|
|
return 0;
|
|
|
|
// Android-specific process name handling:
|
|
// Apps spawned from zygote initially appear as "app_process" or "zygote"
|
|
// in the process list, but their actual package names (e.g.,
|
|
// "com.example.app") are only available in /proc/PID/cmdline. To support
|
|
// name-based matching, we must first fetch cmdline info for all processes,
|
|
// then apply the original name filter.
|
|
ProcessInstanceInfoMatch broad_match_info = match_info;
|
|
broad_match_info.SetNameMatchType(NameMatch::Ignore);
|
|
|
|
ProcessInstanceInfoList all_procs;
|
|
uint32_t count =
|
|
m_remote_platform_sp->FindProcesses(broad_match_info, all_procs);
|
|
|
|
if (count > 0) {
|
|
Status error;
|
|
AdbClientUP adb(GetAdbClient(error));
|
|
if (error.Success())
|
|
SupplementWithCmdlineInfo(all_procs, adb.get());
|
|
|
|
// Apply the original name matching against supplemented process info.
|
|
for (auto &proc_info : all_procs) {
|
|
if (match_info.Matches(proc_info))
|
|
proc_infos.push_back(proc_info);
|
|
}
|
|
}
|
|
|
|
return proc_infos.size();
|
|
}
|
|
|
|
std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) {
|
|
auto sync_service = std::make_unique<AdbSyncService>(m_device_id);
|
|
error = sync_service->SetupSyncConnection();
|
|
if (error.Fail())
|
|
return nullptr;
|
|
return sync_service;
|
|
}
|