
Encountered the following situation: Let we started thread T1 and it hit breakpoint on B1 location. We suspended T1 and continued the process. Then we started thread T2 which hit for example the same location B1. This time in a breakpoint callback we decided not to stop returning false. Expected result: process continues (as if T2 did not hit breakpoint) its workflow with T1 still suspended. Actual result: process do stops (as if T2 callback returned true). Solution: We need invalidate StopInfo for threads that was previously suspended just because something that is already inactive can not be the reason of stop. Thread::GetPrivateStopInfo() may be appropriate place to do it, because it gets called (through Thread::GetStopInfo()) every time before process reports stop and user gets chance to change m_resume_state again i.e if we see m_resume_state == eStateSuspended it definitely means it was set during previous stop and it also means this thread can not be stopped again (cos' it was frozen during previous stop). Differential revision: https://reviews.llvm.org/D80112
169 lines
4.9 KiB
C++
169 lines
4.9 KiB
C++
//===-- ThreadTest.cpp ------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Target/Thread.h"
|
|
#include "Plugins/Platform/Linux/PlatformLinux.h"
|
|
#include "lldb/Core/Debugger.h"
|
|
#include "lldb/Host/FileSystem.h"
|
|
#include "lldb/Host/HostInfo.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StopInfo.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/Reproducer.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::repro;
|
|
using namespace lldb;
|
|
|
|
namespace {
|
|
class ThreadTest : public ::testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None));
|
|
FileSystem::Initialize();
|
|
HostInfo::Initialize();
|
|
platform_linux::PlatformLinux::Initialize();
|
|
}
|
|
void TearDown() override {
|
|
platform_linux::PlatformLinux::Terminate();
|
|
HostInfo::Terminate();
|
|
FileSystem::Terminate();
|
|
Reproducer::Terminate();
|
|
}
|
|
};
|
|
|
|
class DummyProcess : public Process {
|
|
public:
|
|
using Process::Process;
|
|
|
|
virtual bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) {
|
|
return true;
|
|
}
|
|
virtual Status DoDestroy() { return {}; }
|
|
virtual void RefreshStateAfterStop() {}
|
|
virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
|
Status &error) {
|
|
return 0;
|
|
}
|
|
virtual bool UpdateThreadList(ThreadList &old_thread_list,
|
|
ThreadList &new_thread_list) {
|
|
return false;
|
|
}
|
|
virtual ConstString GetPluginName() { return ConstString("Dummy"); }
|
|
virtual uint32_t GetPluginVersion() { return 0; }
|
|
|
|
ProcessModID &GetModIDNonConstRef() { return m_mod_id; }
|
|
};
|
|
|
|
class DummyThread : public Thread {
|
|
public:
|
|
using Thread::Thread;
|
|
|
|
~DummyThread() override { DestroyThread(); }
|
|
|
|
void RefreshStateAfterStop() override {}
|
|
|
|
lldb::RegisterContextSP GetRegisterContext() override { return nullptr; }
|
|
|
|
lldb::RegisterContextSP
|
|
CreateRegisterContextForFrame(StackFrame *frame) override {
|
|
return nullptr;
|
|
}
|
|
|
|
bool CalculateStopInfo() override { return false; }
|
|
|
|
bool IsStillAtLastBreakpointHit() override { return true; }
|
|
};
|
|
} // namespace
|
|
|
|
TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) {
|
|
Status error;
|
|
PlatformSP platform_sp;
|
|
TargetSP target_sp;
|
|
error = debugger_sp->GetTargetList().CreateTarget(
|
|
*debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
|
|
|
|
if (target_sp) {
|
|
debugger_sp->GetTargetList().SetSelectedTarget(target_sp.get());
|
|
}
|
|
|
|
return target_sp;
|
|
}
|
|
|
|
TEST_F(ThreadTest, SetStopInfo) {
|
|
ArchSpec arch("powerpc64-pc-linux");
|
|
|
|
Platform::SetHostPlatform(
|
|
platform_linux::PlatformLinux::CreateInstance(true, &arch));
|
|
|
|
DebuggerSP debugger_sp = Debugger::CreateInstance();
|
|
ASSERT_TRUE(debugger_sp);
|
|
|
|
TargetSP target_sp = CreateTarget(debugger_sp, arch);
|
|
ASSERT_TRUE(target_sp);
|
|
|
|
ListenerSP listener_sp(Listener::MakeListener("dummy"));
|
|
ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp);
|
|
ASSERT_TRUE(process_sp);
|
|
|
|
DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());
|
|
|
|
ThreadSP thread_sp = std::make_shared<DummyThread>(*process_sp.get(), 0);
|
|
ASSERT_TRUE(thread_sp);
|
|
|
|
StopInfoSP stopinfo_sp =
|
|
StopInfo::CreateStopReasonWithBreakpointSiteID(*thread_sp.get(), 0);
|
|
ASSERT_TRUE(stopinfo_sp->IsValid() == true);
|
|
|
|
/*
|
|
Should make stopinfo valid.
|
|
*/
|
|
process->GetModIDNonConstRef().BumpStopID();
|
|
ASSERT_TRUE(stopinfo_sp->IsValid() == false);
|
|
|
|
thread_sp->SetStopInfo(stopinfo_sp);
|
|
ASSERT_TRUE(stopinfo_sp->IsValid() == true);
|
|
}
|
|
|
|
TEST_F(ThreadTest, GetPrivateStopInfo) {
|
|
ArchSpec arch("powerpc64-pc-linux");
|
|
|
|
Platform::SetHostPlatform(
|
|
platform_linux::PlatformLinux::CreateInstance(true, &arch));
|
|
|
|
DebuggerSP debugger_sp = Debugger::CreateInstance();
|
|
ASSERT_TRUE(debugger_sp);
|
|
|
|
TargetSP target_sp = CreateTarget(debugger_sp, arch);
|
|
ASSERT_TRUE(target_sp);
|
|
|
|
ListenerSP listener_sp(Listener::MakeListener("dummy"));
|
|
ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp);
|
|
ASSERT_TRUE(process_sp);
|
|
|
|
DummyProcess *process = static_cast<DummyProcess *>(process_sp.get());
|
|
|
|
ThreadSP thread_sp = std::make_shared<DummyThread>(*process_sp.get(), 0);
|
|
ASSERT_TRUE(thread_sp);
|
|
|
|
StopInfoSP stopinfo_sp =
|
|
StopInfo::CreateStopReasonWithBreakpointSiteID(*thread_sp.get(), 0);
|
|
ASSERT_TRUE(stopinfo_sp);
|
|
|
|
thread_sp->SetStopInfo(stopinfo_sp);
|
|
|
|
/*
|
|
Should make stopinfo valid if thread is at last breakpoint hit.
|
|
*/
|
|
process->GetModIDNonConstRef().BumpStopID();
|
|
ASSERT_TRUE(stopinfo_sp->IsValid() == false);
|
|
StopInfoSP new_stopinfo_sp = thread_sp->GetPrivateStopInfo();
|
|
ASSERT_TRUE(new_stopinfo_sp && stopinfo_sp->IsValid() == true);
|
|
}
|