Skip to content
Merged
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
243 changes: 243 additions & 0 deletions lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,249 @@ 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);

if (status_error.Fail() || status_output.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);
}
}
}
}

// 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())
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;

cmdline_output = llvm::StringRef(cmdline_output).trim().str();
if (cmdline_output.empty())
return;

llvm::SmallVector<llvm::StringRef, 16> args;
llvm::StringRef(cmdline_output).split(args, ' ', -1, false);
if (args.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);
}

// 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;

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);

if (exe_error.Fail() || exe_output.empty())
return;

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");
}

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

// 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;

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;
}

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

return num_matches;
}

std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) {
auto sync_service = std::make_unique<AdbSyncService>(m_device_id);
error = sync_service->SetupSyncConnection();
Expand Down
11 changes: 11 additions & 0 deletions lldb/source/Plugins/Platform/Android/PlatformAndroid.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class PlatformAndroid : public platform_linux::PlatformLinux {

uint32_t GetDefaultMemoryCacheLineSize() override;

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

protected:
const char *GetCacheHostname() override;

Expand Down Expand Up @@ -86,6 +89,14 @@ class PlatformAndroid : public platform_linux::PlatformLinux {
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