blob: b99a16022f1f2bdc3923fb6fa51bfe794eadd32b [file] [log] [blame]
// Copyright 2021 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/debug_adapter/handlers/request_stacktrace.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/client/mock_frame.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/common/scoped_temp_file.h"
#include "src/developer/debug/zxdb/debug_adapter/context_test.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
namespace zxdb {
namespace {
using RequestStackTraceTest = DebugAdapterContextTest;
} // namespace
TEST_F(RequestStackTraceTest, FullFrameAvailable) {
InitializeDebugging();
InjectProcess(kProcessKoid);
// Run client to receive process started event.
RunClient();
auto thread = InjectThread(kProcessKoid, kThreadKoid);
// Run client to receive threads started event.
RunClient();
// Insert mock frames
// Top frame has a valid source location
ScopedTempFile temp_file;
fxl::RefPtr<Function> function1(fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram));
function1->set_assigned_name("test_func1");
function1->set_code_ranges(AddressRanges(AddressRange(0x10000, 0x10020)));
auto location1 = Location(0x10010, FileLine(temp_file.name(), 23), 10,
SymbolContext::ForRelativeAddresses(), function1);
// The source of this frame cannot be found and will not be reported in response.
fxl::RefPtr<Function> function2(fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram));
function2->set_assigned_name("test_func2");
function2->set_code_ranges(AddressRanges(AddressRange(0x10024, 0x10060)));
auto location2 = Location(0x10040, FileLine("not_found.cc", 55), 12,
SymbolContext::ForRelativeAddresses(), function2);
std::vector<std::unique_ptr<Frame>> frames;
frames.push_back(std::make_unique<MockFrame>(&session(), thread, location1, 0x2000));
frames.push_back(std::make_unique<MockFrame>(&session(), thread, location2, 0x2020));
InjectExceptionWithStack(kProcessKoid, kThreadKoid, debug_ipc::ExceptionType::kSingleStep,
std::move(frames), true);
// Receive thread stopped event in client.
RunClient();
// Send request from the client.
dap::StackTraceRequest request = {};
request.threadId = kThreadKoid;
auto response = client().send(request);
// Read request and process it in server.
context().OnStreamReadable();
loop().RunUntilNoTasks();
// Run client to receive response.
RunClient();
auto got = response.get();
EXPECT_FALSE(got.error);
EXPECT_EQ(got.response.totalFrames.value(), 2);
EXPECT_EQ(got.response.stackFrames[0].column, location1.column());
EXPECT_EQ(got.response.stackFrames[0].line, location1.file_line().line());
EXPECT_EQ(got.response.stackFrames[0].name, function1->GetAssignedName());
EXPECT_EQ(got.response.stackFrames[0].source.value().path.value(), temp_file.name());
EXPECT_EQ(got.response.stackFrames[1].column, location2.column());
EXPECT_EQ(got.response.stackFrames[1].line, location2.file_line().line());
EXPECT_EQ(got.response.stackFrames[1].name, function2->GetAssignedName());
EXPECT_FALSE(got.response.stackFrames[1].source);
}
TEST_F(RequestStackTraceTest, SyncFramesRequired) {
InitializeDebugging();
auto process = InjectProcess(kProcessKoid);
// Run client to receive process started event.
RunClient();
InjectThread(kProcessKoid, kThreadKoid);
// Run client to receive threads started event.
RunClient();
constexpr uint64_t kAddress[] = {0x10010, 0x10040, 0x9000};
constexpr uint64_t kStack[] = {0x3000, 0x3020, 0x3050};
constexpr size_t kStackSize = 3;
// Set up symbol resolution for stack frames.
auto mock_module = InjectMockModule(process);
ScopedTempFile temp_file;
std::vector<fxl::RefPtr<Function>> function;
std::vector<Location> location;
for (size_t i = 0; i < kStackSize; i++) {
function.push_back(fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram));
function[i]->set_assigned_name(std::string("test_func_") + std::to_string(i));
function[i]->set_code_ranges(
AddressRanges(AddressRange(kAddress[i] - 0x10, kAddress[i] + 0x10)));
location.push_back(Location(kAddress[i], FileLine(temp_file.name(), 23 + i), 10 + i,
SymbolContext::ForRelativeAddresses(), function[i]));
mock_module->AddSymbolLocations(kAddress[i], {location[i]});
}
// Notify of thread stop and push expected stack frames.
debug_ipc::NotifyException break_notification;
break_notification.type = debug_ipc::ExceptionType::kSoftwareBreakpoint;
break_notification.thread.id = {.process = kProcessKoid, .thread = kThreadKoid};
break_notification.thread.state = debug_ipc::ThreadRecord::State::kBlocked;
InjectException(break_notification);
debug_ipc::ThreadStatusReply expected_reply;
expected_reply.record = break_notification.thread;
expected_reply.record.stack_amount = debug_ipc::ThreadRecord::StackAmount::kFull;
for (size_t i = 0; i < kStackSize; i++) {
debug_ipc::StackFrame frame(kAddress[i], kStack[i]);
expected_reply.record.frames.push_back(frame);
}
mock_remote_api()->set_thread_status_reply(expected_reply);
// Receive thread stopped event in client.
RunClient();
// Send request from the client.
dap::StackTraceRequest request = {};
request.threadId = kThreadKoid;
auto response = client().send(request);
// Read request and process it in server.
context().OnStreamReadable();
loop().RunUntilNoTasks();
// Run client to receive response.
RunClient();
auto got = response.get();
EXPECT_FALSE(got.error);
EXPECT_EQ(static_cast<size_t>(got.response.totalFrames.value()), kStackSize);
for (size_t i = 0; i < kStackSize; i++) {
EXPECT_EQ(got.response.stackFrames[i].column, location[i].column());
EXPECT_EQ(got.response.stackFrames[i].line, location[i].file_line().line());
EXPECT_EQ(got.response.stackFrames[i].name, function[i]->GetAssignedName());
EXPECT_EQ(got.response.stackFrames[i].source.value().path.value(), temp_file.name());
}
}
} // namespace zxdb