mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
[lldb] Remove reproducer replay functionality
This is part of a bigger rework of the reproducer feature. See [1] for more details. [1] https://lists.llvm.org/pipermail/lldb-dev/2021-September/017045.html
This commit is contained in:
@@ -29,8 +29,6 @@ class Reproducer;
|
||||
|
||||
enum class ReproducerMode {
|
||||
Capture,
|
||||
Replay,
|
||||
PassiveReplay,
|
||||
Off,
|
||||
};
|
||||
|
||||
@@ -179,15 +177,12 @@ public:
|
||||
|
||||
const FileSpec &GetRoot() const { return m_root; }
|
||||
|
||||
bool IsPassiveReplay() const { return m_passive_replay; }
|
||||
|
||||
private:
|
||||
bool HasFile(llvm::StringRef file);
|
||||
|
||||
FileSpec m_root;
|
||||
std::vector<std::string> m_files;
|
||||
bool m_loaded;
|
||||
bool m_passive_replay;
|
||||
};
|
||||
|
||||
/// The reproducer enables clients to obtain access to the Generator and
|
||||
@@ -212,11 +207,9 @@ public:
|
||||
FileSpec GetReproducerPath() const;
|
||||
|
||||
bool IsCapturing() { return static_cast<bool>(m_generator); };
|
||||
bool IsReplaying() { return static_cast<bool>(m_loader); };
|
||||
|
||||
protected:
|
||||
llvm::Error SetCapture(llvm::Optional<FileSpec> root);
|
||||
llvm::Error SetReplay(llvm::Optional<FileSpec> root, bool passive = false);
|
||||
|
||||
private:
|
||||
static llvm::Optional<Reproducer> &InstanceImpl();
|
||||
|
||||
@@ -165,111 +165,24 @@ const char *SBReproducer::Capture(const char *path) {
|
||||
}
|
||||
|
||||
const char *SBReproducer::PassiveReplay(const char *path) {
|
||||
static std::string error;
|
||||
if (auto e = Reproducer::Initialize(ReproducerMode::PassiveReplay,
|
||||
FileSpec(path))) {
|
||||
error = llvm::toString(std::move(e));
|
||||
return error.c_str();
|
||||
}
|
||||
|
||||
if (auto *l = lldb_private::repro::Reproducer::Instance().GetLoader()) {
|
||||
FileSpec file = l->GetFile<SBProvider::Info>();
|
||||
auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
|
||||
if (!error_or_file) {
|
||||
error =
|
||||
"unable to read SB API data: " + error_or_file.getError().message();
|
||||
return error.c_str();
|
||||
}
|
||||
static ReplayData r(std::move(*error_or_file));
|
||||
InstrumentationData::Initialize(r.GetDeserializer(), r.GetRegistry());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return "Reproducer replay has been removed";
|
||||
}
|
||||
|
||||
const char *SBReproducer::Replay(const char *path) {
|
||||
SBReplayOptions options;
|
||||
return SBReproducer::Replay(path, options);
|
||||
return "Reproducer replay has been removed";
|
||||
}
|
||||
|
||||
const char *SBReproducer::Replay(const char *path, bool skip_version_check) {
|
||||
SBReplayOptions options;
|
||||
options.SetCheckVersion(!skip_version_check);
|
||||
return SBReproducer::Replay(path, options);
|
||||
return Replay(path);
|
||||
}
|
||||
|
||||
const char *SBReproducer::Replay(const char *path,
|
||||
const SBReplayOptions &options) {
|
||||
static std::string error;
|
||||
if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) {
|
||||
error = llvm::toString(std::move(e));
|
||||
return error.c_str();
|
||||
}
|
||||
|
||||
repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
|
||||
if (!loader) {
|
||||
error = "unable to get replay loader.";
|
||||
return error.c_str();
|
||||
}
|
||||
|
||||
if (options.GetCheckVersion()) {
|
||||
llvm::Expected<std::string> version = loader->LoadBuffer<VersionProvider>();
|
||||
if (!version) {
|
||||
error = llvm::toString(version.takeError());
|
||||
return error.c_str();
|
||||
}
|
||||
if (lldb_private::GetVersion() != llvm::StringRef(*version).rtrim()) {
|
||||
error = "reproducer capture and replay version don't match:\n";
|
||||
error.append("reproducer captured with:\n");
|
||||
error.append(*version);
|
||||
error.append("reproducer replayed with:\n");
|
||||
error.append(lldb_private::GetVersion());
|
||||
return error.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (options.GetVerify()) {
|
||||
bool verification_failed = false;
|
||||
llvm::raw_string_ostream os(error);
|
||||
auto error_callback = [&](llvm::StringRef error) {
|
||||
verification_failed = true;
|
||||
os << "\nerror: " << error;
|
||||
};
|
||||
|
||||
auto warning_callback = [&](llvm::StringRef warning) {
|
||||
verification_failed = true;
|
||||
os << "\nwarning: " << warning;
|
||||
};
|
||||
|
||||
auto note_callback = [&](llvm::StringRef warning) {};
|
||||
|
||||
Verifier verifier(loader);
|
||||
verifier.Verify(error_callback, warning_callback, note_callback);
|
||||
|
||||
if (verification_failed) {
|
||||
os.flush();
|
||||
return error.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
FileSpec file = loader->GetFile<SBProvider::Info>();
|
||||
if (!file) {
|
||||
error = "unable to get replay data from reproducer.";
|
||||
return error.c_str();
|
||||
}
|
||||
|
||||
SBRegistry registry;
|
||||
registry.Replay(file);
|
||||
|
||||
return nullptr;
|
||||
return Replay(path);
|
||||
}
|
||||
|
||||
const char *SBReproducer::Finalize(const char *path) {
|
||||
static std::string error;
|
||||
if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) {
|
||||
error = llvm::toString(std::move(e));
|
||||
return error.c_str();
|
||||
}
|
||||
|
||||
repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
|
||||
if (!loader) {
|
||||
|
||||
@@ -50,15 +50,8 @@ SystemInitializerFull::~SystemInitializerFull() = default;
|
||||
|
||||
llvm::Error SystemInitializerFull::Initialize() {
|
||||
llvm::Error error = SystemInitializerCommon::Initialize();
|
||||
if (error) {
|
||||
// During active replay, the ::Initialize call is replayed like any other
|
||||
// SB API call and the return value is ignored. Since we can't intercept
|
||||
// this, we terminate here before the uninitialized debugger inevitably
|
||||
// crashes.
|
||||
if (repro::Reproducer::Instance().IsReplaying())
|
||||
llvm::report_fatal_error(std::move(error));
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
// Initialize LLVM and Clang
|
||||
llvm::InitializeAllTargets();
|
||||
|
||||
@@ -195,10 +195,6 @@ protected:
|
||||
SetError(result, std::move(e));
|
||||
return result.Succeeded();
|
||||
}
|
||||
} else if (r.IsReplaying()) {
|
||||
// Make this operation a NO-OP in replay mode.
|
||||
result.SetStatus(eReturnStatusSuccessFinishNoResult);
|
||||
return result.Succeeded();
|
||||
} else {
|
||||
result.AppendErrorWithFormat("Unable to get the reproducer generator");
|
||||
return false;
|
||||
@@ -276,7 +272,7 @@ protected:
|
||||
|
||||
auto &r = Reproducer::Instance();
|
||||
|
||||
if (!r.IsCapturing() && !r.IsReplaying()) {
|
||||
if (!r.IsCapturing()) {
|
||||
result.AppendError(
|
||||
"forcing a crash is only supported when capturing a reproducer.");
|
||||
result.SetStatus(eReturnStatusSuccessFinishNoResult);
|
||||
@@ -326,15 +322,10 @@ protected:
|
||||
auto &r = Reproducer::Instance();
|
||||
if (r.IsCapturing()) {
|
||||
result.GetOutputStream() << "Reproducer is in capture mode.\n";
|
||||
} else if (r.IsReplaying()) {
|
||||
result.GetOutputStream() << "Reproducer is in replay mode.\n";
|
||||
} else {
|
||||
result.GetOutputStream() << "Reproducer is off.\n";
|
||||
}
|
||||
|
||||
if (r.IsCapturing() || r.IsReplaying()) {
|
||||
result.GetOutputStream()
|
||||
<< "Path: " << r.GetReproducerPath().GetPath() << '\n';
|
||||
} else {
|
||||
result.GetOutputStream() << "Reproducer is off.\n";
|
||||
}
|
||||
|
||||
// Auto generate is hidden unless enabled because this is mostly for
|
||||
|
||||
@@ -225,18 +225,12 @@ Status PlatformRemoteGDBServer::ConnectRemote(Args &args) {
|
||||
m_platform_hostname = parsed_url->hostname.str();
|
||||
|
||||
m_gdb_client.SetConnection(std::make_unique<ConnectionFileDescriptor>());
|
||||
if (repro::Reproducer::Instance().IsReplaying()) {
|
||||
error = m_gdb_replay_server.Connect(m_gdb_client);
|
||||
if (error.Success())
|
||||
m_gdb_replay_server.StartAsyncThread();
|
||||
} else {
|
||||
if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
|
||||
repro::GDBRemoteProvider &provider =
|
||||
g->GetOrCreate<repro::GDBRemoteProvider>();
|
||||
m_gdb_client.SetPacketRecorder(provider.GetNewPacketRecorder());
|
||||
}
|
||||
m_gdb_client.Connect(url, &error);
|
||||
if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {
|
||||
repro::GDBRemoteProvider &provider =
|
||||
g->GetOrCreate<repro::GDBRemoteProvider>();
|
||||
m_gdb_client.SetPacketRecorder(provider.GetNewPacketRecorder());
|
||||
}
|
||||
m_gdb_client.Connect(url, &error);
|
||||
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
@@ -526,18 +526,15 @@ Status ProcessGDBRemote::WillAttachToProcessWithName(const char *process_name,
|
||||
|
||||
Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) {
|
||||
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
|
||||
|
||||
Status error(WillLaunchOrAttach());
|
||||
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
if (repro::Reproducer::Instance().IsReplaying())
|
||||
error = ConnectToReplayServer();
|
||||
else
|
||||
error = ConnectToDebugserver(remote_url);
|
||||
|
||||
error = ConnectToDebugserver(remote_url);
|
||||
if (error.Fail())
|
||||
return error;
|
||||
|
||||
StartAsyncThread();
|
||||
|
||||
lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID();
|
||||
@@ -3259,9 +3256,6 @@ ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) {
|
||||
if (platform_sp && !platform_sp->IsHost())
|
||||
return Status("Lost debug server connection");
|
||||
|
||||
if (repro::Reproducer::Instance().IsReplaying())
|
||||
return ConnectToReplayServer();
|
||||
|
||||
auto error = LaunchAndConnectToDebugserver(process_info);
|
||||
if (error.Fail()) {
|
||||
const char *error_string = error.AsCString();
|
||||
@@ -3540,7 +3534,7 @@ thread_result_t ProcessGDBRemote::AsyncThread(void *arg) {
|
||||
// So it is safer to simply ignore any remaining packets by
|
||||
// explicitly checking for eStateExited before reentering the
|
||||
// fetch loop.
|
||||
|
||||
|
||||
bool done = false;
|
||||
while (!done && process->GetPrivateState() != eStateExited) {
|
||||
LLDB_LOGF(log,
|
||||
|
||||
@@ -44,10 +44,6 @@ llvm::Error Reproducer::Initialize(ReproducerMode mode,
|
||||
}
|
||||
return Instance().SetCapture(root);
|
||||
} break;
|
||||
case ReproducerMode::Replay:
|
||||
return Instance().SetReplay(root, /*passive*/ false);
|
||||
case ReproducerMode::PassiveReplay:
|
||||
return Instance().SetReplay(root, /*passive*/ true);
|
||||
case ReproducerMode::Off:
|
||||
break;
|
||||
};
|
||||
@@ -116,26 +112,6 @@ llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root, bool passive) {
|
||||
std::lock_guard<std::mutex> guard(m_mutex);
|
||||
|
||||
if (root && m_generator)
|
||||
return make_error<StringError>(
|
||||
"cannot replay a reproducer when generating one",
|
||||
inconvertibleErrorCode());
|
||||
|
||||
if (!root) {
|
||||
m_loader.reset();
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
m_loader.emplace(*root, passive);
|
||||
if (auto e = m_loader->LoadIndex())
|
||||
return e;
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
FileSpec Reproducer::GetReproducerPath() const {
|
||||
if (auto g = GetGenerator())
|
||||
return g->GetRoot();
|
||||
@@ -222,8 +198,7 @@ void Generator::AddProvidersToIndex() {
|
||||
}
|
||||
|
||||
Loader::Loader(FileSpec root, bool passive)
|
||||
: m_root(MakeAbsolute(std::move(root))), m_loaded(false),
|
||||
m_passive_replay(passive) {}
|
||||
: m_root(MakeAbsolute(std::move(root))), m_loaded(false) {}
|
||||
|
||||
llvm::Error Loader::LoadIndex() {
|
||||
if (m_loaded)
|
||||
|
||||
@@ -57,16 +57,6 @@ class ReproducerAttachTestCase(TestBase):
|
||||
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, _ = replay.communicate()
|
||||
outs = outs.decode('utf-8')
|
||||
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)])
|
||||
|
||||
@@ -48,7 +48,6 @@ CHECK: -f
|
||||
CHECK: --help
|
||||
CHECK: -h
|
||||
CHECK: --no-use-colors
|
||||
CHECK: --replay
|
||||
CHECK: --version
|
||||
CHECK: -v
|
||||
CHECK: -X
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
breakpoint set -f foo.cpp -l 11
|
||||
run
|
||||
frame var foo
|
||||
next
|
||||
frame var foo
|
||||
cont
|
||||
reproducer generate
|
||||
@@ -1,10 +0,0 @@
|
||||
breakpoint set -f foo.cpp -l 11
|
||||
run
|
||||
p foo
|
||||
next
|
||||
p Foo(2, 3.33);
|
||||
p $1
|
||||
p foo = Foo(3, 4.44);
|
||||
p foo
|
||||
cont
|
||||
reproducer generate
|
||||
@@ -1,13 +0,0 @@
|
||||
class Foo {
|
||||
public:
|
||||
Foo(int i, double d) : m_i(i), m_d(d){};
|
||||
|
||||
private:
|
||||
int m_i;
|
||||
int m_d;
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
static Foo foo(1, 2.22);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
int a(int);
|
||||
int b(int);
|
||||
int c(int);
|
||||
int complex(int, int, int);
|
||||
|
||||
int a(int val) {
|
||||
int return_value = val;
|
||||
|
||||
if (val <= 1) {
|
||||
return_value = b(val);
|
||||
} else if (val >= 3) {
|
||||
return_value = c(val);
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
int b(int val) {
|
||||
int rc = c(val);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int c(int val) { return val + 3; }
|
||||
|
||||
int complex(int first, int second, int third) { return first + second + third; }
|
||||
|
||||
int main(int argc, char const *argv[]) {
|
||||
int A1 = a(1);
|
||||
|
||||
int B2 = b(2);
|
||||
|
||||
int A3 = a(3);
|
||||
|
||||
int A4 = complex(a(1), b(2), c(3));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# This tests that data formatters continue to work when replaying a reproducer.
|
||||
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %clangxx_host %S/Inputs/foo.cpp -g -o %t.out
|
||||
|
||||
# RUN: %lldb -x -b -s %S/Inputs/DataFormatter.in --capture --capture-path %t.repro %t.out | FileCheck %s
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s
|
||||
|
||||
# CHECK: stop reason = breakpoint 1.1
|
||||
# CHECK: (Foo) foo = (m_i = 0, m_d = 0)
|
||||
# CHECK: stop reason = step over
|
||||
# CHECK: (Foo) foo = (m_i = 1, m_d = 2)
|
||||
# CHECK: Process {{.*}} resuming
|
||||
# CHECK: Process {{.*}} exited with status = 0
|
||||
@@ -1,22 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
# UNSUPPORTED: system-netbsd
|
||||
|
||||
# Flaky
|
||||
# UNSUPPORTED: system-linux
|
||||
|
||||
# This tests that expression evaluation continues to work when replaying a
|
||||
# reproducer.
|
||||
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %clangxx_host %S/Inputs/foo.cpp -g -o %t.out
|
||||
|
||||
# RUN: %lldb -x -b -s %S/Inputs/ExpressionEvaluation.in --capture --capture-path %t.repro %t.out | FileCheck %s
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s
|
||||
|
||||
# CHECK: stop reason = breakpoint 1.1
|
||||
# CHECK: (Foo) $0 = (m_i = 0, m_d = 0)
|
||||
# CHECK: stop reason = step over
|
||||
# CHECK: (Foo) $1 = (m_i = 2, m_d = 3)
|
||||
# CHECK: (Foo) $1 = (m_i = 2, m_d = 3)
|
||||
# CHECK: (Foo) $2 = (m_i = 3, m_d = 4)
|
||||
# CHECK: (Foo) $3 = (m_i = 3, m_d = 4)
|
||||
@@ -1,31 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# This tests that image list works when replaying. We arbitrarily assume
|
||||
# there's at least two entries and compare that they're identical.
|
||||
|
||||
# RUN: %clang_host %S/Inputs/stepping.c -g -o %t.out
|
||||
|
||||
# RUN: rm -rf %t.txt
|
||||
# RUN: rm -rf %t.repro
|
||||
|
||||
# RUN: echo "CAPTURE" >> %t.txt
|
||||
# RUN: %lldb -x -b --capture --capture-path %t.repro \
|
||||
# RUN: -o 'run' \
|
||||
# RUN: -o 'image list' \
|
||||
# RUN: -o 'reproducer generate' \
|
||||
# RUN: %t.out >> %t.txt 2>&1
|
||||
|
||||
# RUN: echo "REPLAY" >> %t.txt
|
||||
# RUN: %lldb -x -b --replay %t.repro >> %t.txt 2>&1
|
||||
|
||||
# RUN: cat %t.txt | FileCheck %s
|
||||
|
||||
# CHECK: CAPTURE
|
||||
# CHECK: image list
|
||||
# CHECK: [ 0] [[ZERO:.*]]
|
||||
# CHECK: [ 1] [[ONE:.*]]
|
||||
|
||||
# CHECK: REPLAY
|
||||
# CHECK: image list
|
||||
# CHECK: [ 0] {{.*}}[[ZERO]]
|
||||
# CHECK: [ 1] {{.*}}[[ONE]]
|
||||
@@ -1,100 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# This tests that stepping continues to work when replaying a reproducer.
|
||||
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %clang_host %S/Inputs/stepping.c -O0 -g -o %t.out
|
||||
# RUN: grep -v '#' %s > %t.in
|
||||
|
||||
# RUN: %lldb -x -b -s %t.in --capture --capture-path %t.repro %t.out | FileCheck %s --check-prefix CHECK
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK
|
||||
|
||||
# Set breakpoints in a,b and c and verify we stop there when stepping.
|
||||
|
||||
breakpoint set -f stepping.c -l 28
|
||||
# CHECK: Breakpoint 1: {{.*}} stepping.c:28
|
||||
|
||||
breakpoint set -f stepping.c -l 10
|
||||
# CHECK: Breakpoint 2: {{.*}} stepping.c:10
|
||||
|
||||
breakpoint set -f stepping.c -l 19
|
||||
# CHECK: Breakpoint 3: {{.*}} stepping.c:19
|
||||
|
||||
breakpoint set -f stepping.c -l 23
|
||||
# CHECK: Breakpoint 4: {{.*}} stepping.c:23
|
||||
|
||||
run
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = breakpoint 1.1
|
||||
|
||||
next
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = breakpoint 2.1
|
||||
|
||||
next
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = breakpoint 3.1
|
||||
|
||||
next
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = breakpoint 4.1
|
||||
|
||||
bt
|
||||
# CHECK: frame #0: {{.*}}`c
|
||||
# CHECK: frame #1: {{.*}}`b
|
||||
# CHECK: frame #2: {{.*}}`a
|
||||
|
||||
# Continue and stop resulting from the step overs.
|
||||
|
||||
cont
|
||||
# CHECK: Process {{.*}} resuming
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = step over
|
||||
cont
|
||||
# CHECK: Process {{.*}} resuming
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = step over
|
||||
cont
|
||||
# CHECK: Process {{.*}} resuming
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: stop reason = step over
|
||||
|
||||
breakpoint disable 1
|
||||
# CHECK: 1 breakpoints disabled.
|
||||
|
||||
breakpoint disable 2
|
||||
# CHECK: 1 breakpoints disabled.
|
||||
|
||||
breakpoint disable 3
|
||||
# CHECK: 1 breakpoints disabled.
|
||||
|
||||
breakpoint disable 4
|
||||
# CHECK: 1 breakpoints disabled.
|
||||
|
||||
# Continue to line 48.
|
||||
|
||||
next
|
||||
# CHECK: stop reason = step over
|
||||
next
|
||||
# CHECK: stop reason = step over
|
||||
|
||||
# Step into c.
|
||||
thread step-in
|
||||
# CHECK: stop reason = step in
|
||||
thread step-in
|
||||
# CHECK: stop reason = step in
|
||||
thread step-in
|
||||
# CHECK: stop reason = step in
|
||||
thread step-in
|
||||
# CHECK: stop reason = step in
|
||||
thread step-in
|
||||
# CHECK: stop reason = step in
|
||||
|
||||
# Finally continue until the end.
|
||||
|
||||
cont
|
||||
# CHECK: Process {{.*}} resuming
|
||||
# CHECK: Process {{.*}} exited with status = 0
|
||||
|
||||
# Generate the reproducer.
|
||||
reproducer generate
|
||||
@@ -31,7 +31,3 @@
|
||||
# RUN: rm -rf %t.root
|
||||
# RUN: rm -rf %t.clang-cache
|
||||
# RUN: rm -rf %t.lldb-cache
|
||||
|
||||
# Replay the debug session.
|
||||
# RUN: %lldb -x -b -o 'settings set symbols.clang-modules-cache-path %t.lldb-cache' -s %S/Inputs/ModuleCXX.in --replay %t.repro %t.root/a.out | FileCheck %s --check-prefix REPLAY
|
||||
# REPLAY: (success = 0)
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# Check that errors are propagated to the driver.
|
||||
#
|
||||
# RUN: not %lldb --capture --capture-path %t/bogus/bogus 2>&1 | FileCheck %s --check-prefix INVALID-CAPTURE
|
||||
# RUN: not %lldb --replay %t/bogus/bogus 2>&1 | FileCheck %s --check-prefix INVALID-REPLAY
|
||||
#
|
||||
# INVALID-CAPTURE: unable to create reproducer directory
|
||||
# INVALID-REPLAY: unable to load reproducer index
|
||||
|
||||
# Check that all option combination work as expected.
|
||||
#
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
# GDB: send packet: $QStartNoAckMode#b0
|
||||
# GDB: read packet: $OK#9a
|
||||
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix FILES
|
||||
|
||||
# RUN: rm %t.repro/gdb-remote.yaml
|
||||
# RUN: not %lldb -b -o 'reproducer dump -p gdb -f %t.repro' 2>&1 | FileCheck %s --check-prefix GDB-ERROR
|
||||
# GDB-ERROR: error: Unable to create GDB loader.
|
||||
|
||||
@@ -10,15 +10,10 @@
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
|
||||
# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in --capture --capture-path %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
|
||||
# RUN: rm %t.out
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
|
||||
# RUN: cat %t.repro/version.txt | FileCheck %s --check-prefix VERSION
|
||||
|
||||
# CAPTURE: testing
|
||||
# REPLAY-NOT: testing
|
||||
|
||||
# CHECK: Process {{.*}} exited
|
||||
|
||||
# CAPTURE: Reproducer is in capture mode.
|
||||
# CAPTURE: Reproducer written
|
||||
|
||||
# VERSION: lldb version
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# This tests the replaying of GDB remote packets.
|
||||
#
|
||||
# We issue the same commands and ensure the output is identical to the original
|
||||
# process. To ensure we're not actually running the original binary we check
|
||||
# that the string "testing" is not printed.
|
||||
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
|
||||
|
||||
# Test reproducer generate command.
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture --capture-path %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
|
||||
# RUN: env FOO=BAR %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
|
||||
|
||||
# CHECK: Breakpoint 1
|
||||
# CHECK: Process {{.*}} stopped
|
||||
# CHECK: Process {{.*}} launched
|
||||
# CHECK: thread {{.*}} stop reason = breakpoint
|
||||
# CHECK: frame {{.*}} simple.c
|
||||
|
||||
# CAPTURE: testing
|
||||
# REPLAY-NOT: testing
|
||||
|
||||
# CHECK: Process {{.*}} resuming
|
||||
# CHECK: Process {{.*}} exited
|
||||
|
||||
# CAPTURE: Reproducer is in capture mode.
|
||||
# CAPTURE: Reproducer written
|
||||
@@ -9,6 +9,4 @@
|
||||
|
||||
# RUN: cat %t.repro/home.txt | FileCheck %t.check
|
||||
# RUN: %lldb -b -o 'reproducer dump -p home -f %t.repro' | FileCheck %t.check
|
||||
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s
|
||||
# CHECK: 95126
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# This tests the replaying with multiple targets.
|
||||
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
|
||||
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %lldb -x -b --capture --capture-path %t.repro -o 'target create %t.out' -o 'target create %t.out' -s %S/Inputs/MultipleTargetsCapture.in | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
|
||||
# RUN: env FOO=BAR %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
|
||||
|
||||
# CHECK: Process [[TARGET0:[0-9]+]] stopped
|
||||
# CHECK: stop reason = breakpoint 1.1
|
||||
# CHECK: simple.c:4:5
|
||||
# CHECK: Process [[TARGET1:[0-9]+]] stopped
|
||||
# CHECK: stop reason = breakpoint 1.1
|
||||
# CHECK: simple.c:8:5
|
||||
# CHECK: Process [[TARGET0]] resuming
|
||||
# CHECK: Process [[TARGET0]] exited
|
||||
# CHECK: Process [[TARGET1]] resuming
|
||||
# CHECK: Process [[TARGET1]] exited
|
||||
|
||||
# CAPTURE: Reproducer is in capture mode.
|
||||
# CAPTURE: Reproducer written
|
||||
|
||||
# REPLAY: Reproducer is in replay mode.
|
||||
@@ -1,21 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# Test that ProcessInfo is correctly serialized by comparing the output of
|
||||
# 'platform process list -v' during capture and replay. The test assumes that
|
||||
# there's at least two processes.
|
||||
|
||||
# RUN: %lldb -x -b -o 'platform process list -v' -o 'reproducer generate' --capture --capture-path %t.repro > %t.log
|
||||
# RUN: %lldb --replay %t.repro >> %t.log
|
||||
# RUN: cat %t.log | FileCheck %s
|
||||
|
||||
# CHECK: [[PROCS:[0-9]+]] matching processes were found
|
||||
# CHECK: PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE ARGUMENTS
|
||||
# CHECK-NEXT: ====== ====== ========== ========== ========== ========== ============================== ============================
|
||||
# CHECK-NEXT: [[PID0:[0-9]+]] [[PROC0:.*]]
|
||||
# CHECK-NEXT: [[PID1:[0-9]+]] [[PROC1:.*]]
|
||||
# CHECK: Reproducer written to
|
||||
# CHECK: [[PROCS]] matching processes were found
|
||||
# CHECK: PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE ARGUMENTS
|
||||
# CHECK-NEXT: ====== ====== ========== ========== ========== ========== ============================== ============================
|
||||
# CHECK-NEXT: [[PID0]] [[PROC0]]
|
||||
# CHECK-NEXT: [[PID1]] [[PROC1]]
|
||||
@@ -1,8 +0,0 @@
|
||||
# This tests relative capture paths.
|
||||
|
||||
# RUN: mkdir -p %t
|
||||
# RUN: cd %t
|
||||
# RUN: rm -rf ./foo
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t/reproducer.out
|
||||
# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in -o 'reproducer dump -p files' --capture --capture-path ./foo %t/reproducer.out
|
||||
# RUN: %lldb --replay ./foo
|
||||
@@ -1,17 +0,0 @@
|
||||
# UNSUPPORTED: system-freebsd
|
||||
|
||||
# Test that we can capture twice to the same directory without breaking the
|
||||
# reproducer functionality.
|
||||
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
|
||||
# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture --capture-path %t.repro %t.out | FileCheck %S/TestGDBRemoteRepro.test --check-prefix CHECK --check-prefix CAPTURE
|
||||
# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture --capture-path %t.repro %t.out | FileCheck %S/TestGDBRemoteRepro.test --check-prefix CHECK --check-prefix CAPTURE
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %S/TestGDBRemoteRepro.test --check-prefix CHECK --check-prefix REPLAY
|
||||
|
||||
# Test that we can replay from a different location, i.e. that the reproducer
|
||||
# is relocatable.
|
||||
|
||||
# RUN: rm -rf %t.repro_moved
|
||||
# RUN: mv %t.repro %t.repro_moved
|
||||
# RUN: %lldb --replay %t.repro_moved | FileCheck %S/TestGDBRemoteRepro.test --check-prefix CHECK --check-prefix REPLAY
|
||||
@@ -1,14 +0,0 @@
|
||||
# REQUIRES: python
|
||||
# Ensure that replay happens in synchronous mode.
|
||||
|
||||
# RUN: rm -rf %t.repro
|
||||
# RUN: %lldb -x -b --capture --capture-path %t.repro -o 'script lldb.debugger.SetAsync(True)' -o 'script lldb.debugger.GetAsync()' -o 'reproducer generate' | FileCheck %s --check-prefix CAPTURE
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix REPLAY
|
||||
|
||||
# CAPTURE: script lldb.debugger.SetAsync(True)
|
||||
# CAPTURE-NEXT: script lldb.debugger.GetAsync()
|
||||
# CAPTURE-NEXT: True
|
||||
|
||||
# REPLAY: script lldb.debugger.SetAsync(True)
|
||||
# REPLAY-NEXT: script lldb.debugger.GetAsync()
|
||||
# REPLAY-NEXT: False
|
||||
@@ -2,7 +2,6 @@
|
||||
# RUN: rm -rf %t.repro2
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
|
||||
# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture --capture-path %t.repro %t.out
|
||||
# RUN: %lldb --replay %t.repro
|
||||
|
||||
# RUN: echo "/bogus/home/dir" > %t.repro/home.txt
|
||||
# RUN: echo "/bogus/current/working/dir" > %t.repro/cwd.txt
|
||||
@@ -14,14 +13,3 @@
|
||||
# RUN: rm %t.repro/root/%S/Inputs/GDBRemoteCapture.in
|
||||
# RUN: echo "CHECK: '%S/Inputs/GDBRemoteCapture.in': No such file or directory" > %t.check
|
||||
# RUN: not %lldb -b -o 'reproducer verify -f %t.repro' 2>&1 | FileCheck %t.check
|
||||
|
||||
# RUN: not %lldb --replay %t.repro 2>&1 | FileCheck %s
|
||||
|
||||
# At this point the reproducer is too broken to ignore the verification issues.
|
||||
# Capture a new reproducer and only change the home directory, which is
|
||||
# recoverable as far as this test goes.
|
||||
|
||||
# RUN: %lldb -x -b -s %S/Inputs/GDBRemoteCapture.in --capture --capture-path %t.repro2 %t.out
|
||||
# RUN: echo "/bogus/home/dir" > %t.repro2/home.txt
|
||||
# RUN: %lldb --replay %t.repro2 --reproducer-no-verify 2>&1 | FileCheck %s --check-prefix NO-VERIFY
|
||||
# NO-VERIFY-NOT: home directory '/bogus/home/dir' not in VFS
|
||||
|
||||
@@ -6,24 +6,12 @@
|
||||
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
|
||||
# RUN: %lldb -x -b -s %S/Inputs/FileCapture.in --capture --capture-path %t.repro %t.out | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE
|
||||
|
||||
# Make sure that replay works.
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
|
||||
|
||||
# Change the reproducer version.
|
||||
# RUN: echo "bogus" >> %t.repro/version.txt
|
||||
|
||||
# Make sure that replay works.
|
||||
# RUN: not %lldb --replay %t.repro 2>&1 | FileCheck %s --check-prefix ERROR
|
||||
|
||||
# Make sure that we can circumvent the version check with -reproducer-no-version-check.
|
||||
# RUN: %lldb --replay %t.repro -reproducer-no-version-check | FileCheck %s --check-prefix CHECK --check-prefix REPLAY
|
||||
|
||||
# CAPTURE: testing
|
||||
# REPLAY-NOT: testing
|
||||
|
||||
# CHECK: Process {{.*}} exited
|
||||
|
||||
# CAPTURE: Reproducer is in capture mode.
|
||||
# CAPTURE: Reproducer written
|
||||
|
||||
# ERROR: error: reproducer replay failed: reproducer capture and replay version don't match
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# This tests that the reproducer can deal with relative files. We create a
|
||||
# binary in a subdirectory and pass its relative path to LLDB. The subdirectory
|
||||
# is removed before replay so that it only exists in the reproducer's VFS.
|
||||
# binary in a subdirectory and pass its relative path to LLDB.
|
||||
|
||||
# RUN: echo "CHECK: %t" > %t.check
|
||||
|
||||
@@ -14,7 +13,6 @@
|
||||
# RUN: rm -rf %t/binary
|
||||
|
||||
# RUN: cat %t.repro/cwd.txt | FileCheck %t.check
|
||||
# RUN: %lldb --replay %t.repro | FileCheck %t.check
|
||||
|
||||
# Make sure the current working directory is recorded even when it's not
|
||||
# referenced.
|
||||
|
||||
@@ -736,18 +736,6 @@ EXAMPLES:
|
||||
|
||||
static llvm::Optional<int> InitializeReproducer(llvm::StringRef argv0,
|
||||
opt::InputArgList &input_args) {
|
||||
if (auto *replay_path = input_args.getLastArg(OPT_replay)) {
|
||||
SBReplayOptions replay_options;
|
||||
replay_options.SetCheckVersion(!input_args.hasArg(OPT_no_version_check));
|
||||
replay_options.SetVerify(!input_args.hasArg(OPT_no_verification));
|
||||
if (const char *error =
|
||||
SBReproducer::Replay(replay_path->getValue(), replay_options)) {
|
||||
WithColor::error() << "reproducer replay failed: " << error << '\n';
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool capture = input_args.hasArg(OPT_capture);
|
||||
bool generate_on_exit = input_args.hasArg(OPT_generate_on_exit);
|
||||
auto *capture_path = input_args.getLastArg(OPT_capture_path);
|
||||
|
||||
@@ -233,15 +233,6 @@ def capture: F<"capture">,
|
||||
def capture_path: Separate<["--", "-"], "capture-path">,
|
||||
MetaVarName<"<filename>">,
|
||||
HelpText<"Tells the debugger to use the given filename for the reproducer.">;
|
||||
def replay: Separate<["--", "-"], "replay">,
|
||||
MetaVarName<"<filename>">,
|
||||
HelpText<"Tells the debugger to replay a reproducer from <filename>.">;
|
||||
def no_version_check: F<"reproducer-no-version-check">,
|
||||
HelpText<"Disable the reproducer version check.">;
|
||||
def no_verification: F<"reproducer-no-verify">,
|
||||
HelpText<"Disable the reproducer verification.">;
|
||||
def no_generate_on_signal: F<"reproducer-no-generate-on-signal">,
|
||||
HelpText<"Don't generate reproducer when a signal is received.">;
|
||||
def generate_on_exit: F<"reproducer-generate-on-exit">,
|
||||
HelpText<"Generate reproducer on exit.">;
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ public:
|
||||
DummyReproducer() : Reproducer(){};
|
||||
|
||||
using Reproducer::SetCapture;
|
||||
using Reproducer::SetReplay;
|
||||
};
|
||||
|
||||
struct YamlData {
|
||||
@@ -98,9 +97,6 @@ TEST(ReproducerTest, SetCapture) {
|
||||
reproducer.GetReproducerPath());
|
||||
|
||||
// Ensure that we cannot enable replay.
|
||||
EXPECT_THAT_ERROR(
|
||||
reproducer.SetReplay(FileSpec("//bogus/path", FileSpec::Style::posix)),
|
||||
Failed());
|
||||
EXPECT_EQ(nullptr, reproducer.GetLoader());
|
||||
|
||||
// Ensure we can disable the generator again.
|
||||
@@ -109,33 +105,6 @@ TEST(ReproducerTest, SetCapture) {
|
||||
EXPECT_EQ(nullptr, reproducer.GetLoader());
|
||||
}
|
||||
|
||||
TEST(ReproducerTest, SetReplay) {
|
||||
DummyReproducer reproducer;
|
||||
|
||||
// Initially both generator and loader are unset.
|
||||
EXPECT_EQ(nullptr, reproducer.GetGenerator());
|
||||
EXPECT_EQ(nullptr, reproducer.GetLoader());
|
||||
|
||||
// Expected to fail because we can't load the index.
|
||||
EXPECT_THAT_ERROR(
|
||||
reproducer.SetReplay(FileSpec("//bogus/path", FileSpec::Style::posix)),
|
||||
Failed());
|
||||
// However the loader should still be set, which we check here.
|
||||
EXPECT_NE(nullptr, reproducer.GetLoader());
|
||||
|
||||
// Make sure the bogus path is correctly set.
|
||||
EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
|
||||
reproducer.GetLoader()->GetRoot());
|
||||
EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
|
||||
reproducer.GetReproducerPath());
|
||||
|
||||
// Ensure that we cannot enable replay.
|
||||
EXPECT_THAT_ERROR(
|
||||
reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
|
||||
Failed());
|
||||
EXPECT_EQ(nullptr, reproducer.GetGenerator());
|
||||
}
|
||||
|
||||
TEST(GeneratorTest, Create) {
|
||||
DummyReproducer reproducer;
|
||||
|
||||
@@ -214,49 +183,4 @@ TEST(GeneratorTest, YamlMultiProvider) {
|
||||
|
||||
generator.Keep();
|
||||
}
|
||||
|
||||
{
|
||||
DummyReproducer reproducer;
|
||||
EXPECT_THAT_ERROR(reproducer.SetReplay(FileSpec(root.str())), Succeeded());
|
||||
|
||||
auto &loader = *reproducer.GetLoader();
|
||||
std::unique_ptr<repro::MultiLoader<YamlMultiProvider>> multi_loader =
|
||||
repro::MultiLoader<YamlMultiProvider>::Create(&loader);
|
||||
|
||||
// Read the first file.
|
||||
{
|
||||
llvm::Optional<std::string> file = multi_loader->GetNextFile();
|
||||
EXPECT_TRUE(static_cast<bool>(file));
|
||||
|
||||
auto buffer = llvm::MemoryBuffer::getFile(*file);
|
||||
EXPECT_TRUE(static_cast<bool>(buffer));
|
||||
|
||||
yaml::Input yin((*buffer)->getBuffer());
|
||||
std::vector<YamlData> data;
|
||||
yin >> data;
|
||||
|
||||
ASSERT_EQ(data.size(), 2U);
|
||||
EXPECT_THAT(data, testing::ElementsAre(data0, data1));
|
||||
}
|
||||
|
||||
// Read the second file.
|
||||
{
|
||||
llvm::Optional<std::string> file = multi_loader->GetNextFile();
|
||||
EXPECT_TRUE(static_cast<bool>(file));
|
||||
|
||||
auto buffer = llvm::MemoryBuffer::getFile(*file);
|
||||
EXPECT_TRUE(static_cast<bool>(buffer));
|
||||
|
||||
yaml::Input yin((*buffer)->getBuffer());
|
||||
std::vector<YamlData> data;
|
||||
yin >> data;
|
||||
|
||||
ASSERT_EQ(data.size(), 2U);
|
||||
EXPECT_THAT(data, testing::ElementsAre(data2, data3));
|
||||
}
|
||||
|
||||
// There is no third file.
|
||||
llvm::Optional<std::string> file = multi_loader->GetNextFile();
|
||||
EXPECT_FALSE(static_cast<bool>(file));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user