[Lldb-commits] [lldb] 2451cbf - [lldb/Reproducers] Intercept the FindProcesses API

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Fri Mar 13 09:31:53 PDT 2020


Author: Jonas Devlieghere
Date: 2020-03-13T09:31:35-07:00
New Revision: 2451cbf07bbc500718c30a9e9447385f7235707b

URL: https://github.com/llvm/llvm-project/commit/2451cbf07bbc500718c30a9e9447385f7235707b
DIFF: https://github.com/llvm/llvm-project/commit/2451cbf07bbc500718c30a9e9447385f7235707b.diff

LOG: [lldb/Reproducers] Intercept the FindProcesses API

This patch extends the reproducers to intercept calls to FindProcesses.
During capture it serializes the ProcessInstanceInfoList returned by the
API. During replay, it returns the serialized data instead of querying
the host.

The motivation for this patch is supporting the process attach workflow
during replay. Without this change it would incorrectly look for the
inferior on the host during replay and failing if no matching process
was found.

Differential revision: https://reviews.llvm.org/D75877

Added: 
    lldb/test/API/functionalities/reproducers/attach/Makefile
    lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
    lldb/test/API/functionalities/reproducers/attach/main.cpp

Modified: 
    lldb/include/lldb/Host/Host.h
    lldb/include/lldb/Utility/ProcessInfo.h
    lldb/source/Commands/CommandObjectReproducer.cpp
    lldb/source/Host/common/Host.cpp
    lldb/source/Host/linux/Host.cpp
    lldb/source/Host/macosx/objcxx/Host.mm
    lldb/source/Host/netbsd/Host.cpp
    lldb/source/Host/openbsd/Host.cpp
    lldb/source/Utility/ProcessInfo.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h
index 269bb18571b0..f19cb85d2329 100644
--- a/lldb/include/lldb/Host/Host.h
+++ b/lldb/include/lldb/Host/Host.h
@@ -232,6 +232,10 @@ class Host {
 
   static std::unique_ptr<Connection>
   CreateDefaultConnection(llvm::StringRef url);
+
+protected:
+  static uint32_t FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+                                    ProcessInstanceInfoList &proc_infos);
 };
 
 } // namespace lldb_private

diff  --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h
index 0d631d06d2df..ec91060cda54 100644
--- a/lldb/include/lldb/Utility/ProcessInfo.h
+++ b/lldb/include/lldb/Utility/ProcessInfo.h
@@ -14,6 +14,7 @@
 #include "lldb/Utility/Environment.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/Reproducer.h"
 #include "llvm/Support/YAMLTraits.h"
 #include <vector>
 
@@ -215,6 +216,42 @@ class ProcessInstanceInfoMatch {
   bool m_match_all_users;
 };
 
+namespace repro {
+class ProcessInfoRecorder : public AbstractRecorder {
+public:
+  ProcessInfoRecorder(const FileSpec &filename, std::error_code &ec)
+      : AbstractRecorder(filename, ec) {}
+
+  static llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
+  Create(const FileSpec &filename);
+
+  void Record(const ProcessInstanceInfoList &process_infos);
+};
+
+class ProcessInfoProvider : public repro::Provider<ProcessInfoProvider> {
+public:
+  struct Info {
+    static const char *name;
+    static const char *file;
+  };
+
+  ProcessInfoProvider(const FileSpec &directory) : Provider(directory) {}
+
+  ProcessInfoRecorder *GetNewProcessInfoRecorder();
+
+  void Keep() override;
+  void Discard() override;
+
+  static char ID;
+
+private:
+  std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
+  std::vector<std::unique_ptr<ProcessInfoRecorder>> m_process_info_recorders;
+};
+
+llvm::Optional<ProcessInstanceInfoList> GetReplayProcessInstanceInfoList();
+
+} // namespace repro
 } // namespace lldb_private
 
 LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::ProcessInstanceInfo)

diff  --git a/lldb/source/Commands/CommandObjectReproducer.cpp b/lldb/source/Commands/CommandObjectReproducer.cpp
index d73fa78231a5..104130b70b2b 100644
--- a/lldb/source/Commands/CommandObjectReproducer.cpp
+++ b/lldb/source/Commands/CommandObjectReproducer.cpp
@@ -8,13 +8,14 @@
 
 #include "CommandObjectReproducer.h"
 
+#include "lldb/Host/HostInfo.h"
 #include "lldb/Host/OptionParser.h"
-#include "lldb/Utility/GDBRemote.h"
-#include "lldb/Utility/Reproducer.h"
-
 #include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Utility/GDBRemote.h"
+#include "lldb/Utility/ProcessInfo.h"
+#include "lldb/Utility/Reproducer.h"
 
 #include <csignal>
 
