|
15 | 15 | #include "lldb/Utility/UriParser.h" |
16 | 16 | #include "lldb/ValueObject/ValueObject.h" |
17 | 17 |
|
| 18 | +#include "llvm/ADT/DenseMap.h" |
| 19 | + |
18 | 20 | #include "AdbClient.h" |
19 | 21 | #include "PlatformAndroid.h" |
20 | 22 | #include "PlatformAndroidRemoteGDBServer.h" |
@@ -479,246 +481,130 @@ std::string PlatformAndroid::GetRunAs() { |
479 | 481 | return run_as.str(); |
480 | 482 | } |
481 | 483 |
|
482 | | -// Helper function to populate process status information from |
483 | | -// /proc/[pid]/status |
484 | | -void PlatformAndroid::PopulateProcessStatusInfo( |
485 | | - lldb::pid_t pid, ProcessInstanceInfo &process_info) { |
486 | | - // Read /proc/[pid]/status to get parent PID, UIDs, and GIDs |
487 | | - Status error; |
488 | | - AdbClientUP status_adb = GetAdbClient(error); |
489 | | - if (error.Fail()) |
490 | | - return; |
491 | | - |
492 | | - std::string status_output; |
493 | | - StreamString status_cmd; |
494 | | - status_cmd.Printf( |
495 | | - "cat /proc/%llu/status 2>/dev/null | grep -E '^(PPid|Uid|Gid):'", |
496 | | - static_cast<unsigned long long>(pid)); |
497 | | - Status status_error = |
498 | | - status_adb->Shell(status_cmd.GetData(), seconds(5), &status_output); |
| 484 | +static bool NeedsCmdlineSupplement(const ProcessInstanceInfo &proc_info) { |
| 485 | + llvm::StringRef name = |
| 486 | + proc_info.GetExecutableFile().GetFilename().GetStringRef(); |
| 487 | + return name.contains("app_process") || name.contains("zygote"); |
| 488 | +} |
499 | 489 |
|
500 | | - if (status_error.Fail() || status_output.empty()) |
| 490 | +// Fetch /proc/PID/cmdline for processes to get actual package names. |
| 491 | +// Android apps often show as "zygote" or "app_process" without this. |
| 492 | +static void SupplementWithCmdlineInfo(ProcessInstanceInfoList &proc_infos, |
| 493 | + AdbClient *adb) { |
| 494 | + if (proc_infos.empty()) |
501 | 495 | return; |
502 | 496 |
|
503 | | - llvm::SmallVector<llvm::StringRef, 16> lines; |
504 | | - llvm::StringRef(status_output).split(lines, '\n'); |
505 | | - |
506 | | - for (llvm::StringRef line : lines) { |
507 | | - line = line.trim(); |
508 | | - if (line.starts_with("PPid:")) { |
509 | | - llvm::StringRef ppid_str = line.substr(5).trim(); |
510 | | - lldb::pid_t ppid; |
511 | | - if (llvm::to_integer(ppid_str, ppid)) |
512 | | - process_info.SetParentProcessID(ppid); |
513 | | - } else if (line.starts_with("Uid:")) { |
514 | | - llvm::SmallVector<llvm::StringRef, 4> uid_parts; |
515 | | - line.substr(4).trim().split(uid_parts, '\t', -1, false); |
516 | | - if (uid_parts.size() >= 2) { |
517 | | - uint32_t uid, euid; |
518 | | - if (llvm::to_integer(uid_parts[0].trim(), uid)) |
519 | | - process_info.SetUserID(uid); |
520 | | - if (llvm::to_integer(uid_parts[1].trim(), euid)) |
521 | | - process_info.SetEffectiveUserID(euid); |
522 | | - } |
523 | | - } else if (line.starts_with("Gid:")) { |
524 | | - llvm::SmallVector<llvm::StringRef, 4> gid_parts; |
525 | | - line.substr(4).trim().split(gid_parts, '\t', -1, false); |
526 | | - if (gid_parts.size() >= 2) { |
527 | | - uint32_t gid, egid; |
528 | | - if (llvm::to_integer(gid_parts[0].trim(), gid)) |
529 | | - process_info.SetGroupID(gid); |
530 | | - if (llvm::to_integer(gid_parts[1].trim(), egid)) |
531 | | - process_info.SetEffectiveGroupID(egid); |
532 | | - } |
| 497 | + llvm::DenseMap<lldb::pid_t, ProcessInstanceInfo *> pid_map; |
| 498 | + std::string pid_list; |
| 499 | + for (auto &proc_info : proc_infos) { |
| 500 | + if (NeedsCmdlineSupplement(proc_info)) { |
| 501 | + lldb::pid_t pid = proc_info.GetProcessID(); |
| 502 | + pid_map[pid] = &proc_info; |
| 503 | + if (!pid_list.empty()) |
| 504 | + pid_list += " "; |
| 505 | + pid_list += std::to_string(pid); |
533 | 506 | } |
534 | 507 | } |
535 | | -} |
536 | 508 |
|
537 | | -// Helper function to populate command line arguments from /proc/[pid]/cmdline |
538 | | -void PlatformAndroid::PopulateProcessCommandLine( |
539 | | - lldb::pid_t pid, ProcessInstanceInfo &process_info) { |
540 | | - // Read /proc/[pid]/cmdline to get command line arguments |
541 | | - Status error; |
542 | | - AdbClientUP cmdline_adb = GetAdbClient(error); |
543 | | - if (error.Fail()) |
| 509 | + if (pid_list.empty()) |
544 | 510 | return; |
545 | 511 |
|
| 512 | + Log *log = GetLog(LLDBLog::Platform); |
| 513 | + |
| 514 | + // Use xargs -P to parallelize cmdline fetching (up to 8 concurrent reads) |
| 515 | + StreamString cmd; |
| 516 | + cmd.Printf( |
| 517 | + "echo '%s' | xargs -n 1 -P 8 sh -c " |
| 518 | + "'echo \"$1:$(cat /proc/$1/cmdline 2>/dev/null | tr \"\\0\" \" \")\"' sh", |
| 519 | + pid_list.c_str()); |
| 520 | + |
546 | 521 | std::string cmdline_output; |
547 | | - StreamString cmdline_cmd; |
548 | | - cmdline_cmd.Printf("cat /proc/%llu/cmdline 2>/dev/null | tr '\\000' ' '", |
549 | | - static_cast<unsigned long long>(pid)); |
550 | | - Status cmdline_error = |
551 | | - cmdline_adb->Shell(cmdline_cmd.GetData(), seconds(5), &cmdline_output); |
| 522 | + Status error = adb->Shell(cmd.GetData(), seconds(5), &cmdline_output); |
552 | 523 |
|
553 | | - if (cmdline_error.Fail() || cmdline_output.empty()) |
| 524 | + if (error.Fail() || cmdline_output.empty()) |
554 | 525 | return; |
555 | 526 |
|
556 | | - cmdline_output = llvm::StringRef(cmdline_output).trim().str(); |
557 | | - if (cmdline_output.empty()) |
558 | | - return; |
| 527 | + llvm::SmallVector<llvm::StringRef, 256> lines; |
| 528 | + llvm::StringRef(cmdline_output).split(lines, '\n', -1, false); |
559 | 529 |
|
560 | | - llvm::SmallVector<llvm::StringRef, 16> args; |
561 | | - llvm::StringRef(cmdline_output).split(args, ' ', -1, false); |
562 | | - if (args.empty()) |
563 | | - return; |
| 530 | + for (llvm::StringRef line : lines) { |
| 531 | + line = line.trim(); |
| 532 | + auto [pid_str, cmdline] = line.split(':'); |
| 533 | + if (pid_str.empty() || cmdline.empty()) |
| 534 | + continue; |
564 | 535 |
|
565 | | - process_info.SetArg0(args[0]); |
566 | | - Args process_args; |
567 | | - for (size_t i = 1; i < args.size(); i++) { |
568 | | - if (!args[i].empty()) |
569 | | - process_args.AppendArgument(args[i]); |
570 | | - } |
571 | | - process_info.SetArguments(process_args, false); |
572 | | -} |
| 536 | + cmdline = cmdline.trim(); |
573 | 537 |
|
574 | | -// Helper function to populate architecture from /proc/[pid]/exe |
575 | | -void PlatformAndroid::PopulateProcessArchitecture( |
576 | | - lldb::pid_t pid, ProcessInstanceInfo &process_info) { |
577 | | - // Read /proc/[pid]/exe to get executable path for architecture detection |
578 | | - Status error; |
579 | | - AdbClientUP exe_adb = GetAdbClient(error); |
580 | | - if (error.Fail()) |
581 | | - return; |
| 538 | + lldb::pid_t pid; |
| 539 | + if (!llvm::to_integer(pid_str, pid) || cmdline.empty()) |
| 540 | + continue; |
582 | 541 |
|
583 | | - std::string exe_output; |
584 | | - StreamString exe_cmd; |
585 | | - exe_cmd.Printf("readlink /proc/%llu/exe 2>/dev/null", |
586 | | - static_cast<unsigned long long>(pid)); |
587 | | - Status exe_error = exe_adb->Shell(exe_cmd.GetData(), seconds(5), &exe_output); |
| 542 | + auto it = pid_map.find(pid); |
| 543 | + if (it == pid_map.end()) |
| 544 | + continue; |
588 | 545 |
|
589 | | - if (exe_error.Fail() || exe_output.empty()) |
590 | | - return; |
| 546 | + ProcessInstanceInfo *proc_info = it->second; |
| 547 | + llvm::SmallVector<llvm::StringRef, 16> args; |
| 548 | + cmdline.split(args, ' ', -1, false); |
591 | 549 |
|
592 | | - exe_output = llvm::StringRef(exe_output).trim().str(); |
593 | | - |
594 | | - // Determine architecture from exe path |
595 | | - ArchSpec arch; |
596 | | - if (exe_output.find("64") != std::string::npos || |
597 | | - exe_output.find("arm64") != std::string::npos || |
598 | | - exe_output.find("aarch64") != std::string::npos) { |
599 | | - arch.SetTriple("aarch64-unknown-linux-android"); |
600 | | - } else if (exe_output.find("x86_64") != std::string::npos) { |
601 | | - arch.SetTriple("x86_64-unknown-linux-android"); |
602 | | - } else if (exe_output.find("x86") != std::string::npos || |
603 | | - exe_output.find("i686") != std::string::npos) { |
604 | | - arch.SetTriple("i686-unknown-linux-android"); |
605 | | - } else { |
606 | | - // Default to armv7 for 32-bit ARM (most common on Android) |
607 | | - arch.SetTriple("armv7-unknown-linux-android"); |
608 | | - } |
| 550 | + if (!args.empty()) { |
| 551 | + proc_info->GetExecutableFile().SetFile(args[0], FileSpec::Style::posix); |
| 552 | + |
| 553 | + if (args.size() > 1) { |
| 554 | + Args process_args; |
| 555 | + for (size_t i = 1; i < args.size(); ++i) { |
| 556 | + if (!args[i].empty()) |
| 557 | + process_args.AppendArgument(args[i]); |
| 558 | + } |
| 559 | + proc_info->SetArguments(process_args, false); |
| 560 | + } |
609 | 561 |
|
610 | | - if (arch.IsValid()) |
611 | | - process_info.SetArchitecture(arch); |
| 562 | + LLDB_LOGF(log, |
| 563 | + "PlatformAndroid::%s supplemented PID %llu with cmdline: %s", |
| 564 | + __FUNCTION__, static_cast<unsigned long long>(pid), |
| 565 | + cmdline.str().c_str()); |
| 566 | + } |
| 567 | + } |
612 | 568 | } |
613 | 569 |
|
614 | 570 | uint32_t |
615 | 571 | PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info, |
616 | 572 | ProcessInstanceInfoList &proc_infos) { |
617 | 573 | proc_infos.clear(); |
618 | 574 |
|
619 | | - // When LLDB is running natively on an Android device (IsHost() == true), |
620 | | - // use the parent class's standard Linux /proc enumeration. IsHost() is only |
621 | | - // true when compiled for Android (#if defined(__ANDROID__)), so calling |
622 | | - // PlatformLinux methods is safe (Android is Linux-based). |
623 | 575 | if (IsHost()) |
624 | 576 | return PlatformLinux::FindProcesses(match_info, proc_infos); |
625 | 577 |
|
626 | | - // Remote Android platform: implement process name lookup using 'pidof' over |
627 | | - // adb. |
628 | | - |
629 | | - // LLDB stores the search name in GetExecutableFile() (even though it's |
630 | | - // actually a process name like "com.android.chrome" rather than an |
631 | | - // executable path). If no search name is provided, we can't use |
632 | | - // 'pidof', so return early with no results. |
633 | | - const ProcessInstanceInfo &match_process_info = match_info.GetProcessInfo(); |
634 | | - if (!match_process_info.GetExecutableFile() || |
635 | | - match_info.GetNameMatchType() == NameMatch::Ignore) { |
636 | | - return 0; |
637 | | - } |
638 | | - |
639 | | - // Extract the process name to search for (typically an Android package name |
640 | | - // like "com.example.app" or binary name like "app_process64") |
641 | | - std::string process_name = match_process_info.GetExecutableFile().GetPath(); |
642 | | - if (process_name.empty()) |
643 | | - return 0; |
644 | | - |
645 | | - // Use adb to find the process by name |
646 | | - Status error; |
647 | | - AdbClientUP adb(GetAdbClient(error)); |
648 | | - if (error.Fail()) { |
649 | | - Log *log = GetLog(LLDBLog::Platform); |
650 | | - LLDB_LOGF(log, "PlatformAndroid::%s failed to get ADB client: %s", |
651 | | - __FUNCTION__, error.AsCString()); |
652 | | - return 0; |
653 | | - } |
654 | | - |
655 | | - // Use 'pidof' command to get PIDs for the process name. |
656 | | - // Quote the process name to handle special characters (spaces, etc.) |
657 | | - std::string pidof_output; |
658 | | - StreamString command; |
659 | | - command.Printf("pidof '%s'", process_name.c_str()); |
660 | | - error = adb->Shell(command.GetData(), seconds(5), &pidof_output); |
661 | | - |
662 | | - if (error.Fail()) { |
663 | | - Log *log = GetLog(LLDBLog::Platform); |
664 | | - LLDB_LOG(log, "PlatformAndroid::{} 'pidof {}' failed: {}", __FUNCTION__, |
665 | | - process_name.c_str(), error.AsCString()); |
666 | | - return 0; |
667 | | - } |
668 | | - |
669 | | - // Parse PIDs from pidof output. |
670 | | - // Note: pidof can return multiple PIDs (space-separated) if multiple |
671 | | - // instances of the same executable are running. |
672 | | - pidof_output = llvm::StringRef(pidof_output).trim().str(); |
673 | | - if (pidof_output.empty()) { |
674 | | - Log *log = GetLog(LLDBLog::Platform); |
675 | | - LLDB_LOGF(log, "PlatformAndroid::%s no process found with name '%s'", |
676 | | - __FUNCTION__, process_name.c_str()); |
| 578 | + if (!m_remote_platform_sp) |
677 | 579 | return 0; |
678 | | - } |
679 | | - |
680 | | - // Split the output by whitespace to handle multiple PIDs |
681 | | - llvm::SmallVector<llvm::StringRef, 8> pid_strings; |
682 | | - llvm::StringRef(pidof_output).split(pid_strings, ' ', -1, false); |
683 | | - |
684 | | - Log *log = GetLog(LLDBLog::Platform); |
685 | | - |
686 | | - // Process each PID and gather information |
687 | | - uint32_t num_matches = 0; |
688 | | - for (llvm::StringRef pid_str : pid_strings) { |
689 | | - pid_str = pid_str.trim(); |
690 | | - if (pid_str.empty()) |
691 | | - continue; |
692 | | - |
693 | | - lldb::pid_t pid; |
694 | | - if (!llvm::to_integer(pid_str, pid)) { |
695 | | - LLDB_LOGF(log, "PlatformAndroid::%s failed to parse PID from: '%s'", |
696 | | - __FUNCTION__, pid_str.str().c_str()); |
697 | | - continue; |
698 | | - } |
699 | | - |
700 | | - ProcessInstanceInfo process_info; |
701 | | - process_info.SetProcessID(pid); |
702 | | - process_info.GetExecutableFile().SetFile(process_name, |
703 | | - FileSpec::Style::posix); |
704 | | - |
705 | | - // Populate additional process information |
706 | | - PopulateProcessStatusInfo(pid, process_info); |
707 | | - PopulateProcessCommandLine(pid, process_info); |
708 | | - PopulateProcessArchitecture(pid, process_info); |
709 | | - |
710 | | - // Check if this process matches the criteria |
711 | | - if (match_info.Matches(process_info)) { |
712 | | - proc_infos.push_back(process_info); |
713 | | - num_matches++; |
714 | 580 |
|
715 | | - LLDB_LOGF(log, "PlatformAndroid::%s found process '%s' with PID %llu", |
716 | | - __FUNCTION__, process_name.c_str(), |
717 | | - static_cast<unsigned long long>(pid)); |
| 581 | + // Android-specific process name handling: |
| 582 | + // Apps spawned from zygote initially appear as "app_process" or "zygote" |
| 583 | + // in the process list, but their actual package names (e.g., |
| 584 | + // "com.example.app") are only available in /proc/PID/cmdline. To support |
| 585 | + // name-based matching, we must first fetch cmdline info for all processes, |
| 586 | + // then apply the original name filter. |
| 587 | + ProcessInstanceInfoMatch broad_match_info = match_info; |
| 588 | + broad_match_info.SetNameMatchType(NameMatch::Ignore); |
| 589 | + |
| 590 | + ProcessInstanceInfoList all_procs; |
| 591 | + uint32_t count = |
| 592 | + m_remote_platform_sp->FindProcesses(broad_match_info, all_procs); |
| 593 | + |
| 594 | + if (count > 0) { |
| 595 | + Status error; |
| 596 | + AdbClientUP adb(GetAdbClient(error)); |
| 597 | + if (error.Success()) |
| 598 | + SupplementWithCmdlineInfo(all_procs, adb.get()); |
| 599 | + |
| 600 | + // Apply the original name matching against supplemented process info. |
| 601 | + for (auto &proc_info : all_procs) { |
| 602 | + if (match_info.Matches(proc_info)) |
| 603 | + proc_infos.push_back(proc_info); |
718 | 604 | } |
719 | 605 | } |
720 | 606 |
|
721 | | - return num_matches; |
| 607 | + return proc_infos.size(); |
722 | 608 | } |
723 | 609 |
|
724 | 610 | std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) { |
|
0 commit comments