blob: 117e50c191123295ed1fceb288304be995433975 [file] [log] [blame]
// Copyright 2019 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/step_into_specific_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/symbols/line_details.h"
namespace zxdb {
class StepIntoSpecificThreadControllerTest : public InlineThreadControllerTest {
public:
// Returns the stack with the "middle inline 2" frame at the top. This removes the top and "top
// inline 2" frames from the default mock inline stack.
auto GetStackAtMiddleInline2() const {
auto frames = GetStack();
frames.erase(frames.begin(), frames.begin() + 2);
return frames;
}
};
// For convenience this steps into the same function call twice (this lets us use the mock stack
// from the inline thread controller test). The first call is done from within the range so gets
// stepped over, the second call is the one we step into.
TEST_F(StepIntoSpecificThreadControllerTest, Step) {
// Provide line information for the "top" physical frame which is where we want to stop. Otherwise
// the stepper will continue through unsymbolized functions.
const uint64_t kEndAddress = kTopFunctionRange.begin();
module_symbols()->AddLineDetails(
kEndAddress, LineDetails(kTopFileLine, {LineDetails::LineEntry(kTopFunctionRange)}));
auto mock_frames = GetStackAtMiddleInline2();
// The location we're stepping from is the middle frame.
const uint64_t kFromAddress = mock_frames[0]->GetAddress();
const uint64_t kToAddress = kFromAddress + 8;
AddressRange range(kFromAddress, kToAddress); // Range being stepped over.
// Inject an exception at the top inline of the middle frame. It's about to call the top frame.
InjectExceptionWithStack(process()->GetKoid(), thread()->GetKoid(),
debug_ipc::ExceptionType::kSingleStep,
MockFrameVectorToFrameVector(std::move(mock_frames)), true);
// Step over the range and into the next function.
thread()->ContinueWith(std::make_unique<StepIntoSpecificThreadController>(range),
[](const Err& err) {});
EXPECT_EQ(1, mock_remote_api()->GetAndResetResumeCount()); // Continue.
// Stop in a new stack frame called by the previous execution. It should continue.
mock_frames = GetStack();
mock_frames.erase(mock_frames.begin()); // Delete top inline to leave us at top (we don't need
// the top inline for this test).
InjectExceptionWithStack(process()->GetKoid(), thread()->GetKoid(),
debug_ipc::ExceptionType::kSingleStep,
MockFrameVectorToFrameVector(std::move(mock_frames)), true);
EXPECT_EQ(1, mock_remote_api()->GetAndResetResumeCount()); // Continue.
// Execution returns to the original "middle" frame at the next instruction. This is a software
// breakpoint set by the "until" controller.
debug_ipc::BreakpointStats breakpoint{
.id = static_cast<uint32_t>(mock_remote_api()->last_breakpoint_id()), .hit_count = 1};
mock_frames = GetStackAtMiddleInline2();
mock_frames[0]->SetAddress(kFromAddress + 1); // Set to next instruction.
InjectExceptionWithStack(
process()->GetKoid(), thread()->GetKoid(), debug_ipc::ExceptionType::kSingleStep,
MockFrameVectorToFrameVector(std::move(mock_frames)), true, {breakpoint});
EXPECT_EQ(1, mock_remote_api()->GetAndResetResumeCount()); // Continue.
// Now exit the range.
mock_frames = GetStackAtMiddleInline2();
mock_frames[0]->SetAddress(kToAddress); // End of range (is non-inclusive).
InjectExceptionWithStack(process()->GetKoid(), thread()->GetKoid(),
debug_ipc::ExceptionType::kSingleStep,
MockFrameVectorToFrameVector(std::move(mock_frames)), true);
EXPECT_EQ(1, mock_remote_api()->GetAndResetResumeCount()); // Continue.
// Step into a new stack frame. Since we exited the range this is the "specific" function being
// stepped into.
mock_frames = GetStack();
mock_frames.erase(mock_frames.begin()); // Delete top inline to leave us at top.
mock_frames[0]->SetAddress(kEndAddress);
InjectExceptionWithStack(process()->GetKoid(), thread()->GetKoid(),
debug_ipc::ExceptionType::kSingleStep,
MockFrameVectorToFrameVector(std::move(mock_frames)), true);
EXPECT_EQ(0, mock_remote_api()->GetAndResetResumeCount()); // Stop.
}
} // namespace zxdb