Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
290 changes: 86 additions & 204 deletions lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,246 +479,128 @@ std::string PlatformAndroid::GetRunAs() {
return run_as.str();
}

// Helper function to populate process status information from
// /proc/[pid]/status
void PlatformAndroid::PopulateProcessStatusInfo(
lldb::pid_t pid, ProcessInstanceInfo &process_info) {
// Read /proc/[pid]/status to get parent PID, UIDs, and GIDs
Status error;
AdbClientUP status_adb = GetAdbClient(error);
if (error.Fail())
return;

std::string status_output;
StreamString status_cmd;
status_cmd.Printf(
"cat /proc/%llu/status 2>/dev/null | grep -E '^(PPid|Uid|Gid):'",
static_cast<unsigned long long>(pid));
Status status_error =
status_adb->Shell(status_cmd.GetData(), seconds(5), &status_output);
// Check if a process looks like it needs cmdline supplementation.
// Android apps spawned from zygote show as "app_process" or "zygote" initially.
static bool NeedsCmdlineSupplement(const ProcessInstanceInfo &proc_info) {
llvm::StringRef name =
proc_info.GetExecutableFile().GetFilename().GetStringRef();
return name.contains("app_process") || name.contains("zygote");
}

if (status_error.Fail() || status_output.empty())
// 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, Log *log) {
if (proc_infos.empty())
return;

llvm::SmallVector<llvm::StringRef, 16> lines;
llvm::StringRef(status_output).split(lines, '\n');

for (llvm::StringRef line : lines) {
line = line.trim();
if (line.starts_with("PPid:")) {
llvm::StringRef ppid_str = line.substr(5).trim();
lldb::pid_t ppid;
if (llvm::to_integer(ppid_str, ppid))
process_info.SetParentProcessID(ppid);
} else if (line.starts_with("Uid:")) {
llvm::SmallVector<llvm::StringRef, 4> uid_parts;
line.substr(4).trim().split(uid_parts, '\t', -1, false);
if (uid_parts.size() >= 2) {
uint32_t uid, euid;
if (llvm::to_integer(uid_parts[0].trim(), uid))
process_info.SetUserID(uid);
if (llvm::to_integer(uid_parts[1].trim(), euid))
process_info.SetEffectiveUserID(euid);
}
} else if (line.starts_with("Gid:")) {
llvm::SmallVector<llvm::StringRef, 4> gid_parts;
line.substr(4).trim().split(gid_parts, '\t', -1, false);
if (gid_parts.size() >= 2) {
uint32_t gid, egid;
if (llvm::to_integer(gid_parts[0].trim(), gid))
process_info.SetGroupID(gid);
if (llvm::to_integer(gid_parts[1].trim(), egid))
process_info.SetEffectiveGroupID(egid);
}
// Only supplement processes that look like zygote-spawned apps
std::string pid_list;
for (const auto &proc_info : proc_infos) {
if (NeedsCmdlineSupplement(proc_info)) {
if (!pid_list.empty())
pid_list += " ";
pid_list += std::to_string(proc_info.GetProcessID());
}
}
}

// Helper function to populate command line arguments from /proc/[pid]/cmdline
void PlatformAndroid::PopulateProcessCommandLine(
lldb::pid_t pid, ProcessInstanceInfo &process_info) {
// Read /proc/[pid]/cmdline to get command line arguments
Status error;
AdbClientUP cmdline_adb = GetAdbClient(error);
if (error.Fail())
if (pid_list.empty())
return;

std::string cmdline_output;
StreamString cmdline_cmd;
cmdline_cmd.Printf("cat /proc/%llu/cmdline 2>/dev/null | tr '\\000' ' '",
static_cast<unsigned long long>(pid));
Status cmdline_error =
cmdline_adb->Shell(cmdline_cmd.GetData(), seconds(5), &cmdline_output);

if (cmdline_error.Fail() || cmdline_output.empty())
return;
// 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());

cmdline_output = llvm::StringRef(cmdline_output).trim().str();
if (cmdline_output.empty())
return;
std::string cmdline_output;
Status error = adb->Shell(cmd.GetData(), seconds(5), &cmdline_output);

llvm::SmallVector<llvm::StringRef, 16> args;
llvm::StringRef(cmdline_output).split(args, ' ', -1, false);
if (args.empty())
if (error.Fail() || cmdline_output.empty())
return;

process_info.SetArg0(args[0]);
Args process_args;
for (size_t i = 1; i < args.size(); i++) {
if (!args[i].empty())
process_args.AppendArgument(args[i]);
}
process_info.SetArguments(process_args, false);
}
llvm::SmallVector<llvm::StringRef, 256> lines;
llvm::StringRef(cmdline_output).split(lines, '\n', -1, false);

// Helper function to populate architecture from /proc/[pid]/exe
void PlatformAndroid::PopulateProcessArchitecture(
lldb::pid_t pid, ProcessInstanceInfo &process_info) {
// Read /proc/[pid]/exe to get executable path for architecture detection
Status error;
AdbClientUP exe_adb = GetAdbClient(error);
if (error.Fail())
return;
for (llvm::StringRef line : lines) {
line = line.trim();
auto colon_pos = line.find(':');
if (colon_pos == llvm::StringRef::npos)
continue;

std::string exe_output;
StreamString exe_cmd;
exe_cmd.Printf("readlink /proc/%llu/exe 2>/dev/null",
static_cast<unsigned long long>(pid));
Status exe_error = exe_adb->Shell(exe_cmd.GetData(), seconds(5), &exe_output);
llvm::StringRef pid_str = line.substr(0, colon_pos);
llvm::StringRef cmdline = line.substr(colon_pos + 1).trim();

if (exe_error.Fail() || exe_output.empty())
return;
lldb::pid_t pid;
if (!llvm::to_integer(pid_str, pid) || cmdline.empty())
continue;

exe_output = llvm::StringRef(exe_output).trim().str();

// Determine architecture from exe path
ArchSpec arch;
if (exe_output.find("64") != std::string::npos ||
exe_output.find("arm64") != std::string::npos ||
exe_output.find("aarch64") != std::string::npos) {
arch.SetTriple("aarch64-unknown-linux-android");
} else if (exe_output.find("x86_64") != std::string::npos) {
arch.SetTriple("x86_64-unknown-linux-android");
} else if (exe_output.find("x86") != std::string::npos ||
exe_output.find("i686") != std::string::npos) {
arch.SetTriple("i686-unknown-linux-android");
} else {
// Default to armv7 for 32-bit ARM (most common on Android)
arch.SetTriple("armv7-unknown-linux-android");
for (auto &proc_info : proc_infos) {
if (proc_info.GetProcessID() != pid)
continue;

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());
}
break;
}
}

if (arch.IsValid())
process_info.SetArchitecture(arch);
}

uint32_t
PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
ProcessInstanceInfoList &proc_infos) {
proc_infos.clear();

// When LLDB is running natively on an Android device (IsHost() == true),
// use the parent class's standard Linux /proc enumeration. IsHost() is only
// true when compiled for Android (#if defined(__ANDROID__)), so calling
// PlatformLinux methods is safe (Android is Linux-based).
if (IsHost())
return PlatformLinux::FindProcesses(match_info, proc_infos);

// Remote Android platform: implement process name lookup using 'pidof' over
// adb.

// LLDB stores the search name in GetExecutableFile() (even though it's
// actually a process name like "com.android.chrome" rather than an
// executable path). If no search name is provided, we can't use
// 'pidof', so return early with no results.
const ProcessInstanceInfo &match_process_info = match_info.GetProcessInfo();
if (!match_process_info.GetExecutableFile() ||
match_info.GetNameMatchType() == NameMatch::Ignore) {
return 0;
}

// Extract the process name to search for (typically an Android package name
// like "com.example.app" or binary name like "app_process64")
std::string process_name = match_process_info.GetExecutableFile().GetPath();
if (process_name.empty())
return 0;

// Use adb to find the process by name
Status error;
AdbClientUP adb(GetAdbClient(error));
if (error.Fail()) {
Log *log = GetLog(LLDBLog::Platform);
LLDB_LOGF(log, "PlatformAndroid::%s failed to get ADB client: %s",
__FUNCTION__, error.AsCString());
return 0;
}

// Use 'pidof' command to get PIDs for the process name.
// Quote the process name to handle special characters (spaces, etc.)
std::string pidof_output;
StreamString command;
command.Printf("pidof '%s'", process_name.c_str());
error = adb->Shell(command.GetData(), seconds(5), &pidof_output);

if (error.Fail()) {
Log *log = GetLog(LLDBLog::Platform);
LLDB_LOG(log, "PlatformAndroid::{} 'pidof {}' failed: {}", __FUNCTION__,
process_name.c_str(), error.AsCString());
return 0;
}

// Parse PIDs from pidof output.
// Note: pidof can return multiple PIDs (space-separated) if multiple
// instances of the same executable are running.
pidof_output = llvm::StringRef(pidof_output).trim().str();
if (pidof_output.empty()) {
Log *log = GetLog(LLDBLog::Platform);
LLDB_LOGF(log, "PlatformAndroid::%s no process found with name '%s'",
__FUNCTION__, process_name.c_str());
if (!m_remote_platform_sp)
return 0;
}

// Split the output by whitespace to handle multiple PIDs
llvm::SmallVector<llvm::StringRef, 8> pid_strings;
llvm::StringRef(pidof_output).split(pid_strings, ' ', -1, false);

Log *log = GetLog(LLDBLog::Platform);
// For Android, we need to get cmdline info before doing name matching
// because apps run as "zygote" but have package names in cmdline.
ProcessInstanceInfoMatch broad_match_info = match_info;
broad_match_info.SetNameMatchType(NameMatch::Ignore);

// Process each PID and gather information
uint32_t num_matches = 0;
for (llvm::StringRef pid_str : pid_strings) {
pid_str = pid_str.trim();
if (pid_str.empty())
continue;
ProcessInstanceInfoList all_procs;
uint32_t count =
m_remote_platform_sp->FindProcesses(broad_match_info, all_procs);

lldb::pid_t pid;
if (!llvm::to_integer(pid_str, pid)) {
LLDB_LOGF(log, "PlatformAndroid::%s failed to parse PID from: '%s'",
__FUNCTION__, pid_str.str().c_str());
continue;
if (count > 0) {
Status error;
AdbClientUP adb(GetAdbClient(error));
if (error.Success()) {
Log *log = GetLog(LLDBLog::Platform);
SupplementWithCmdlineInfo(all_procs, adb.get(), log);
}

ProcessInstanceInfo process_info;
process_info.SetProcessID(pid);
process_info.GetExecutableFile().SetFile(process_name,
FileSpec::Style::posix);

// Populate additional process information
PopulateProcessStatusInfo(pid, process_info);
PopulateProcessCommandLine(pid, process_info);
PopulateProcessArchitecture(pid, process_info);

// Check if this process matches the criteria
if (match_info.Matches(process_info)) {
proc_infos.push_back(process_info);
num_matches++;

LLDB_LOGF(log, "PlatformAndroid::%s found process '%s' with PID %llu",
__FUNCTION__, process_name.c_str(),
static_cast<unsigned long long>(pid));
// Now 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 num_matches;
return proc_infos.size();
}

std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) {
Expand Down
11 changes: 1 addition & 10 deletions lldb/source/Plugins/Platform/Android/PlatformAndroid.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class PlatformAndroid : public platform_linux::PlatformLinux {
uint32_t GetDefaultMemoryCacheLineSize() override;

uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
ProcessInstanceInfoList &proc_infos) override;
ProcessInstanceInfoList &process_infos) override;

protected:
const char *GetCacheHostname() override;
Expand All @@ -86,17 +86,8 @@ class PlatformAndroid : public platform_linux::PlatformLinux {
protected:
virtual std::unique_ptr<AdbSyncService> GetSyncService(Status &error);

private:
std::string m_device_id;
uint32_t m_sdk_version;

// Helper functions for process information gathering
void PopulateProcessStatusInfo(lldb::pid_t pid,
ProcessInstanceInfo &process_info);
void PopulateProcessCommandLine(lldb::pid_t pid,
ProcessInstanceInfo &process_info);
void PopulateProcessArchitecture(lldb::pid_t pid,
ProcessInstanceInfo &process_info);
};

} // namespace platform_android
Expand Down
Loading