@@ -27,6 +28,7 @@ enum ReproducerProvider {
   eReproducerProviderCommands,
   eReproducerProviderFiles,
   eReproducerProviderGDB,
+  eReproducerProviderProcessInfo,
   eReproducerProviderVersion,
   eReproducerProviderWorkingDirectory,
   eReproducerProviderNone
@@ -48,6 +50,11 @@ static constexpr OptionEnumValueElement g_reproducer_provider_type[] = {
         "gdb",
         "GDB Remote Packets",
     },
+    {
+        eReproducerProviderProcessInfo,
+        "processes",
+        "Process Info",
+    },
     {
         eReproducerProviderVersion,
         "version",
@@ -97,6 +104,24 @@ static constexpr OptionEnumValues ReproducerSignalType() {
 #define LLDB_OPTIONS_reproducer_xcrash
 #include "CommandOptions.inc"
 
+template <typename T>
+llvm::Expected<T> static ReadFromYAML(StringRef filename) {
+  auto error_or_file = MemoryBuffer::getFile(filename);
+  if (auto err = error_or_file.getError()) {
+    return errorCodeToError(err);
+  }
+
+  T t;
+  yaml::Input yin((*error_or_file)->getBuffer());
+  yin >> t;
+
+  if (auto err = yin.error()) {
+    return errorCodeToError(err);
+  }
+
+  return t;
+}
+
 class CommandObjectReproducerGenerate : public CommandObjectParsed {
 public:
   CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
@@ -458,23 +483,41 @@ class CommandObjectReproducerDump : public CommandObjectParsed {
 
       llvm::Optional<std::string> gdb_file;
       while ((gdb_file = multi_loader->GetNextFile())) {
-        auto error_or_file = MemoryBuffer::getFile(*gdb_file);
-        if (auto err = error_or_file.getError()) {
-          SetError(result, errorCodeToError(err));
+        if (llvm::Expected<std::vector<GDBRemotePacket>> packets =
+                ReadFromYAML<std::vector<GDBRemotePacket>>(*gdb_file)) {
+          for (GDBRemotePacket &packet : *packets) {
+            packet.Dump(result.GetOutputStream());
+          }
+        } else {
+          SetError(result, packets.takeError());
           return false;
         }
+      }
 
-        std::vector<GDBRemotePacket> packets;
-        yaml::Input yin((*error_or_file)->getBuffer());
-        yin >> packets;
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+      return true;
+    }
+    case eReproducerProviderProcessInfo: {
+      std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
+          multi_loader =
+              repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
 
-        if (auto err = yin.error()) {
-          SetError(result, errorCodeToError(err));
-          return false;
-        }
+      if (!multi_loader) {
+        SetError(result, make_error<StringError>(
+                             llvm::inconvertibleErrorCode(),
+                             "Unable to create process info loader."));
+        return false;
+      }
 
-        for (GDBRemotePacket &packet : packets) {
-          packet.Dump(result.GetOutputStream());
+      llvm::Optional<std::string> process_file;
+      while ((process_file = multi_loader->GetNextFile())) {
+        if (llvm::Expected<ProcessInstanceInfoList> infos =
+                ReadFromYAML<ProcessInstanceInfoList>(*process_file)) {
+          for (ProcessInstanceInfo info : *infos)
+            info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
+        } else {
+          SetError(result, infos.takeError());
+          return false;
         }
       }
 

diff  --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp
index 63963cf9fad4..b2485393cd6a 100644
--- a/lldb/source/Host/common/Host.cpp
+++ b/lldb/source/Host/common/Host.cpp
@@ -678,3 +678,23 @@ void llvm::format_provider<WaitStatus>::format(const WaitStatus &WS,
   }
   OS << desc << " " << int(WS.status);
 }
+
+uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+                             ProcessInstanceInfoList &process_infos) {
+
+  if (llvm::Optional<ProcessInstanceInfoList> infos =
+          repro::GetReplayProcessInstanceInfoList()) {
+    process_infos = *infos;
+    return process_infos.size();
+  }
+
+  uint32_t result = FindProcessesImpl(match_info, process_infos);
+
+  if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+    g->GetOrCreate<repro::ProcessInfoProvider>()
+        .GetNewProcessInfoRecorder()
+        ->Record(process_infos);
+  }
+
+  return result;
+}

diff  --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp
index 3e002ca2f77a..45973f5d214b 100644
--- a/lldb/source/Host/linux/Host.cpp
+++ b/lldb/source/Host/linux/Host.cpp
@@ -221,8 +221,8 @@ static bool GetProcessAndStatInfo(::pid_t pid,
   return true;
 }
 
-uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
-                             ProcessInstanceInfoList &process_infos) {
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+                                 ProcessInstanceInfoList &process_infos) {
   static const char procdir[] = "/proc/";
 
   DIR *dirproc = opendir(procdir);

diff  --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm
index 0e668713d4bd..2475338a37fd 100644
--- a/lldb/source/Host/macosx/objcxx/Host.mm
+++ b/lldb/source/Host/macosx/objcxx/Host.mm
@@ -591,8 +591,8 @@ static bool GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) {
   return false;
 }
 
-uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
-                             ProcessInstanceInfoList &process_infos) {
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+                                 ProcessInstanceInfoList &process_infos) {
   std::vector<struct kinfo_proc> kinfos;
 
   int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};

diff  --git a/lldb/source/Host/netbsd/Host.cpp b/lldb/source/Host/netbsd/Host.cpp
index 10c0c78beed7..57f985129a61 100644
--- a/lldb/source/Host/netbsd/Host.cpp
+++ b/lldb/source/Host/netbsd/Host.cpp
@@ -176,8 +176,8 @@ static bool GetNetBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
   return false;
 }
 
-uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
-                             ProcessInstanceInfoList &process_infos) {
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+                                 ProcessInstanceInfoList &process_infos) {
   const ::pid_t our_pid = ::getpid();
   const ::uid_t our_uid = ::getuid();
 

diff  --git a/lldb/source/Host/openbsd/Host.cpp b/lldb/source/Host/openbsd/Host.cpp
index 8fc8801775f3..4ce549522908 100644
--- a/lldb/source/Host/openbsd/Host.cpp
+++ b/lldb/source/Host/openbsd/Host.cpp
@@ -140,8 +140,8 @@ static bool GetOpenBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
   return false;
 }
 
-uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
-                             ProcessInstanceInfoList &process_infos) {
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+                                 ProcessInstanceInfoList &process_infos) {
   std::vector<struct kinfo_proc> kinfos;
 
   int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};

diff  --git a/lldb/source/Utility/ProcessInfo.cpp b/lldb/source/Utility/ProcessInfo.cpp
index 450e62d8c5d6..9a29218f7f1d 100644
--- a/lldb/source/Utility/ProcessInfo.cpp
+++ b/lldb/source/Utility/ProcessInfo.cpp
@@ -18,6 +18,7 @@
 
 using namespace lldb;
 using namespace lldb_private;
+using namespace lldb_private::repro;
 
 ProcessInfo::ProcessInfo()
     : m_executable(), m_arguments(), m_environment(), m_uid(UINT32_MAX),
@@ -344,3 +345,86 @@ void llvm::yaml::MappingTraits<ProcessInstanceInfo>::mapping(
   io.mapRequired("effective-gid", Info.m_egid);
   io.mapRequired("parent-pid", Info.m_parent_pid);
 }
+
+llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
+ProcessInfoRecorder::Create(const FileSpec &filename) {
+  std::error_code ec;
+  auto recorder =
+      std::make_unique<ProcessInfoRecorder>(std::move(filename), ec);
+  if (ec)
+    return llvm::errorCodeToError(ec);
+  return std::move(recorder);
+}
+
+void ProcessInfoProvider::Keep() {
+  std::vector<std::string> files;
+  for (auto &recorder : m_process_info_recorders) {
+    recorder->Stop();
+    files.push_back(recorder->GetFilename().GetPath());
+  }
+
+  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
+  std::error_code ec;
+  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
+  if (ec)
+    return;
+  llvm::yaml::Output yout(os);
+  yout << files;
+}
+
+void ProcessInfoProvider::Discard() { m_process_info_recorders.clear(); }
+
+ProcessInfoRecorder *ProcessInfoProvider::GetNewProcessInfoRecorder() {
+  std::size_t i = m_process_info_recorders.size() + 1;
+  std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +
+                          llvm::Twine(i) + llvm::Twine(".yaml"))
+                             .str();
+  auto recorder_or_error = ProcessInfoRecorder::Create(
+      GetRoot().CopyByAppendingPathComponent(filename));
+  if (!recorder_or_error) {
+    llvm::consumeError(recorder_or_error.takeError());
+    return nullptr;
+  }
+
+  m_process_info_recorders.push_back(std::move(*recorder_or_error));
+  return m_process_info_recorders.back().get();
+}
+
+void ProcessInfoRecorder::Record(const ProcessInstanceInfoList &process_infos) {
+  if (!m_record)
+    return;
+  llvm::yaml::Output yout(m_os);
+  yout << const_cast<ProcessInstanceInfoList &>(process_infos);
+  m_os.flush();
+}
+
+llvm::Optional<ProcessInstanceInfoList>
+repro::GetReplayProcessInstanceInfoList() {
+  static std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
+      loader = repro::MultiLoader<repro::ProcessInfoProvider>::Create(
+          repro::Reproducer::Instance().GetLoader());
+
+  if (!loader)
+    return {};
+
+  llvm::Optional<std::string> nextfile = loader->GetNextFile();
+  if (!nextfile)
+    return {};
+
+  auto error_or_file = llvm::MemoryBuffer::getFile(*nextfile);
+  if (std::error_code err = error_or_file.getError())
+    return {};
+
+  ProcessInstanceInfoList infos;
+  llvm::yaml::Input yin((*error_or_file)->getBuffer());
+  yin >> infos;
+
+  if (auto err = yin.error())
+    return {};
+
+  return infos;
+}
+
+char ProcessInfoProvider::ID = 0;
+const char *ProcessInfoProvider::Info::file = "process-info.yaml";
+const char *ProcessInfoProvider::Info::name = "process-info";

