This revision was automatically updated to reflect the committed changes.
JDevlieghere marked an inline comment as done.
Closed by commit rG2451cbf07bbc: [lldb/Reproducers] Intercept the FindProcesses
API (authored by JDevlieghere).
Changed prior to commit:
https://reviews.llvm.org/D75877?vs=249764&id=250236#toc
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D75877/new/
https://reviews.llvm.org/D75877
Files:
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
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,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;
+}
Index: lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
===================================================================
--- /dev/null
+++ 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)])
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),
@@ -344,3 +345,86 @@
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";
Index: lldb/source/Host/openbsd/Host.cpp
===================================================================
--- lldb/source/Host/openbsd/Host.cpp
+++ lldb/source/Host/openbsd/Host.cpp
@@ -140,8 +140,8 @@
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};
Index: lldb/source/Host/netbsd/Host.cpp
===================================================================
--- lldb/source/Host/netbsd/Host.cpp
+++ lldb/source/Host/netbsd/Host.cpp
@@ -176,8 +176,8 @@
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();
Index: lldb/source/Host/macosx/objcxx/Host.mm
===================================================================
--- lldb/source/Host/macosx/objcxx/Host.mm
+++ lldb/source/Host/macosx/objcxx/Host.mm
@@ -591,8 +591,8 @@
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};
Index: lldb/source/Host/linux/Host.cpp
===================================================================
--- lldb/source/Host/linux/Host.cpp
+++ lldb/source/Host/linux/Host.cpp
@@ -221,8 +221,8 @@
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);
Index: lldb/source/Host/common/Host.cpp
===================================================================
--- lldb/source/Host/common/Host.cpp
+++ lldb/source/Host/common/Host.cpp
@@ -678,3 +678,23 @@
}
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;
+}
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
@@ -49,6 +51,11 @@
"GDB Remote Packets",
},
{
+ eReproducerProviderProcessInfo,
+ "processes",
+ "Process Info",
+ },
+ {
eReproducerProviderVersion,
"version",
"Version",
@@ -97,6 +104,24 @@
#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 @@
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;
}
}
Index: lldb/include/lldb/Utility/ProcessInfo.h
===================================================================
--- lldb/include/lldb/Utility/ProcessInfo.h
+++ 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 @@
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)
Index: lldb/include/lldb/Host/Host.h
===================================================================
--- lldb/include/lldb/Host/Host.h
+++ lldb/include/lldb/Host/Host.h
@@ -232,6 +232,10 @@
static std::unique_ptr<Connection>
CreateDefaultConnection(llvm::StringRef url);
+
+protected:
+ static uint32_t FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &proc_infos);
};
} // namespace lldb_private
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits