blob: fc3bd6571ec7b95c1ce726e50999d4f61f667dbb [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/process_impl.h"
#include <gtest/gtest.h>
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/client/remote_api_test.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/target_impl.h"
#include "src/developer/debug/zxdb/symbols/elf_symbol.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
namespace zxdb {
namespace {
class ProcessSink : public MockRemoteAPI {
public:
ProcessSink() = default;
~ProcessSink() = default;
const debug_ipc::ResumeRequest& resume_request() const { return resume_request_; }
int resume_count() const { return resume_count_; }
void Resume(const debug_ipc::ResumeRequest& request,
fit::callback<void(const Err&, debug_ipc::ResumeReply)> cb) override {
resume_count_++;
resume_request_ = request;
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb = std::move(cb)]() mutable { cb(Err(), debug_ipc::ResumeReply()); });
}
// No-op.
void Threads(const debug_ipc::ThreadsRequest& request,
fit::callback<void(const Err&, debug_ipc::ThreadsReply)> cb) override {
thread_request_made_ = true;
}
bool thread_request_made() const { return thread_request_made_; }
private:
debug_ipc::ResumeRequest resume_request_;
int resume_count_ = 0;
bool thread_request_made_ = false;
};
class ProcessImplTest : public RemoteAPITest {
public:
ProcessImplTest() = default;
~ProcessImplTest() override = default;
ProcessSink* sink() { return sink_; }
private:
std::unique_ptr<RemoteAPI> GetRemoteAPIImpl() override {
auto sink = std::make_unique<ProcessSink>();
sink_ = sink.get();
return sink;
}
private:
ProcessSink* sink_; // Owned by the session.
};
} // namespace
// Tests that the correct threads are resumed after the modules are loaded.
TEST_F(ProcessImplTest, OnModules) {
constexpr uint64_t kProcessKoid = 1234;
Process* process = InjectProcess(kProcessKoid);
ASSERT_TRUE(process);
EXPECT_FALSE(sink()->thread_request_made());
debug_ipc::NotifyModules notify;
notify.process_koid = kProcessKoid;
notify.modules.resize(1);
notify.modules[0].name = "comctl32.dll";
notify.modules[0].base = 0x7685348234;
constexpr uint64_t kThread1Koid = 237645;
constexpr uint64_t kThread2Koid = 809712;
notify.stopped_thread_koids.push_back(kThread1Koid);
notify.stopped_thread_koids.push_back(kThread2Koid);
session().DispatchNotifyModules(notify);
EXPECT_TRUE(sink()->thread_request_made());
// Should have resumed both of those threads.
ASSERT_EQ(1, sink()->resume_count());
const debug_ipc::ResumeRequest& resume = sink()->resume_request();
EXPECT_EQ(kProcessKoid, resume.process_koid);
EXPECT_EQ(debug_ipc::ResumeRequest::How::kResolveAndContinue, resume.how);
EXPECT_EQ(notify.stopped_thread_koids, resume.thread_koids);
}
TEST_F(ProcessImplTest, GetTLSHelpers) {
constexpr uint64_t kProcessKoid = 1234;
constexpr uint64_t kThreadKoid = 5678;
constexpr char kBuildId[] = "abcd";
constexpr uint64_t kModuleBase = 0x1000;
Process* process = InjectProcess(kProcessKoid);
ASSERT_TRUE(process);
Thread* thread = InjectThread(kProcessKoid, kThreadKoid);
ASSERT_TRUE(thread);
SymbolContext symbol_context(kModuleBase);
auto module_symbols = fxl::MakeRefCounted<MockModuleSymbols>("libc.so");
session().system().GetSymbols()->InjectModuleForTesting(kBuildId, module_symbols.get());
std::vector<debug_ipc::Module> modules;
debug_ipc::Module module;
module.name = "libc";
module.base = kModuleBase;
module.build_id = kBuildId;
modules.push_back(module);
TargetImpl* target = session().system().GetTargetImpls()[0];
target->process()->OnModules(modules, std::vector<uint64_t>());
constexpr uint64_t kThrdTAddr = 0x1000;
constexpr uint64_t kLinkMapTlsModIdAddr = 0x2000;
constexpr uint64_t kTlsBaseAddr = 0x3000;
constexpr uint64_t kSymSize = 4;
ElfSymbolRecord thrd_t_record(ElfSymbolType::kNormal, kThrdTAddr, kSymSize, "zxdb.thrd_t");
ElfSymbolRecord link_map_tls_modid_record(ElfSymbolType::kNormal, kLinkMapTlsModIdAddr, kSymSize,
"zxdb.link_map_tls_modid");
ElfSymbolRecord tlsbase_record(ElfSymbolType::kNormal, kTlsBaseAddr, kSymSize, "zxdb.tlsbase");
auto thrd_t_sym = fxl::MakeRefCounted<ElfSymbol>(module_symbols->GetWeakPtr(), thrd_t_record);
auto link_map_tls_modid_sym =
fxl::MakeRefCounted<ElfSymbol>(module_symbols->GetWeakPtr(), link_map_tls_modid_record);
auto tlsbase_sym = fxl::MakeRefCounted<ElfSymbol>(module_symbols->GetWeakPtr(), tlsbase_record);
module_symbols->AddSymbolLocations("zxdb.thrd_t", {Location(kThrdTAddr + kModuleBase, FileLine(),
0, symbol_context, thrd_t_sym)});
module_symbols->AddSymbolLocations("zxdb.link_map_tls_modid",
{Location(kLinkMapTlsModIdAddr + kModuleBase, FileLine(), 0,
symbol_context, link_map_tls_modid_sym)});
module_symbols->AddSymbolLocations(
"zxdb.tlsbase",
{Location(kTlsBaseAddr + kModuleBase, FileLine(), 0, symbol_context, tlsbase_sym)});
sink()->AddMemory(kThrdTAddr + kModuleBase, {0x1, 0x2, 0x3, 0x4});
sink()->AddMemory(kLinkMapTlsModIdAddr + kModuleBase, {0x5, 0x6, 0x7, 0x8});
sink()->AddMemory(kTlsBaseAddr + kModuleBase, {0x9, 0xa, 0xb, 0xc});
bool called = false;
bool called_2 = false;
Process::TLSHelpers helpers;
Process::TLSHelpers helpers_2;
Err err;
Err err_2;
process->GetTLSHelpers([&called, &helpers, &err](ErrOr<const Process::TLSHelpers*> got) {
called = true;
if (got.has_error()) {
err = got.err();
} else {
helpers = *got.value();
}
});
process->GetTLSHelpers([&called_2, &helpers_2, &err_2](ErrOr<const Process::TLSHelpers*> got) {
called_2 = true;
if (got.has_error()) {
err_2 = got.err();
} else {
helpers_2 = *got.value();
}
});
EXPECT_FALSE(called);
EXPECT_FALSE(called_2);
loop().RunUntilNoTasks();
EXPECT_TRUE(called);
EXPECT_TRUE(called_2);
ASSERT_FALSE(err.has_error()) << err.msg();
EXPECT_FALSE(err_2.has_error());
ASSERT_EQ(helpers.thrd_t.size(), kSymSize);
ASSERT_EQ(helpers.link_map_tls_modid.size(), kSymSize);
ASSERT_EQ(helpers.tlsbase.size(), kSymSize);
EXPECT_EQ(0x1, helpers.thrd_t[0]);
EXPECT_EQ(0x2, helpers.thrd_t[1]);
EXPECT_EQ(0x3, helpers.thrd_t[2]);
EXPECT_EQ(0x4, helpers.thrd_t[3]);
EXPECT_EQ(0x5, helpers.link_map_tls_modid[0]);
EXPECT_EQ(0x6, helpers.link_map_tls_modid[1]);
EXPECT_EQ(0x7, helpers.link_map_tls_modid[2]);
EXPECT_EQ(0x8, helpers.link_map_tls_modid[3]);
EXPECT_EQ(0x9, helpers.tlsbase[0]);
EXPECT_EQ(0xa, helpers.tlsbase[1]);
EXPECT_EQ(0xb, helpers.tlsbase[2]);
EXPECT_EQ(0xc, helpers.tlsbase[3]);
EXPECT_EQ(0x1, helpers_2.thrd_t[0]);
EXPECT_EQ(0x2, helpers_2.thrd_t[1]);
EXPECT_EQ(0x3, helpers_2.thrd_t[2]);
EXPECT_EQ(0x4, helpers_2.thrd_t[3]);
EXPECT_EQ(0x5, helpers_2.link_map_tls_modid[0]);
EXPECT_EQ(0x6, helpers_2.link_map_tls_modid[1]);
EXPECT_EQ(0x7, helpers_2.link_map_tls_modid[2]);
EXPECT_EQ(0x8, helpers_2.link_map_tls_modid[3]);
EXPECT_EQ(0x9, helpers_2.tlsbase[0]);
EXPECT_EQ(0xa, helpers_2.tlsbase[1]);
EXPECT_EQ(0xb, helpers_2.tlsbase[2]);
EXPECT_EQ(0xc, helpers_2.tlsbase[3]);
called = false;
process->GetTLSHelpers([&called, &helpers, &err](ErrOr<const Process::TLSHelpers*> got) {
called = true;
if (got.has_error()) {
err = got.err();
} else {
helpers = *got.value();
}
});
EXPECT_TRUE(called);
ASSERT_FALSE(err.has_error()) << err.msg();
ASSERT_EQ(helpers.thrd_t.size(), kSymSize);
ASSERT_EQ(helpers.link_map_tls_modid.size(), kSymSize);
ASSERT_EQ(helpers.tlsbase.size(), kSymSize);
EXPECT_EQ(0x1, helpers.thrd_t[0]);
EXPECT_EQ(0x2, helpers.thrd_t[1]);
EXPECT_EQ(0x3, helpers.thrd_t[2]);
EXPECT_EQ(0x4, helpers.thrd_t[3]);
EXPECT_EQ(0x5, helpers.link_map_tls_modid[0]);
EXPECT_EQ(0x6, helpers.link_map_tls_modid[1]);
EXPECT_EQ(0x7, helpers.link_map_tls_modid[2]);
EXPECT_EQ(0x8, helpers.link_map_tls_modid[3]);
EXPECT_EQ(0x9, helpers.tlsbase[0]);
EXPECT_EQ(0xa, helpers.tlsbase[1]);
EXPECT_EQ(0xb, helpers.tlsbase[2]);
EXPECT_EQ(0xc, helpers.tlsbase[3]);
}
} // namespace zxdb