| // 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. | 
 |  | 
 | #include <lib/zx/eventpair.h> | 
 |  | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include "test_suite_helpers.h" | 
 |  | 
 | // This is a self contained binary that is meant to be run *manually*. This is the smallest code | 
 | // that can be used to reproduce a HW breakpoint exception. | 
 | // This is meant to be able to test the functionality of zircon without having to go throught the | 
 | // hassle of having the whole debugger context around. | 
 |  | 
 | namespace { | 
 |  | 
 | // Test Cases ====================================================================================== | 
 |  | 
 | // BreakOnFunction --------------------------------------------------------------------------------- | 
 | // | 
 | // 1. Create a thread that will loop forever, continually calling a particular | 
 | //    function. | 
 | // 2. Suspend that thread. | 
 | // 3. Install a HW breakpoint through zx_thread_write_state. | 
 | // 4. Resume the thread. | 
 | // 5. Wait for some time for the exception. If the exception never happened, it | 
 | //    means that Zircon is not doing the right thing. | 
 |  | 
 | // This is the function that we will set up the breakpoint to. | 
 | using HWBreakpointTestCaseFunctionToBeCalled = int (*)(int); | 
 | int __NO_INLINE FunctionToBreakpointOn1(int c) { return c + c; } | 
 | int __NO_INLINE FunctionToBreakpointOn2(int c) { return c + c; } | 
 | int __NO_INLINE FunctionToBreakpointOn3(int c) { return c + c; } | 
 | int __NO_INLINE FunctionToBreakpointOn4(int c) { return c + c; } | 
 | int __NO_INLINE FunctionToBreakpointOn5(int c) { return c + c; } | 
 |  | 
 | const char kBeacon[] = "Counter: Thread running.\n"; | 
 |  | 
 | // This is the code that the new thread will run. | 
 | // It's meant to be an eternal loop. | 
 | int BreakOnFunctionThreadFunction(void* user) { | 
 |   auto* thread_setup = reinterpret_cast<ThreadSetup*>(user); | 
 |  | 
 |   // We signal the test harness that we are here. | 
 |   thread_setup->event.signal(kHarnessToThread, kThreadToHarness); | 
 |  | 
 |   // We wait now for the harness to tell us we can continue. | 
 |   CHECK_OK(thread_setup->event.wait_one(kHarnessToThread, zx::time::infinite(), nullptr)); | 
 |  | 
 |   PRINT("Got signaled by harness."); | 
 |  | 
 |   int counter = 1; | 
 |   while (thread_setup->test_running) { | 
 |     auto* function_to_call = | 
 |         reinterpret_cast<HWBreakpointTestCaseFunctionToBeCalled>(thread_setup->user); | 
 |     FX_DCHECK(function_to_call); | 
 |  | 
 |     // We use write to avoid deadlocking with the outside libc calls. | 
 |     write(1, kBeacon, sizeof(kBeacon)); | 
 |     counter = function_to_call(counter); | 
 |     zx::nanosleep(zx::deadline_after(zx::sec(1))); | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | void BreakOnFunctionTestCase() { | 
 |   printf("Running HW breakpoint when calling a function test.\n"); | 
 |  | 
 |   // The functions to be called sequentially by the test. | 
 |   // clang-format off | 
 |   HWBreakpointTestCaseFunctionToBeCalled breakpoint_functions[] = { | 
 |       FunctionToBreakpointOn1, | 
 |       FunctionToBreakpointOn2, | 
 |       FunctionToBreakpointOn3, | 
 |       FunctionToBreakpointOn4, | 
 |       FunctionToBreakpointOn5, | 
 |   }; | 
 |   // clang-format on | 
 |  | 
 |   auto thread_setup = CreateTestSetup(BreakOnFunctionThreadFunction); | 
 |   auto [port, exception_channel] = CreateExceptionChannel(thread_setup->thread); | 
 |   WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |  | 
 |   Exception exception = {}; | 
 |   for (size_t i = 0; i < std::size(breakpoint_functions); i++) { | 
 |     // If this is the first iteration, we don't resume the exception. | 
 |     if (i > 0u) { | 
 |       WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |       ResumeException(thread_setup->thread, std::move(exception)); | 
 |     } | 
 |  | 
 |     auto* breakpoint_function = breakpoint_functions[i]; | 
 |  | 
 |     // Pass in the function to call as extra data. | 
 |     thread_setup->user = reinterpret_cast<void*>(breakpoint_function); | 
 |  | 
 |     // Install the breakpoint. | 
 |     uint64_t breakpoint_address = reinterpret_cast<uint64_t>(breakpoint_function); | 
 |     InstallHWBreakpoint(thread_setup->thread, breakpoint_address); | 
 |  | 
 |     // Tell the thread to continue. | 
 |     thread_setup->event.signal(kThreadToHarness, kHarnessToThread); | 
 |  | 
 |     // We wait until we receive an exception. | 
 |     auto opt_excp = WaitForException(port, exception_channel); | 
 |     FX_DCHECK(opt_excp.has_value()); | 
 |     exception = std::move(*opt_excp); | 
 |  | 
 |     FX_DCHECK(exception.info.type == ZX_EXCP_HW_BREAKPOINT); | 
 |     PRINT("Hit HW breakpoint %zu on 0x%zx", i, exception.pc); | 
 |  | 
 |     // Remove the breakpoint. | 
 |     RemoveHWBreakpoint(thread_setup->thread); | 
 |   } | 
 |  | 
 |   // Tell the thread to exit. | 
 |   thread_setup->test_running = false; | 
 |   ResumeException(thread_setup->thread, std::move(exception)); | 
 | } | 
 |  | 
 | // Watchpoints ------------------------------------------------------------------------------------- | 
 | // | 
 | // This test has an array of bytes that will be accessed one by one by another thread. | 
 | // The harness will set a watchpoint on each of those bytes and expects to receive an exception for | 
 | // of them. | 
 |  | 
 | uint8_t gDataToTouch[16] = {}; | 
 |  | 
 | int WatchpointThreadFunction(void* user) { | 
 |   auto* thread_setup = reinterpret_cast<ThreadSetup*>(user); | 
 |  | 
 |   // We signal the test harness that we are here. | 
 |   thread_setup->event.signal(kHarnessToThread, kThreadToHarness); | 
 |  | 
 |   while (thread_setup->test_running) { | 
 |     // We wait now for the harness to tell us we can continue. | 
 |     CHECK_OK(thread_setup->event.wait_one(kHarnessToThread, zx::time::infinite(), nullptr)); | 
 |  | 
 |     uint8_t* byte = reinterpret_cast<uint8_t*>(thread_setup->user); | 
 |     FX_DCHECK(byte); | 
 |  | 
 |     *byte += 1; | 
 |  | 
 |     // We signal that we finished this write. | 
 |     CHECK_OK(thread_setup->event.signal(kHarnessToThread, kThreadToHarness)); | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | // Returns whether the breakpoint was hit. | 
 | bool TestWatchpointRun(const zx::port& port, const zx::channel& exception_channel, | 
 |                        ThreadSetup* thread_setup, uint64_t wp_address, uint32_t length, | 
 |                        uint8_t* address_to_write) { | 
 |   thread_setup->user = address_to_write; | 
 |  | 
 |   // Install the watchpoint. | 
 |   InstallWatchpoint(thread_setup->thread, wp_address, length); | 
 |  | 
 |   // Tell the thread to continue. | 
 |   CHECK_OK(thread_setup->event.signal(kThreadToHarness, kHarnessToThread)); | 
 |  | 
 |   // Wait until the exception is hit. | 
 |   auto opt_excp = WaitForException(port, exception_channel, | 
 |                                    zx::deadline_after(zx::msec(kExceptionWaitTimeout))); | 
 |  | 
 |   // Remove the watchpoint. | 
 |   RemoveWatchpoint(thread_setup->thread); | 
 |  | 
 |   if (!opt_excp) { | 
 |     PRINT_CLEAN("Writing into 0x%zx.", (uint64_t)address_to_write); | 
 |     return false; | 
 |   } | 
 |  | 
 |   Exception exception = std::move(*opt_excp); | 
 |  | 
 |   FX_DCHECK(exception.info.type == ZX_EXCP_HW_BREAKPOINT); | 
 |   PRINT_CLEAN("Writing into 0x%zx. Hit!", (uint64_t)address_to_write); | 
 |  | 
 |   WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |   ResumeException(thread_setup->thread, std::move(exception)); | 
 |  | 
 |   // Wait until the thread tells us it's ready. | 
 |   CHECK_OK(thread_setup->event.wait_one(kThreadToHarness, zx::time::infinite(), nullptr)); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | void WatchpointTestCase() { | 
 |   PRINT("Running Watchpoint test case."); | 
 |  | 
 |   auto thread_setup = CreateTestSetup(WatchpointThreadFunction); | 
 |   auto [port, exception_channel] = CreateExceptionChannel(thread_setup->thread); | 
 |   WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |  | 
 |   uint32_t kSizes[] = {1, 2, 4, 8}; | 
 |   for (uint32_t size : kSizes) { | 
 |     PRINT_CLEAN("===================================================================="); | 
 |     PRINT_CLEAN("%u BYTE ALIGNED WATCHPOINTS", size); | 
 |     for (size_t i = 0; i < std::size(gDataToTouch); i++) { | 
 |       uint64_t brk = reinterpret_cast<uint64_t>(gDataToTouch) + i; | 
 |  | 
 |       if (i > 0) | 
 |         PRINT_CLEAN("----------------------------------------"); | 
 |       PRINT_CLEAN("* Setting %u byte watchpoint for 0x%zx\n", size, brk); | 
 |  | 
 |       for (size_t j = 0; j < std::size(gDataToTouch); j++) { | 
 |         // Pass in the byte to break on. | 
 |         uint8_t* data_ptr = gDataToTouch + j; | 
 |         bool hit = | 
 |             TestWatchpointRun(port, exception_channel, thread_setup.get(), brk, size, data_ptr); | 
 |  | 
 |         // We should only hit if it is the expected byte. | 
 |         bool in_range = (j - i) < size; | 
 |         if (hit) { | 
 |           FX_DCHECK(in_range) << "i: " << i << ", j: " << j << ". Got unexpected hit."; | 
 |         } else { | 
 |           FX_DCHECK(!in_range) << "i: " << i << ", j: " << j << ". Didn't get expected hit."; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Tell the thread to exit. | 
 |   thread_setup->test_running = false; | 
 |   CHECK_OK(thread_setup->event.signal(kThreadToHarness, kHarnessToThread)); | 
 | } | 
 |  | 
 | // Aligned Watchpoint ------------------------------------------------------------------------------ | 
 | // | 
 | // This test runs a thread that within a loop prints a group of ints, increments them (via var++) | 
 | // and then prints it again (the function is AlignedWatchpointThreadFunction). | 
 | // On the control thread, it sets a read/write watchpoint on one of the globals and verifies that | 
 | // the following accesses are hit: | 
 | // | 
 | // 1. Read on the first printf. | 
 | // 2. Read on the var++. | 
 | // 3. Write on the var++. | 
 | // 4. Read on the second printf. | 
 | // | 
 | // NOTE: In order to do this correctly, this tests does the same thing that zxdb does when it | 
 | //       encounters a breakpoint: It deactivates the breakpoint, single steps the thread and then | 
 | //       installs the breakpoint again. The watchpoint here is installed/uninstalled for every hit | 
 | //       and the thread is single stepped. | 
 |  | 
 | int SomeInt = 10; | 
 | int SomeInt2 = 20; | 
 | int SomeInt3 = 30; | 
 | int SomeInt4 = 40; | 
 |  | 
 | struct AlignedWatchpointUserData { | 
 |   // How many times to run the test. | 
 |   int times = 10; | 
 | }; | 
 |  | 
 | int AlignedWatchpointThreadFunction(void* user) { | 
 |   auto* thread_setup = reinterpret_cast<ThreadSetup*>(user); | 
 |   auto* user_data = reinterpret_cast<AlignedWatchpointUserData*>(thread_setup->user); | 
 |  | 
 |   // We signal the test harness that we are here. | 
 |   thread_setup->event.signal(kHarnessToThread, kThreadToHarness); | 
 |  | 
 |   CHECK_OK(thread_setup->event.wait_one(kHarnessToThread, zx::time::infinite(), nullptr)); | 
 |  | 
 |   printf("User data times: %d.\n", user_data->times); | 
 |   for (int i = 0; i < user_data->times; i++) { | 
 |     printf("Before: %d, %d, %d, %d\n", SomeInt, SomeInt2, SomeInt3, SomeInt4); | 
 |     SomeInt++; | 
 |     SomeInt2++; | 
 |     SomeInt3++; | 
 |     SomeInt4++; | 
 |     printf("After:  %d, %d, %d, %d\n", SomeInt, SomeInt2, SomeInt3, SomeInt4); | 
 |     printf("-----------------------------\n"); | 
 |   } | 
 |  | 
 |   fflush(stdout); | 
 |  | 
 |   // We signal that we finished this write. | 
 |   CHECK_OK(thread_setup->event.signal(kHarnessToThread, kThreadToHarness)); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | void WatchpointStepOver(uint64_t wp_address, const zx::thread& thread, const zx::port& port, | 
 |                         const zx::channel& exception_channel, std::optional<Exception> exception) { | 
 |   RemoveWatchpoint(thread); | 
 |  | 
 |   exception = SingleStep(thread, port, exception_channel, std::move(exception)); | 
 |   FX_DCHECK(exception); | 
 |  | 
 |   // Now that we have single stepped, we can reinstall the watchpoint. | 
 |   InstallWatchpoint(thread, wp_address, 4, WatchpointType::kReadWrite); | 
 |  | 
 |   WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |   ResumeException(thread, std::move(*exception)); | 
 | } | 
 |  | 
 | #define GET_DEADLINE(timeout) zx::deadline_after(zx::msec((timeout))) | 
 |  | 
 | void AlignedWatchpointTestCase() { | 
 |   PRINT("Running aligned watchpoint test case."); | 
 |   PRINT("SomeInt:  0x%p", &SomeInt); | 
 |   PRINT("SomeInt2: 0x%p", &SomeInt2); | 
 |   PRINT("SomeInt3: 0x%p", &SomeInt3); | 
 |   PRINT("SomeInt4: 0x%p", &SomeInt4); | 
 |  | 
 |   // Create test setup. | 
 |   AlignedWatchpointUserData user_data = {}; | 
 |   user_data.times = 1; | 
 |   auto thread_setup = CreateTestSetup(AlignedWatchpointThreadFunction, &user_data); | 
 |   const zx::thread& thread = thread_setup->thread; | 
 |  | 
 |   auto [port, exception_channel] = CreateExceptionChannel(thread); | 
 |   WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |  | 
 |   // We install a watchpoint. | 
 |   uint64_t wp_address = reinterpret_cast<uint64_t>(&SomeInt); | 
 |   InstallWatchpoint(thread, wp_address, 4, WatchpointType::kReadWrite); | 
 |  | 
 |   // Tell the test to run. | 
 |   CHECK_OK(thread_setup->event.signal(kThreadToHarness, kHarnessToThread)); | 
 |  | 
 |   for (int i = 0; i < user_data.times; i++) { | 
 |     uint64_t pc = 0; | 
 |     PRINT("ITERATION %d ---------------------------------------------------------", i); | 
 |     // Wait until the exception is hit. | 
 |     auto exception = WaitForException(port, exception_channel, GET_DEADLINE(kExceptionWaitTimeout)); | 
 |     FX_DCHECK(exception.has_value()); | 
 |     FX_DCHECK(exception->pc > pc); | 
 |     FX_DCHECK(DecodeHWException(thread, exception.value()) == HWExceptionType::kWatchpoint); | 
 |     pc = exception->pc; | 
 |     PRINT("Exception on 0x%p: Hit first printf read!", (void*)exception->pc); | 
 |  | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |  | 
 |     exception = WaitForException(port, exception_channel, GET_DEADLINE(kExceptionWaitTimeout)); | 
 |     FX_DCHECK(exception.has_value()); | 
 |     FX_DCHECK(exception->pc > pc); | 
 |     FX_DCHECK(DecodeHWException(thread, exception.value()) == HWExceptionType::kWatchpoint); | 
 |     pc = exception->pc; | 
 |     PRINT("Exception on 0x%p: Hit ++ read!", (void*)exception->pc); | 
 |  | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |  | 
 |     exception = WaitForException(port, exception_channel, GET_DEADLINE(kExceptionWaitTimeout)); | 
 |     FX_DCHECK(exception.has_value()); | 
 |     FX_DCHECK(exception->pc > pc); | 
 |     FX_DCHECK(DecodeHWException(thread, exception.value()) == HWExceptionType::kWatchpoint); | 
 |     pc = exception->pc; | 
 |     PRINT("Exception on 0x%p: Hit ++ write!", (void*)exception->pc); | 
 |  | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |  | 
 |     exception = WaitForException(port, exception_channel, GET_DEADLINE(kExceptionWaitTimeout)); | 
 |     FX_DCHECK(exception.has_value()); | 
 |     FX_DCHECK(exception->pc > pc); | 
 |     FX_DCHECK(DecodeHWException(thread, exception.value()) == HWExceptionType::kWatchpoint); | 
 |     pc = exception->pc; | 
 |     PRINT("Exception on 0x%p: Hit second printf read!", (void*)exception->pc); | 
 |  | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |   } | 
 |  | 
 |   // Wait until the thread is done. | 
 |   CHECK_OK(thread_setup->event.wait_one(kThreadToHarness, zx::time::infinite(), nullptr)); | 
 | } | 
 |  | 
 | // Channel messaging ------------------------------------------------------------------------------- | 
 | // | 
 | // 1. Thread writes a set of messages into the channel then closes its endpoint. | 
 | // 2. The main thread will wait until the channel has been closed. | 
 | // 3. It will then read all the messages from it. | 
 |  | 
 | int ChannelMessagingThreadFunction(void* user) { | 
 |   ThreadSetup* thread_setup = reinterpret_cast<ThreadSetup*>(user); | 
 |  | 
 |   // We signal the test harness that we are here. | 
 |   thread_setup->event.signal(kHarnessToThread, kThreadToHarness); | 
 |  | 
 |   // We wait now for the harness to tell us we can continue. | 
 |   CHECK_OK(thread_setup->event.wait_one(kHarnessToThread, zx::time::infinite(), nullptr)); | 
 |  | 
 |   zx::channel* channel = reinterpret_cast<zx::channel*>(thread_setup->user); | 
 |  | 
 |   constexpr char kMsg[] = "Hello, World!"; | 
 |  | 
 |   for (int i = 0; i < 10; i++) { | 
 |     CHECK_OK(channel->write(0, kMsg, sizeof(kMsg), nullptr, 0)); | 
 |     PRINT("Added message %d.", i); | 
 |   } | 
 |  | 
 |   channel->reset(); | 
 |   PRINT("Closed channel."); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | void ChannelMessagingTestCase() { | 
 |   PRINT("Running channel messaging."); | 
 |  | 
 |   zx::channel mine, theirs; | 
 |   CHECK_OK(zx::channel::create(0, &mine, &theirs)); | 
 |  | 
 |   auto thread_setup = CreateTestSetup(ChannelMessagingThreadFunction, &theirs); | 
 |  | 
 |   // Tell the thread to continue. | 
 |   thread_setup->event.signal(kThreadToHarness, kHarnessToThread); | 
 |  | 
 |   // Wait for peer closed. | 
 |   CHECK_OK(mine.wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), nullptr)); | 
 |  | 
 |   // Start reading from the channel. | 
 |   int read_count = 0; | 
 |   char buf[1024] = {}; | 
 |   while (true) { | 
 |     zx_status_t status = mine.read_etc(0, buf, nullptr, sizeof(buf), 0, nullptr, nullptr); | 
 |     if (status == ZX_OK) { | 
 |       PRINT("Read message %d: %s", read_count++, buf); | 
 |     } else { | 
 |       PRINT("No more messages (status: %s).", zx_status_get_string(status)); | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   thread_setup->test_running = false; | 
 | } | 
 |  | 
 | // Watchpoint Server/Client ------------------------------------------------------------------------ | 
 | // | 
 | // The test spawns a new process with this binary, but passing the |watchpoint_client| option. | 
 | // It coordinates through a channel and an event. The idea is that the server listen on the debugger | 
 | // exception port of the client and setup a read-write watchpoint on a thread of the client. | 
 | // The client runs the same thread as AlignedWatchpointTestCase (AlignedWatchpointThreadFunction), | 
 | // which the server will set a brekapoint to. Basically it's a multi-process |aligned_watchpoint| | 
 | // test. | 
 | // | 
 | // The setup is as follows: | 
 | // 1. Client sends the addresses of the ints (SomeInt, SomeInt2, etc.). It also passes the memory | 
 | //    associated with AlignedWatchpointThreadFunction, so that the server can verify which address | 
 | //    actually triggered the exception. | 
 | // 2. The server listens on the exception port of the client and sets up a R/W Breakpoint. | 
 | // 3. The client starts another thread with AlignedWatchpointThreadFunction. | 
 | // 4. The server verifies that all the expected watchpoint exceptions are hit. | 
 |  | 
 | #define DEADLINE(d) zx::deadline_after(zx::sec((d))) | 
 |  | 
 | constexpr uint32_t kIterations = 1000; | 
 |  | 
 | constexpr uint32_t kInstructionBufferSize = 4096; | 
 |  | 
 | // These are the instructions that the server expects the client to have in the PC when it triggers | 
 | // each of the exceptions. Thhe client will send |kInstructionBufferSize| instructions starting | 
 | // with the first of |AlignedWatchpointThreadFunction| and the base address of it. That way the | 
 | // server can see the offset and see which instruction triggered the exception. | 
 | constexpr uint32_t kPrintLoad1 = 0xb9400101;  // ldr w1, [x8] | 
 | constexpr uint32_t kPlusRead = 0xb940010c;    // ldr w12, [x8] | 
 | constexpr uint32_t kPlusWrite = 0xb900010c;   // str w12, [x8] | 
 | constexpr uint32_t kPrintLoad2 = 0xb9400101;  // ldr w1, [x8] | 
 |  | 
 | // Verifies the exception that was triggered by the client. | 
 | // |instructions| is an array with the instructions of |AlignedWatchpointThreadFunction. | 
 | // |base_address| is the address where |AlignedWatchpointThreadFunction| starts. | 
 | // |expected_instruction| is what we expect the pc points to. | 
 | uint64_t CheckWatchpointException(const std::optional<Exception>& exception, | 
 |                                   const zx::thread& thread, const zx::port& port, | 
 |                                   const zx::channel& exception_channel, uint32_t* instructions, | 
 |                                   uint32_t expected_instruction, uint64_t base_address, uint64_t pc, | 
 |                                   const char* msg) { | 
 |   // Wait until the exception is hit. | 
 |   FX_DCHECK(exception.has_value()); | 
 |   FX_DCHECK(exception->pc > pc); | 
 |   FX_DCHECK(DecodeHWException(thread, exception.value()) == HWExceptionType::kWatchpoint); | 
 |   pc = exception->pc; | 
 |   FX_DCHECK(pc < base_address + kInstructionBufferSize * sizeof(uint64_t)); | 
 |   uint32_t instruction = instructions[(pc - base_address) >> 2]; | 
 |   FX_DCHECK(instruction == expected_instruction); | 
 |   PRINT("SERVER: Exception on %p (0x%x): %s.", reinterpret_cast<void*>(pc), instruction, msg); | 
 |  | 
 |   return pc; | 
 | } | 
 |  | 
 | void WatchpointServer() { | 
 |   PRINT("Running Watchpoint Server"); | 
 |  | 
 |   zx::job default_job(zx_job_default()); | 
 |   zx::job child_job; | 
 |   CHECK_OK(zx::job::create(default_job, 0, &child_job)); | 
 |  | 
 |   // Spawn a process the FDIO way. | 
 |   Process process; | 
 |   std::vector<std::string> args = {"/pkg/bin/hw_breakpointer", "watchpoint_client"}; | 
 |   CHECK_OK(LaunchProcess(child_job, "test-process", args, &process)); | 
 |  | 
 |   zx::eventpair event, theirs; | 
 |   CHECK_OK(zx::eventpair::create(0, &event, &theirs)); | 
 |  | 
 |   // Send an event down the channel. | 
 |   zx_handle_t theirs_handle = theirs.release(); | 
 |   CHECK_OK(process.comm_channel.write(0, &kIterations, sizeof(kIterations), &theirs_handle, 1)); | 
 |  | 
 |   // Wait on the event. | 
 |   CHECK_OK(WaitForClient(event, DEADLINE(1))); | 
 |   PRINT("SERVER: Client got the event."); | 
 |  | 
 |   // We set up the exception channel. | 
 |   zx::port port; | 
 |   CHECK_OK(zx::port::create(0, &port)); | 
 |   zx::channel exception_channel; | 
 |   CHECK_OK(process.handle.create_exception_channel(0, &exception_channel)); | 
 |   WaitAsyncOnExceptionChannel(port, exception_channel); | 
 |  | 
 |   // Wait until the client sends us where the addresses are. | 
 |   CHECK_OK(WaitOnChannelReadable(process.comm_channel, DEADLINE(1))); | 
 |   uint64_t kAddresses[4]; | 
 |   CHECK_OK( | 
 |       process.comm_channel.read(0, kAddresses, nullptr, sizeof(kAddresses), 0, nullptr, nullptr)); | 
 |  | 
 |   PRINT("SERVER: SomeInt:  0x%p", reinterpret_cast<void*>(kAddresses[0])); | 
 |   PRINT("SERVER: SomeInt2: 0x%p", reinterpret_cast<void*>(kAddresses[1])); | 
 |   PRINT("SERVER: SomeInt3: 0x%p", reinterpret_cast<void*>(kAddresses[2])); | 
 |   PRINT("SERVER: SomeInt4: 0x%p", reinterpret_cast<void*>(kAddresses[3])); | 
 |  | 
 |   // Read the instructions. | 
 |   CHECK_OK(WaitOnChannelReadable(process.comm_channel, DEADLINE(1))); | 
 |   uint32_t kInstructions[kInstructionBufferSize] = {}; | 
 |   CHECK_OK(process.comm_channel.read(0, kInstructions, nullptr, sizeof(kInstructions), 0, nullptr, | 
 |                                      nullptr)); | 
 |  | 
 |   // Read the base address. | 
 |   CHECK_OK(WaitOnChannelReadable(process.comm_channel, DEADLINE(1))); | 
 |   uint64_t kBaseAddress = 0; | 
 |   CHECK_OK(process.comm_channel.read(0, &kBaseAddress, nullptr, sizeof(kBaseAddress), 0, nullptr, | 
 |                                      nullptr)); | 
 |   FX_DCHECK(kBaseAddress > 0); | 
 |   PRINT("SERVER: Got Base address %p.", reinterpret_cast<void*>(kBaseAddress)); | 
 |  | 
 |   // Ping the client we got it and wait for it to spawn up a thread and send the handle over. | 
 |   CHECK_OK(SignalClient(event)); | 
 |   CHECK_OK(WaitForClient(event, DEADLINE(1))); | 
 |   CHECK_OK(WaitOnChannelReadable(process.comm_channel, DEADLINE(1))); | 
 |  | 
 |   zx::thread thread; | 
 |   CHECK_OK(process.comm_channel.read(0, nullptr, thread.reset_and_get_address(), 0, 1, nullptr, | 
 |                                      nullptr)); | 
 |   PRINT("SERVER: Received the thread handle."); | 
 |  | 
 |   // Setup a watchpoint. | 
 |   uint64_t wp_address = kAddresses[0]; | 
 |   InstallWatchpoint(thread, wp_address, 4, WatchpointType::kReadWrite); | 
 |  | 
 |   CHECK_OK(SignalClient(event)); | 
 |   for (uint32_t i = 0; i < kIterations; i++) { | 
 |     uint64_t pc = 0; | 
 |     PRINT("SERVER: ITERATION %d ---------------------------------------------------------", i); | 
 |  | 
 |     // printf 1. | 
 |     auto exception = WaitForException(port, exception_channel, DEADLINE(1)); | 
 |     pc = CheckWatchpointException(exception, thread, port, exception_channel, kInstructions, | 
 |                                   kPrintLoad1, kBaseAddress, pc, "First printf read"); | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |  | 
 |     // ++ Read. | 
 |     exception = WaitForException(port, exception_channel, DEADLINE(1)); | 
 |     pc = CheckWatchpointException(exception, thread, port, exception_channel, kInstructions, | 
 |                                   kPlusRead, kBaseAddress, pc, "++ read"); | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |  | 
 |     // ++ Write. | 
 |     exception = WaitForException(port, exception_channel, DEADLINE(1)); | 
 |     pc = CheckWatchpointException(exception, thread, port, exception_channel, kInstructions, | 
 |                                   kPlusWrite, kBaseAddress, pc, "++ write"); | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |  | 
 |     // printf 2. | 
 |     exception = WaitForException(port, exception_channel, DEADLINE(1)); | 
 |     pc = CheckWatchpointException(exception, thread, port, exception_channel, kInstructions, | 
 |                                   kPrintLoad2, kBaseAddress, pc, "Second printf read"); | 
 |     WatchpointStepOver(wp_address, thread, port, exception_channel, std::move(exception)); | 
 |   } | 
 |  | 
 |   // Wait for the client to be done. | 
 |   CHECK_OK(WaitForClient(event, DEADLINE(1))); | 
 | } | 
 |  | 
 | void WatchpointClient() { | 
 |   zx::channel channel; | 
 |   CHECK_OK(InitSubProcess(&channel)); | 
 |  | 
 |   CHECK_OK(WaitOnChannelReadable(channel, DEADLINE(1))); | 
 |  | 
 |   uint32_t kTimes = 0; | 
 |   zx::eventpair event; | 
 |   CHECK_OK( | 
 |       channel.read(0, &kTimes, event.reset_and_get_address(), sizeof(kTimes), 1, nullptr, nullptr)); | 
 |   FX_DCHECK(kTimes > 0); | 
 |  | 
 |   PRINT("CLIENT: Read event. Times: %u.", kTimes); | 
 |   CHECK_OK(SignalServer(event)); | 
 |  | 
 |   // Send over the addresses of the watchpoints. | 
 |   uint64_t kAddresses[4] = {(uint64_t)&SomeInt, (uint64_t)&SomeInt2, (uint64_t)&SomeInt3, | 
 |                             (uint64_t)&SomeInt3}; | 
 |   CHECK_OK(channel.write(0, kAddresses, sizeof(kAddresses), nullptr, 0)); | 
 |  | 
 |   PRINT("CLIENT: SomeInt:  0x%p", reinterpret_cast<void*>(kAddresses[0])); | 
 |   PRINT("CLIENT: SomeInt2: 0x%p", reinterpret_cast<void*>(kAddresses[1])); | 
 |   PRINT("CLIENT: SomeInt3: 0x%p", reinterpret_cast<void*>(kAddresses[2])); | 
 |   PRINT("CLIENT: SomeInt4: 0x%p", reinterpret_cast<void*>(kAddresses[3])); | 
 |   PRINT("CLIENT: Wrote addresses."); | 
 |  | 
 |   // Send over the instructions of the function. | 
 |   uint32_t kInstructions[kInstructionBufferSize] = {}; | 
 |   memcpy(kInstructions, reinterpret_cast<void*>(AlignedWatchpointThreadFunction), | 
 |          sizeof(kInstructions)); | 
 |   CHECK_OK(channel.write(0, kInstructions, sizeof(kInstructions), nullptr, 0)); | 
 |  | 
 |   // Send over the base address. | 
 |   uint64_t kBaseAddress = (uint64_t)AlignedWatchpointThreadFunction; | 
 |   CHECK_OK(channel.write(0, &kBaseAddress, sizeof(kBaseAddress), nullptr, 0)); | 
 |   PRINT("CLIENT: Sent base address %p.", reinterpret_cast<void*>(kBaseAddress)); | 
 |  | 
 |   // Wait for ack from the server. | 
 |   CHECK_OK(WaitForServer(event, DEADLINE(1))); | 
 |  | 
 |   // Start the thread. | 
 |   AlignedWatchpointUserData user_data = {}; | 
 |   user_data.times = kTimes; | 
 |   auto thread_setup = CreateTestSetup(AlignedWatchpointThreadFunction, &user_data); | 
 |  | 
 |   // Write the thread handle over. | 
 |   zx::thread thread_to_send; | 
 |   CHECK_OK(thread_setup->thread.duplicate(ZX_RIGHT_SAME_RIGHTS, &thread_to_send)); | 
 |  | 
 |   CHECK_OK(SignalServer(event)); | 
 |  | 
 |   zx_handle_t handle = thread_to_send.release(); | 
 |   CHECK_OK(channel.write(0, nullptr, 0, &handle, 1)); | 
 |  | 
 |   PRINT("CLIENT: Created and sent the thread handle over."); | 
 |  | 
 |   // Tell the client we wrote. | 
 |   CHECK_OK(WaitForServer(event, DEADLINE(1))); | 
 |   PRINT("CLIENT: Starting test thread."); | 
 |  | 
 |   // Tell the test to run. | 
 |   CHECK_OK(thread_setup->event.signal(kThreadToHarness, kHarnessToThread)); | 
 |  | 
 |   // Wait until the thread is done. | 
 |   CHECK_OK(thread_setup->event.wait_one(kThreadToHarness, zx::time::infinite(), nullptr)); | 
 |  | 
 |   // Signal the server we're done. | 
 |   CHECK_OK(SignalServer(event)); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Main -------------------------------------------------------------------------------------------- | 
 |  | 
 | namespace { | 
 |  | 
 | struct TestCase { | 
 |   using TestFunction = void (*)(); | 
 |  | 
 |   std::string name = nullptr; | 
 |   std::string description = nullptr; | 
 |   TestFunction test_function = nullptr; | 
 | }; | 
 |  | 
 | TestCase kTestCases[] = { | 
 |     {"hw_breakpoints", "Call multiple HW breakpoints on different functions.", | 
 |      BreakOnFunctionTestCase}, | 
 |  | 
 |     {"watchpoints", "Call multiple watchpoints.", WatchpointTestCase}, | 
 |  | 
 |     {"aligned_watchpoints", "Call aligned R/W watchpoint", AlignedWatchpointTestCase}, | 
 |  | 
 |     {"channel_calls", | 
 |      "Send multiple messages over a channel call and read from it after it is closed.", | 
 |      ChannelMessagingTestCase}, | 
 |  | 
 |     {"watchpoint_server", "Will start a client process and sets up a R/W watchpoint on it.", | 
 |      WatchpointServer}, | 
 |     {"watchpoint_client", "Started by |watchpoint_server|. Not meant to be run manually.", | 
 |      WatchpointClient}, | 
 | }; | 
 |  | 
 | void PrintUsage() { | 
 |   printf("Usage: hw_breakpointer <TEST CASE>\n"); | 
 |   printf("Test cases are:\n"); | 
 |   for (auto& test_case : kTestCases) { | 
 |     printf("- %s: %s\n", test_case.name.c_str(), test_case.description.c_str()); | 
 |   } | 
 |   fflush(stdout); | 
 | } | 
 |  | 
 | TestCase::TestFunction GetTestCase(std::string test_name) { | 
 |   for (auto& test_case : kTestCases) { | 
 |     if (test_name == test_case.name) | 
 |       return test_case.test_function; | 
 |   } | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | int main(int argc, char* argv[]) { | 
 |   if (argc != 2) { | 
 |     PrintUsage(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   const char* test_name = argv[1]; | 
 |   auto* test_function = GetTestCase(test_name); | 
 |   if (!test_function) { | 
 |     printf("Unknown test case %s\n", test_name); | 
 |     PrintUsage(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   test_function(); | 
 |   return 0; | 
 | } |