blob: 005057dc3835aec9beaa565c86a6a750a9f8f9a7 [file] [log] [blame]
// Copyright 2020 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 <gtest/gtest.h>
#include "src/developer/debug/debug_agent/integration_tests/message_loop_wrapper.h"
#include "src/developer/debug/debug_agent/integration_tests/so_wrapper.h"
#include "src/developer/debug/debug_agent/local_stream_backend.h"
#include "src/developer/debug/debug_agent/zircon_system_interface.h"
#include "src/developer/debug/shared/zx_status.h"
namespace debug_agent {
namespace {
const char kSoName[] = "debug_agent_test_so.so";
class DynamicLoaderStreamBackend : public LocalStreamBackend {
public:
DynamicLoaderStreamBackend(debug_ipc::MessageLoop* loop) : loop_(loop) {}
void set_remote_api(RemoteAPI* remote_api) { remote_api_ = remote_api; }
// LocalStreamBackend implementation:
void HandleNotifyThreadStarting(debug_ipc::NotifyThread thread) override;
void HandleNotifyModules(debug_ipc::NotifyModules modules) override;
void HandleNotifyProcessExiting(debug_ipc::NotifyProcessExiting) override;
private:
void ResumeAll();
bool HasModuleForFile(const debug_ipc::NotifyModules& modules, const char* file_name);
enum class Stage {
kWaitingForThread,
kWaitingForModules,
kWaitingForLoad,
kWaitingForUnload,
kWaitingForExit,
kDone,
};
Stage stage_ = Stage::kWaitingForThread;
debug_ipc::MessageLoop* loop_ = nullptr;
RemoteAPI* remote_api_ = nullptr;
zx_koid_t process_koid_ = 0;
zx_koid_t thread_koid_ = 0;
};
void DynamicLoaderStreamBackend::HandleNotifyThreadStarting(debug_ipc::NotifyThread thread) {
if (stage_ == Stage::kWaitingForThread) {
process_koid_ = thread.record.process_koid;
thread_koid_ = thread.record.thread_koid;
stage_ = Stage::kWaitingForModules;
ResumeAll();
} else {
ADD_FAILURE() << "Got starting message when not expected";
loop_->QuitNow();
}
}
void DynamicLoaderStreamBackend::HandleNotifyModules(debug_ipc::NotifyModules modules) {
if (stage_ == Stage::kWaitingForModules) {
EXPECT_FALSE(HasModuleForFile(modules, kSoName));
stage_ = Stage::kWaitingForLoad;
ResumeAll();
} else if (stage_ == Stage::kWaitingForLoad) {
EXPECT_TRUE(HasModuleForFile(modules, kSoName));
// TODO(bug 58371) our dynamic loader does not implement dlclose() so that we never get a
// notification that dynamic libraries are unloaded. When that's implemented, this corresponding
// code should be used to test the debugger's behavior in that context.
#if 0
stage_ = Stage::kWaitingForUnload;
ResumeAll();
} else if (stage_ == Stage::kWaitingForUnload) {
EXPECT_FALSE(HasModuleForFile(modules, kSoName));
#endif
stage_ = Stage::kWaitingForExit;
ResumeAll();
} else {
ADD_FAILURE() << "Unexpected NotifyModules call.";
loop_->QuitNow();
}
}
void DynamicLoaderStreamBackend::HandleNotifyProcessExiting(debug_ipc::NotifyProcessExiting) {
if (stage_ == Stage::kWaitingForExit) {
stage_ = Stage::kDone;
} else {
ADD_FAILURE() << "Process exited before getting the right notifications.";
}
loop_->QuitNow();
}
void DynamicLoaderStreamBackend::ResumeAll() {
debug_ipc::ResumeRequest resume_request;
debug_ipc::ResumeReply resume_reply;
remote_api_->OnResume(resume_request, &resume_reply);
}
bool DynamicLoaderStreamBackend::HasModuleForFile(const debug_ipc::NotifyModules& modules,
const char* file_name) {
for (const auto& m : modules.modules) {
if (m.name == file_name)
return true;
}
return false;
}
} // namespace
// Tests that dynamic library load and unload events are caught by the debug agent and the proper
// notifications are issued.
TEST(DynamicLoader, LoadUnload) {
MessageLoopWrapper loop_wrapper;
{
auto* loop = loop_wrapper.loop();
DynamicLoaderStreamBackend backend(loop);
DebugAgent agent(std::make_unique<ZirconSystemInterface>());
agent.Connect(&backend.stream());
backend.set_remote_api(&agent);
debug_ipc::LaunchRequest launch_request = {};
launch_request.argv = {"/pkg/bin/load_so_exe"};
launch_request.inferior_type = debug_ipc::InferiorType::kBinary;
debug_ipc::LaunchReply launch_reply;
agent.OnLaunch(launch_request, &launch_reply);
ASSERT_EQ(launch_reply.status, ZX_OK) << debug_ipc::ZxStatusToString(launch_reply.status);
loop->Run();
}
}
} // namespace debug_agent