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

Reply via email to