diff  --git a/lldb/test/API/functionalities/reproducers/attach/Makefile b/lldb/test/API/functionalities/reproducers/attach/Makefile
new file mode 100644
index 000000000000..3d0b98f13f3d
--- /dev/null
+++ b/lldb/test/API/functionalities/reproducers/attach/Makefile
@@ -0,0 +1,2 @@
+CXX_SOURCES := main.cpp
+include Makefile.rules

diff  --git a/lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py b/lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
new file mode 100644
index 000000000000..48659e46ebd7
--- /dev/null
+++ b/lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
@@ -0,0 +1,71 @@
+"""
+Test reproducer attach.
+"""
+
+import lldb
+import tempfile
+from lldbsuite.test import lldbtest_config
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class CreateAfterAttachTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipIfFreeBSD
+    @skipIfNetBSD
+    @skipIfWindows
+    @skipIfRemote
+    @skipIfiOSSimulator
+    def test_create_after_attach_with_fork(self):
+        """Test thread creation after process attach."""
+        exe = '%s_%d' % (self.testMethodName, os.getpid())
+
+        token = self.getBuildArtifact(exe + '.token')
+        if os.path.exists(token):
+            os.remove(token)
+
+        reproducer = self.getBuildArtifact(exe + '.reproducer')
+        if os.path.exists(reproducer):
+            try:
+                shutil.rmtree(reproducer)
+            except OSError:
+                pass
+
+        self.build(dictionary={'EXE': exe})
+        self.addTearDownHook(self.cleanupSubprocesses)
+
+        inferior = self.spawnSubprocess(self.getBuildArtifact(exe), [token])
+        pid = inferior.pid
+
+        lldbutil.wait_for_file_on_target(self, token)
+
+        # Use Popen because pexpect is overkill and spawnSubprocess is
+        # asynchronous.
+        capture = subprocess.Popen([
+            lldbtest_config.lldbExec, '-b', '--capture', '--capture-path',
+            reproducer, '-o', 'proc att -n {}'.format(exe), '-o',
+            'reproducer generate'
+        ],
+                                   stdin=subprocess.PIPE,
+                                   stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+        outs, errs = capture.communicate()
+        self.assertIn('Process {} stopped'.format(pid), outs)
+        self.assertIn('Reproducer written', outs)
+
+        # Check that replay works.
+        replay = subprocess.Popen(
+            [lldbtest_config.lldbExec, '-replay', reproducer],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        outs, errs = replay.communicate()
+        self.assertIn('Process {} stopped'.format(pid), outs)
+
+        # We can dump the reproducer in the current context.
+        self.expect('reproducer dump -f {} -p process'.format(reproducer),
+                    substrs=['pid = {}'.format(pid), 'name = {}'.format(exe)])

diff  --git a/lldb/test/API/functionalities/reproducers/attach/main.cpp b/lldb/test/API/functionalities/reproducers/attach/main.cpp
new file mode 100644
index 000000000000..781da12400af
--- /dev/null
+++ b/lldb/test/API/functionalities/reproducers/attach/main.cpp
@@ -0,0 +1,24 @@
+#include <chrono>
+#include <stdio.h>
+#include <thread>
+
+using std::chrono::seconds;
+
+int main(int argc, char const *argv[]) {
+  lldb_enable_attach();
+
+  // Create the synchronization token.
+  FILE *f;
+  if (f = fopen(argv[1], "wx")) {
+    fputs("\n", f);
+    fflush(f);
+    fclose(f);
+  } else
+    return 1;
+
+  while (true) {
+    std::this_thread::sleep_for(seconds(1));
+  }
+
+  return 0;
+}


        


More information about the lldb-commits mailing list