diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp index 57d88f615e2b3..14e07877aee2a 100644 --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -479,136 +479,90 @@ 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(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 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 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 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(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 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 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(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 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(pid), + cmdline.str().c_str()); + } + break; + } } - - if (arch.IsValid()) - process_info.SetArchitecture(arch); } uint32_t @@ -616,109 +570,37 @@ 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 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(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 PlatformAndroid::GetSyncService(Status &error) { diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h index e771c6ae97d4d..c6a412b39d410 100644 --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h @@ -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; @@ -86,17 +86,8 @@ class PlatformAndroid : public platform_linux::PlatformLinux { protected: virtual std::unique_ptr 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