JDevlieghere updated this revision to Diff 249555.
JDevlieghere added a comment.

- Add ProcessInfo provider.
- Rewrite test as an dotest-test.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D75877/new/

https://reviews.llvm.org/D75877

Files:
  lldb/include/lldb/Utility/FileSpec.h
  lldb/include/lldb/Utility/ProcessInfo.h
  lldb/source/Commands/CommandObjectReproducer.cpp
  lldb/source/Host/macosx/objcxx/Host.mm
  lldb/source/Utility/FileSpec.cpp
  lldb/source/Utility/ProcessInfo.cpp
  lldb/test/API/functionalities/reproducers/attach/Makefile
  lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
  lldb/test/API/functionalities/reproducers/attach/main.cpp

Index: lldb/test/API/functionalities/reproducers/attach/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/reproducers/attach/main.cpp
@@ -0,0 +1,14 @@
+#include <chrono>
+#include <thread>
+
+using std::chrono::microseconds;
+
+int main(int argc, char const *argv[]) {
+  lldb_enable_attach();
+
+  while (true) {
+    std::this_thread::sleep_for(microseconds(1));
+  }
+
+  return 0;
+}
Index: lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
@@ -0,0 +1,75 @@
+"""
+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."""
+        self.build(dictionary={'EXE': 'somewhat_unique_name'})
+        self.addTearDownHook(self.cleanupSubprocesses)
+
+        reproducer_patch = tempfile.NamedTemporaryFile().name
+
+        inferior = self.spawnSubprocess(
+            self.getBuildArtifact("somewhat_unique_name"))
+        pid = inferior.pid
+
+        # Use Popen because pexpect is overkill and spawnSubprocess is
+        # asynchronous.
+        capture = subprocess.Popen([
+            lldbtest_config.lldbExec, '-b', '--capture', '--capture-path',
+            reproducer_patch, '-o', 'proc att -n somewhat_unique_name', '-o',
+            'reproducer generate'
+        ],
+                                   stdin=subprocess.PIPE,
+                                   stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+        outs, errs = capture.communicate()
+        self.assertTrue('Process {} stopped'.format(pid) in outs)
+        self.assertTrue('Reproducer written' in outs)
+
+        # Check that replay works.
+        replay = subprocess.Popen(
+            [lldbtest_config.lldbExec, '-replay', reproducer_patch],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        outs, errs = replay.communicate()
+        self.assertTrue('Process {} stopped'.format(pid) in outs)
+
+        # Check that reproducer dump works for process info.
+        replay = subprocess.Popen([
+            lldbtest_config.lldbExec, '-b', '-o',
+            'reproducer dump -f {} -p process'.format(reproducer_patch)
+        ],
+                                  stdin=subprocess.PIPE,
+                                  stdout=subprocess.PIPE,
+                                  stderr=subprocess.PIPE)
+        outs, errs = replay.communicate()
+        print(outs)
+        self.assertTrue('name = {}'.format('somewhat_unique_name'))
+        self.assertTrue('pid = {}'.format(pid))
+
+        # Remove the reproducer but don't complain in case the directory was
+        # never created.
+        try:
+            shutil.rmtree(reproducer_patch)
+        except OSError:
+            pass
Index: lldb/test/API/functionalities/reproducers/attach/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/reproducers/attach/Makefile
@@ -0,0 +1,2 @@
+CXX_SOURCES := main.cpp
+include Makefile.rules
Index: lldb/source/Utility/ProcessInfo.cpp
===================================================================
--- lldb/source/Utility/ProcessInfo.cpp
+++ 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),
@@ -331,3 +332,113 @@
   m_name_match_type = NameMatch::Ignore;
   m_match_all_users = false;
 }
+
+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();
+}
+
+void llvm::yaml::MappingTraits<ProcessInstanceInfo>::mapping(
+    IO &io, ProcessInstanceInfo &Info) {
+  io.mapRequired("executable", Info.m_executable);
+  io.mapRequired("arg0", Info.m_arg0);
+  io.mapRequired("uid", Info.m_uid);
+  io.mapRequired("gid", Info.m_gid);
+  io.mapRequired("pid", Info.m_pid);
+  io.mapRequired("effective-uid", Info.m_euid);
+  io.mapRequired("effective-gid", Info.m_egid);
+  io.mapRequired("parent-pid", Info.m_parent_pid);
+}
+
+llvm::StringRef llvm::yaml::MappingTraits<ProcessInstanceInfo>::validate(
+    IO &io, ProcessInstanceInfo &) {
+  return {};
+}
+
+void llvm::yaml::MappingTraits<ProcessInstanceInfoList>::mapping(
+    IO &io, ProcessInstanceInfoList &List) {
+  io.mapRequired("processes", List.m_infos);
+}
+
+llvm::StringRef llvm::yaml::MappingTraits<ProcessInstanceInfoList>::validate(
+    IO &io, ProcessInstanceInfoList &) {
+  return {};
+}
+
+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 (auto 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";
Index: lldb/source/Utility/FileSpec.cpp
===================================================================
--- lldb/source/Utility/FileSpec.cpp
+++ lldb/source/Utility/FileSpec.cpp
@@ -537,3 +537,15 @@
   if (!file.empty())
     Stream << file;
 }
+
+void llvm::yaml::ScalarTraits<FileSpec>::output(const FileSpec &Val, void *,
+                                                raw_ostream &Out) {
+  Out << Val.GetPath();
+}
+
+llvm::StringRef
+llvm::yaml::ScalarTraits<FileSpec>::input(llvm::StringRef Scalar, void *,
+                                          FileSpec &Val) {
+  Val = FileSpec(Scalar);
+  return {};
+}
Index: lldb/source/Host/macosx/objcxx/Host.mm
===================================================================
--- lldb/source/Host/macosx/objcxx/Host.mm
+++ lldb/source/Host/macosx/objcxx/Host.mm
@@ -593,6 +593,12 @@
 
 uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
                              ProcessInstanceInfoList &process_infos) {
+  if (llvm::Optional<ProcessInstanceInfoList> infos =
+          repro::GetReplayProcessInstanceInfoList()) {
+    process_infos = *infos;
+    return process_infos.GetSize();
+  }
+
   std::vector<struct kinfo_proc> kinfos;
 
   int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
@@ -664,6 +670,13 @@
       }
     }
   }
+
+  if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
+    g->GetOrCreate<repro::ProcessInfoProvider>()
+        .GetNewProcessInfoRecorder()
+        ->Record(process_infos);
+  }
+
   return process_infos.GetSize();
 }
 
Index: lldb/source/Commands/CommandObjectReproducer.cpp
===================================================================
--- lldb/source/Commands/CommandObjectReproducer.cpp
+++ 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 @@
   eReproducerProviderCommands,
   eReproducerProviderFiles,
   eReproducerProviderGDB,
+  eReproducerProviderProcessInfo,
   eReproducerProviderVersion,
   eReproducerProviderWorkingDirectory,
   eReproducerProviderNone
@@ -48,6 +50,11 @@
         "gdb",
         "GDB Remote Packets",
     },
+    {
+        eReproducerProviderProcessInfo,
+        "processes",
+        "Process Info",
+    },
     {
         eReproducerProviderVersion,
         "version",
@@ -448,6 +455,7 @@
       std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>>
           multi_loader =
               repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);
+
       llvm::Optional<std::string> gdb_file;
       while ((gdb_file = multi_loader->GetNextFile())) {
         auto error_or_file = MemoryBuffer::getFile(*gdb_file);
@@ -473,6 +481,45 @@
       result.SetStatus(eReturnStatusSuccessFinishResult);
       return true;
     }
+    case eReproducerProviderProcessInfo: {
+      std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
+          multi_loader =
+              repro::MultiLoader<repro::ProcessInfoProvider>::Create(loader);
+
+      if (!multi_loader) {
+        SetError(result, make_error<StringError>(
+                             llvm::inconvertibleErrorCode(),
+                             "Unable to create process info loader."));
+        return false;
+      }
+
+      llvm::Optional<std::string> process_file;
+
+      while ((process_file = multi_loader->GetNextFile())) {
+        auto error_or_file = MemoryBuffer::getFile(*process_file);
+        if (auto err = error_or_file.getError()) {
+          SetError(result, errorCodeToError(err));
+          return false;
+        }
+
+        ProcessInstanceInfoList infos;
+        yaml::Input yin((*error_or_file)->getBuffer());
+        yin >> infos;
+
+        if (auto err = yin.error()) {
+          SetError(result, errorCodeToError(err));
+          return false;
+        }
+
+        for (size_t i = 0; i < infos.GetSize(); ++i) {
+          ProcessInstanceInfo info = infos.GetProcessInfoAtIndex(i);
+          info.Dump(result.GetOutputStream(), HostInfo::GetUserIDResolver());
+        }
+      }
+
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+      return true;
+    }
     case eReproducerProviderNone:
       result.SetError("No valid provider specified.");
       return false;
Index: lldb/include/lldb/Utility/ProcessInfo.h
===================================================================
--- lldb/include/lldb/Utility/ProcessInfo.h
+++ lldb/include/lldb/Utility/ProcessInfo.h
@@ -15,6 +15,7 @@
 #include "lldb/Utility/Environment.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/NameMatches.h"
+#include "lldb/Utility/Reproducer.h"
 
 #include <vector>
 
@@ -89,6 +90,8 @@
   const Environment &GetEnvironment() const { return m_environment; }
 
 protected:
+  template <class T> friend struct llvm::yaml::MappingTraits;
+
   FileSpec m_executable;
   std::string m_arg0; // argv[0] if supported. If empty, then use m_executable.
   // Not all process plug-ins support specifying an argv[0] that differs from
@@ -150,6 +153,8 @@
                       bool verbose) const;
 
 protected:
+  template <class T> friend struct llvm::yaml::MappingTraits;
+
   uint32_t m_euid;
   uint32_t m_egid;
   lldb::pid_t m_parent_pid;
@@ -188,6 +193,7 @@
   }
 
 protected:
+  template <class T> friend struct llvm::yaml::MappingTraits;
   std::vector<ProcessInstanceInfo> m_infos;
 };
 
@@ -248,6 +254,60 @@
   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)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<lldb_private::ProcessInstanceInfo> {
+  static void mapping(IO &io, lldb_private::ProcessInstanceInfo &PII);
+  static StringRef validate(IO &io, lldb_private::ProcessInstanceInfo &);
+};
+
+template <> struct MappingTraits<lldb_private::ProcessInstanceInfoList> {
+  static void mapping(IO &io, lldb_private::ProcessInstanceInfoList &PIIL);
+  static StringRef validate(IO &io, lldb_private::ProcessInstanceInfoList &);
+};
+
+} // namespace yaml
+} // namespace llvm
+
 #endif // LLDB_UTILITY_PROCESSINFO_H
Index: lldb/include/lldb/Utility/FileSpec.h
===================================================================
--- lldb/include/lldb/Utility/FileSpec.h
+++ lldb/include/lldb/Utility/FileSpec.h
@@ -18,6 +18,7 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/YAMLTraits.h"
 
 #include <stddef.h>
 #include <stdint.h>
@@ -436,6 +437,14 @@
   static void format(const lldb_private::FileSpec &F, llvm::raw_ostream &Stream,
                      StringRef Style);
 };
+
+namespace yaml {
+template <> struct ScalarTraits<lldb_private::FileSpec> {
+  static void output(const lldb_private::FileSpec &, void *, raw_ostream &);
+  static StringRef input(StringRef, void *, lldb_private::FileSpec &);
+  static QuotingType mustQuote(StringRef S) { return QuotingType::Double; }
+};
+} // namespace yaml
 } // namespace llvm
 
 #endif // LLDB_UTILITY_FILESPEC_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to