| // 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. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_DEBUG_AGENT_TEST_DATA_TEST_SUITE_HELPERS_H_ |
| #define SRC_DEVELOPER_DEBUG_DEBUG_AGENT_TEST_DATA_TEST_SUITE_HELPERS_H_ |
| |
| #include <lib/fit/defer.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zx/event.h> |
| #include <lib/zx/exception.h> |
| #include <lib/zx/job.h> |
| #include <lib/zx/port.h> |
| #include <lib/zx/thread.h> |
| #include <unistd.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/debug.h> |
| #include <zircon/syscalls/exception.h> |
| #include <zircon/threads.h> |
| |
| #include <iostream> |
| #include <iterator> |
| #include <mutex> |
| #include <thread> |
| |
| #include "src/lib/files/path.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| constexpr uint32_t kHarnessToThread = ZX_USER_SIGNAL_0; |
| constexpr uint32_t kThreadToHarness = ZX_USER_SIGNAL_1; |
| |
| // How many ms to wait on an timeout. |
| constexpr int kExceptionWaitTimeout = 25; |
| |
| // Thread Test Setup ------------------------------------------------------------------------------- |
| |
| // Control struct for each running test case. |
| struct ThreadSetup { |
| using Function = int (*)(void*); |
| |
| ~ThreadSetup(); |
| |
| zx::event event; |
| zx::thread thread; |
| thrd_t c_thread; |
| |
| std::atomic<bool> test_running = false; |
| void* user = nullptr; |
| }; |
| // |user| is a opaque pointer to specific user data to be passed on to the test. |
| // Should be stable in memory throughout the test. |
| std::unique_ptr<ThreadSetup> CreateTestSetup(ThreadSetup::Function func, void* user = nullptr); |
| |
| // Exception Management ---------------------------------------------------------------------------- |
| |
| struct Exception { |
| zx::process process; |
| zx::thread thread; |
| |
| zx::exception handle; |
| zx_exception_info_t info; |
| zx_thread_state_general_regs_t regs; |
| uint64_t pc = 0; |
| }; |
| Exception GetException(const zx::channel& exception_channel); |
| void ResumeException(const zx::thread& thread, Exception&& exception, bool handled = true); |
| |
| bool IsOnException(const zx::thread& thread); |
| |
| // Implemented in terms of |WaitOnPort|. |
| std::optional<Exception> WaitForException(const zx::port& port, |
| const zx::channel& exception_channel, |
| zx::time deadline = zx::time::infinite()); |
| |
| std::pair<zx::port, zx::channel> CreateExceptionChannel(const zx::thread&, bool debugger = false); |
| |
| // Makes the |port| listen for events on the |exception_channel|. |
| void WaitAsyncOnExceptionChannel(const zx::port& port, const zx::channel& exception_channel); |
| std::optional<zx_port_packet_t> WaitOnPort(const zx::port& port, zx_signals_t signals, |
| zx::time deadline = zx::time::infinite()); |
| |
| // Exception Decoding ------------------------------------------------------------------------------ |
| |
| enum class HWExceptionType { |
| kSingleStep, |
| kHardware, |
| kWatchpoint, |
| kNone, |
| }; |
| HWExceptionType DecodeHWException(const zx::thread&, const Exception&); |
| |
| // Thread Management ------------------------------------------------------------------------------- |
| |
| zx_thread_state_general_regs_t ReadGeneralRegs(const zx::thread& thread); |
| |
| void WriteGeneralRegs(const zx::thread& thread, const zx_thread_state_general_regs_t& regs); |
| |
| zx_thread_state_debug_regs_t ReadDebugRegs(const zx::thread&); |
| |
| // NOTE: This might return an invalid (empty) suspend_token. |
| // If that happens, it means that |thread| is on an exception. |
| zx::suspend_token Suspend(const zx::thread& thread); |
| |
| void InstallHWBreakpoint(const zx::thread& thread, uint64_t address); |
| void RemoveHWBreakpoint(const zx::thread& thread); |
| |
| enum class WatchpointType { |
| kWrite, |
| kReadWrite, |
| }; |
| |
| // Length is how many bytes to hit. |
| // Must be a power of 2 (1, 2, 4, 8 bytes). |
| void InstallWatchpoint(const zx::thread& thread, uint64_t address, uint32_t length, |
| WatchpointType type = WatchpointType::kWrite); |
| void RemoveWatchpoint(const zx::thread& thread); |
| |
| // |exception| means that the thread might be currently on an exception that needs to be resumed. |
| std::optional<Exception> SingleStep(const zx::thread&, const zx::port&, |
| const zx::channel& exception_channel, |
| std::optional<Exception> exception = {}); |
| |
| // Multi-Process Utilities ------------------------------------------------------------------------- |
| // |
| struct Process { |
| std::string name; |
| zx::process handle; |
| zx::channel comm_channel; |
| }; |
| |
| // |argv[0]| should have a path to the ELF binary. |
| zx_status_t LaunchProcess(const zx::job&, const std::string& name, |
| const std::vector<std::string>& argv, Process*); |
| |
| // Initialization code for a process launched via |LaunchProcess|. Should be call at the beginning |
| // of the program. Meant to receive coordination resources such as an special channel. |
| zx_status_t InitSubProcess(zx::channel*); |
| |
| // Waits on ZX_FIFO_READABLE. |
| zx_status_t WaitOnChannelReadable(const zx::channel&, zx::time deadline = zx::time::infinite()); |
| |
| constexpr uint32_t kServerToClient = ZX_USER_SIGNAL_0; |
| constexpr uint32_t kClientToServer = ZX_USER_SIGNAL_1; |
| |
| zx_status_t SignalClient(const zx::eventpair& event); |
| zx_status_t SignalServer(const zx::eventpair& event); |
| |
| // Counterpart call of SignalClient/Server. |
| zx_status_t WaitForClient(const zx::eventpair& event, zx::time deadline = zx::time::infinite()); |
| zx_status_t WaitForServer(const zx::eventpair& event, zx::time deadline = zx::time::infinite()); |
| |
| // Utilitity Macros -------------------------------------------------------------------------------- |
| |
| #define PRINT_CLEAN(...) \ |
| { std::cout << fxl::StringPrintf(__VA_ARGS__) << std::endl << std::flush; } |
| |
| #define PRINT(...) \ |
| { \ |
| std::cout << "[" << __FILE__ << ":" << __LINE__ << "][t: " << std::this_thread::get_id() \ |
| << "] " << fxl::StringPrintf(__VA_ARGS__) << std::endl \ |
| << std::flush; \ |
| } |
| |
| #define DEFER_PRINT(...) auto __defer = fit::defer([=]() { PRINT(__VA_ARGS__); }); |
| |
| #define CHECK_OK(stmt) \ |
| { \ |
| zx_status_t __res = (stmt); \ |
| FX_DCHECK(__res == ZX_OK) << zx_status_get_string(__res); \ |
| } |
| |
| #define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a))[0]) |
| |
| #endif // SRC_DEVELOPER_DEBUG_DEBUG_AGENT_TEST_DATA_TEST_SUITE_HELPERS_H_ |