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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits