wallace created this revision. wallace added reviewers: clayborg, aadsm, labath, xiaobai. Herald added subscribers: lldb-commits, kristof.beyls, krytarowski, srhines. Herald added a project: LLDB.
By default `platform process list` only shows the processes of the current user that lldb-server can parse. There are several problems: - apk programs don't have an executable file. They instead use a package name as identifier. - each apk also runs under a different user. That's how android works - because of the user permission, some files like /proc/<pid>/{environ,exe} can't be read. This results in a very small process list. This is a local run on my machine (lldb) platform process list 2 matching processes were found on "remote-android" PID PARENT USER TRIPLE NAME ====== ====== ========== ======================== ============================ 23291 3177 aarch64-unknown-linux-android sh 23301 23291 aarch64-unknown-linux-android lldb-server However, I have 700 processes running at this time. By implementing a few fallbacks for android, I've expanded this list to 202, filtering out kernel processes, which would presumably appear in this list if the device was rooted. (lldb) platform process list 202 matching processes were found on "remote-android" PID PARENT USER TRIPLE NAME ====== ====== ========== ======================== ============================ ... 12647 3208 aarch64-unknown-linux-android sh 12649 12647 aarch64-unknown-linux-android lldb-server 12653 982 com.samsung.faceservice 13185 982 com.samsung.vvm 15899 982 com.samsung.android.spay 16220 982 com.sec.spp.push 17126 982 com.sec.spp.push:RemoteDlcProcess 19772 983 com.android.chrome 20209 982 com.samsung.cmh:CMH 20380 982 com.google.android.inputmethod.latin 20879 982 com.samsung.android.oneconnect:Receiver 21212 983 com.tencent.mm 24459 1 aarch64-unknown-linux-android wpa_supplicant 25974 982 com.samsung.android.contacts 26293 982 com.samsung.android.messaging 28714 982 com.samsung.android.dialer 31605 982 com.samsung.android.MtpApplication 32256 982 com.bezobidny Something to notice is that the architecture is unkonwn for all apks. And that's fine, because run-as would be required to gather this information and that would make this entire functionality massively slow. There are still several improvements to make here, like displaying actual user names, which I'll try to do in a following diff. Note: Regarding overall apk debugging support from lldb. I'm planning on having lldb spawn lldb-server by itself with the correct user, so that everything works well. The initial lldb-server used for connecting to the remote platform can be reused for such purpose. Furthermore, eventually lldb could also launch that initial lldb-server on its own. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D68289 Files: lldb/source/Host/linux/Host.cpp lldb/source/Utility/FileSpec.cpp
Index: lldb/source/Utility/FileSpec.cpp =================================================================== --- lldb/source/Utility/FileSpec.cpp +++ lldb/source/Utility/FileSpec.cpp @@ -127,53 +127,55 @@ return true; for (auto i = path.find_first_of("\\/"); i != llvm::StringRef::npos; i = path.find_first_of("\\/", i + 1)) { - const auto next = safeCharAtIndex(path, i+1); + const auto next = safeCharAtIndex(path, i + 1); switch (next) { + case 0: + // path separator char at the end of the string which should be + // stripped unless it is the one and only character + return i > 0; + case '/': + case '\\': + // two path separator chars in the middle of a path needs to be + // normalized + if (i > 0) + return true; + ++i; + break; + + case '.': { + const auto next_next = safeCharAtIndex(path, i + 2); + switch (next_next) { + default: + break; case 0: - // path separator char at the end of the string which should be - // stripped unless it is the one and only character - return i > 0; + return true; // ends with "/." case '/': case '\\': - // two path separator chars in the middle of a path needs to be - // normalized - if (i > 0) - return true; - ++i; - break; - + return true; // contains "/./" case '.': { - const auto next_next = safeCharAtIndex(path, i+2); - switch (next_next) { - default: break; - case 0: return true; // ends with "/." - case '/': - case '\\': - return true; // contains "/./" - case '.': { - const auto next_next_next = safeCharAtIndex(path, i+3); - switch (next_next_next) { - default: break; - case 0: return true; // ends with "/.." - case '/': - case '\\': - return true; // contains "/../" - } - break; - } - } + const auto next_next_next = safeCharAtIndex(path, i + 3); + switch (next_next_next) { + default: + break; + case 0: + return true; // ends with "/.." + case '/': + case '\\': + return true; // contains "/../" } break; + } + } + } break; - default: - break; + default: + break; } } return false; } - -} +} // namespace // Assignment operator. const FileSpec &FileSpec::operator=(const FileSpec &rhs) { if (this != &rhs) { @@ -220,11 +222,11 @@ // Split path into filename and directory. We rely on the underlying char // pointer to be nullptr when the components are empty. llvm::StringRef filename = llvm::sys::path::filename(resolved, m_style); - if(!filename.empty()) + if (!filename.empty()) m_filename.SetString(filename); llvm::StringRef directory = llvm::sys::path::parent_path(resolved, m_style); - if(!directory.empty()) + if (!directory.empty()) m_directory.SetString(directory); } @@ -315,9 +317,8 @@ // case sensitivity of equality test const bool case_sensitive = a.IsCaseSensitive() || b.IsCaseSensitive(); - const bool filenames_equal = ConstString::Equals(a.m_filename, - b.m_filename, - case_sensitive); + const bool filenames_equal = + ConstString::Equals(a.m_filename, b.m_filename, case_sensitive); if (!filenames_equal) return false; @@ -328,7 +329,8 @@ return a == b; } -llvm::Optional<FileSpec::Style> FileSpec::GuessPathStyle(llvm::StringRef absolute_path) { +llvm::Optional<FileSpec::Style> +FileSpec::GuessPathStyle(llvm::StringRef absolute_path) { if (absolute_path.startswith("/")) return Style::posix; if (absolute_path.startswith(R"(\\)")) @@ -495,9 +497,7 @@ return g_source_file_regex.Execute(extension.GetStringRef()); } -bool FileSpec::IsRelative() const { - return !IsAbsolute(); -} +bool FileSpec::IsRelative() const { return !IsAbsolute(); } bool FileSpec::IsAbsolute() const { llvm::SmallString<64> current_path; Index: lldb/source/Host/linux/Host.cpp =================================================================== --- lldb/source/Host/linux/Host.cpp +++ lldb/source/Host/linux/Host.cpp @@ -55,7 +55,7 @@ return false; llvm::StringRef Rest = BufferOrError.get()->getBuffer(); - while(!Rest.empty()) { + while (!Rest.empty()) { llvm::StringRef Line; std::tie(Line, Rest) = Rest.split('\n'); @@ -144,68 +144,102 @@ } } -static bool GetProcessAndStatInfo(::pid_t pid, - ProcessInstanceInfo &process_info, - ProcessState &State, ::pid_t &tracerpid) { - tracerpid = 0; - process_info.Clear(); +static bool GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) { + auto BufferOrError = getProcFile(pid, "cmdline"); + if (!BufferOrError) + return false; + std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError); + + llvm::StringRef Arg0, Rest; + std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0'); + process_info.SetArg0(Arg0); + while (!Rest.empty()) { + llvm::StringRef Arg; + std::tie(Arg, Rest) = Rest.split('\0'); + process_info.GetArguments().AppendArgument(Arg); + } + return true; +} +static bool GetExePathArchAndProcessArgs(::pid_t pid, + ProcessInstanceInfo &process_info) { + GetProcessArgs(pid, process_info); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + std::string ExePath(PATH_MAX, '\0'); // We can't use getProcFile here because proc/[pid]/exe is a symbolic link. llvm::SmallString<64> ProcExe; (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe); - std::string ExePath(PATH_MAX, '\0'); ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX); - if (len <= 0) { + if (len > 0) { + ExePath.resize(len); + } else { LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid, Status(errno, eErrorTypePOSIX)); - return false; + ExePath.resize(0); +#if defined(__ANDROID__) + // On android we fallback to Arg0, which commonly is an apk package name. + if (!process_info.GetArg0().empty()) { + ExePath = process_info.GetArg0(); + } +#endif } - ExePath.resize(len); - + if (ExePath.empty()) + return false; // If the binary has been deleted, the link name has " (deleted)" appended. // Remove if there. - llvm::StringRef PathRef = ExePath; + llvm::StringRef PathRef(ExePath); PathRef.consume_back(" (deleted)"); + process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native); process_info.SetArchitecture(GetELFProcessCPUType(PathRef)); + return true; +} +static bool GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) { // Get the process environment. auto BufferOrError = getProcFile(pid, "environ"); - if (!BufferOrError) + if (BufferOrError) { + std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError); + llvm::StringRef Rest = Environ->getBuffer(); + while (!Rest.empty()) { + llvm::StringRef Var; + std::tie(Var, Rest) = Rest.split('\0'); + process_info.GetEnvironment().insert(Var); + } + return true; + } +#if defined(__ANDROID__) + // It's okay if we can't get the environment on android + return true; +#else + return false; +#endif +} + +static bool GetProcessAndStatInfo(::pid_t pid, + ProcessInstanceInfo &process_info, + ProcessState &State, ::pid_t &tracerpid) { + tracerpid = 0; + process_info.Clear(); + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + process_info.SetProcessID(pid); + + if (!GetExePathArchAndProcessArgs(pid, process_info)) return false; - std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError); + printf("%d: passed 1\n", pid); - // Get the command line used to start the process. - BufferOrError = getProcFile(pid, "cmdline"); - if (!BufferOrError) + if (!GetProcessEnviron(pid, process_info)) return false; - std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError); + printf("%d: passed 2\n", pid); // Get User and Group IDs and get tracer pid. if (!GetStatusInfo(pid, process_info, State, tracerpid)) return false; - - process_info.SetProcessID(pid); - process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native); - - llvm::StringRef Rest = Environ->getBuffer(); - while (!Rest.empty()) { - llvm::StringRef Var; - std::tie(Var, Rest) = Rest.split('\0'); - process_info.GetEnvironment().insert(Var); - } - - llvm::StringRef Arg0; - std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0'); - process_info.SetArg0(Arg0); - while (!Rest.empty()) { - llvm::StringRef Arg; - std::tie(Arg, Rest) = Rest.split('\0'); - process_info.GetArguments().AppendArgument(Arg); - } + printf("%d: passed 3\n", pid); return true; } @@ -219,7 +253,14 @@ struct dirent *direntry = nullptr; const uid_t our_uid = getuid(); const lldb::pid_t our_pid = getpid(); - bool all_users = match_info.GetMatchAllUsers(); + bool all_users = +#if defined(__ANDROID__) + // On android each apk has its own user, so it's better to display all + // users instead of the current one. + true; +#else + match_info.GetMatchAllUsers(); +#endif while ((direntry = readdir(dirproc)) != nullptr) { if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name)) @@ -237,6 +278,7 @@ if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid)) continue; + printf("%d: passed main function\n", pid); // Skip if process is being debugged. if (tracerpid != 0)
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits