blob: 703bc88f5980a9a40f0fae7537684835a9ad4184 [file] [log] [blame]
// Copyright 2022 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 <atomic>
#include <gtest/gtest.h>
#include "src/developer/debug/e2e_tests/e2e_test.h"
#include "src/developer/debug/ipc/records.h"
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/zxdb/client/breakpoint.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/target.h"
#include "src/developer/debug/zxdb/client/thread.h"
#include "src/developer/debug/zxdb/console/command_sequence.h"
#include "src/developer/debug/zxdb/console/mock_console.h"
namespace zxdb {
namespace {
// This is a very simple test to ensure basic functionality of commonly used commands. This
// simulates a user setting a breakpoint at a well-known symbol (a function name in this case),
// then running the program with the "run-component" command. The test ensures that the breakpoint
// is eventually matched when symbols are loaded, and then that the breakpoint was hit before
// killing the program.
class RunAndKillProcess : public E2eTest {
public:
void Run() {
console().ProcessInputLine(
"break blind_write",
fxl::MakeRefCounted<ConsoleCommandContext>(&console(), [this](const Err& e) {
// Make sure the breakpoint was added, but hasn't been resolved to a location yet.
EXPECT_TRUE(e.ok()) << e.msg();
auto bps = this->session().system().GetBreakpoints();
ASSERT_EQ(bps.size(), 1u);
ASSERT_NE(bps[0], nullptr);
EXPECT_EQ(this->console().context().GetActiveBreakpoint(), bps[0]);
EXPECT_EQ(bps[0]->GetSettings().scope.type(), ExecutionScope::kSystem);
EXPECT_EQ(bps[0]->GetSettings().stop_mode, BreakpointSettings::StopMode::kAll);
// Shouldn't have any resolved locations yet, because symbols haven't been loaded yet.
EXPECT_EQ(bps[0]->GetLocations().size(), 0u);
}));
console().ProcessInputLine(
"run-component fuchsia-pkg://fuchsia.com/crasher#meta/cpp_crasher.cm");
// Kick off the MessageLoop, we should catch the process starting in the above observer
// implementations.
loop().Run();
}
void DidCreateProcess(Process* process, uint64_t timestamp) override {
FX_LOGS(INFO) << "DidCreateProcess";
EXPECT_EQ(process->GetName(), "cpp_crasher.cm");
}
// BreakpointObserver implementation. This observer method should be called first.
void OnBreakpointMatched(Breakpoint* breakpoint, bool user_requested) override {
FX_LOGS(INFO) << "OnBreakpointMatched";
ASSERT_NE(breakpoint, nullptr);
auto threads = console().context().GetActiveTarget()->GetProcess()->GetThreads();
ASSERT_GT(threads.size(), 0u);
auto current_bp = console().context().GetActiveBreakpoint();
// Should be the same breakpoint that we just installed.
EXPECT_EQ(current_bp->GetStats().id, breakpoint->GetStats().id);
// This breakpoint should match something (could be more than 1) now.
EXPECT_GE(breakpoint->GetLocations().size(), 1u);
// Because the breakpoint wasn't matched at the time the user issued the "break" command, this
// is not considered user requested.
EXPECT_FALSE(user_requested);
// We shouldn't have hit the breakpoint yet since this notification is dispatched when the
// breakpoint actually matches a symbol.
EXPECT_EQ(breakpoint->GetStats().hit_count, 0u);
}
// ThreadObserver implementation.
void OnThreadStopped(Thread* thread, const StopInfo& info) override {
FX_LOGS(INFO) << "OnThreadStopped";
ASSERT_NE(thread, nullptr);
// We should have hit our breakpoint.
EXPECT_EQ(info.exception_type, debug_ipc::ExceptionType::kSoftwareBreakpoint);
auto target = console().context().GetActiveTarget();
auto active_thread = console().context().GetActiveThreadForTarget(target);
EXPECT_EQ(active_thread->GetKoid(), thread->GetKoid());
EXPECT_TRUE(thread->IsBlockedOnException());
EXPECT_EQ(thread->GetBlockedReason(), debug_ipc::ThreadRecord::BlockedReason::kException);
console().ProcessInputLine("frame");
console().ProcessInputLine("kill");
}
// ProcessObserver implementation.
void WillDestroyProcess(Process* process, DestroyReason reason, int exit_code,
uint64_t timestamp) override {
FX_LOGS(INFO) << "WillDestroyProcess";
ASSERT_NE(process, nullptr);
EXPECT_EQ(process, console().context().GetActiveTarget()->GetProcess());
EXPECT_EQ(reason, ProcessObserver::DestroyReason::kKill);
// Quit the MessageLoop to end the test.
debug::MessageLoop::Current()->QuitNow();
}
};
} // namespace
TEST_F(RunAndKillProcess, RunAndKillProcess) { Run(); }
} // namespace zxdb