blob: e2e67fd42ffa8e5d0980d982ead63a308d06e881 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/client/until_thread_controller.h"
#include "src/developer/debug/zxdb/client/inline_thread_controller_test.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
namespace zxdb {
// This test doesn't test any inline functionality but it's convenient to re-use that class'
// default stack setup.
using UntilThreadControllerTest = InlineThreadControllerTest;
namespace {
// Wrapper around UntilThreadController that reports when it's destroyed.
class TrackedUntilThreadController : public UntilThreadController {
public:
TrackedUntilThreadController(bool* destroyed_flag, std::vector<InputLocation> locations)
: UntilThreadController(std::move(locations)), destroyed_flag_(destroyed_flag) {}
~TrackedUntilThreadController() { *destroyed_flag_ = true; }
private:
bool* destroyed_flag_;
};
} // namespace
TEST_F(UntilThreadControllerTest, Basic) {
// Report a stop at the source address.
auto mock_frames = GetStack();
uint64_t start_address = mock_frames[0]->GetAddress();
InjectExceptionWithStack(process()->GetKoid(), thread()->GetKoid(),
debug_ipc::ExceptionType::kSingleStep,
MockFrameVectorToFrameVector(std::move(mock_frames)), true);
bool controller_destroyed = false;
// Continue "until" an address a few bytes later. Normally this completes asynchronously (it
// blocks on successful breakpoint set) so we need to run the message loop.
bool init_complete = false;
uint64_t dest_address = start_address + 32;
thread()->ContinueWith(
std::make_unique<TrackedUntilThreadController>(
&controller_destroyed, std::vector<InputLocation>{InputLocation(dest_address)}),
[&init_complete](const Err& err) {
init_complete = true;
debug::MessageLoop::Current()->QuitNow();
});
loop().RunUntilNoTasks();
EXPECT_TRUE(init_complete);
EXPECT_FALSE(controller_destroyed);
// The thread should have continued.
EXPECT_EQ(1, mock_remote_api()->GetAndResetResumeCount());
// Report a software breakpoint stop at the target address. The breakpoint must correspond to
// the one the controller set.
debug_ipc::BreakpointStats breakpoint{
.id = static_cast<uint32_t>(mock_remote_api()->last_breakpoint_id()), .hit_count = 1};
mock_frames = GetStack();
mock_frames[0]->SetAddress(dest_address);
InjectExceptionWithStack(
process()->GetKoid(), thread()->GetKoid(), debug_ipc::ExceptionType::kSoftwareBreakpoint,
MockFrameVectorToFrameVector(std::move(mock_frames)), true, {breakpoint});
// Should not have continued and the controller should be done.
EXPECT_EQ(0, mock_remote_api()->GetAndResetResumeCount()); // Stop.
EXPECT_TRUE(controller_destroyed);
}
} // namespace zxdb