| 1 | //===-- WatchpointAlgorithmsTests.cpp -------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "gtest/gtest.h" |
| 10 | |
| 11 | #include "lldb/Breakpoint/WatchpointAlgorithms.h" |
| 12 | |
| 13 | #include <utility> |
| 14 | #include <vector> |
| 15 | |
| 16 | using namespace lldb; |
| 17 | using namespace lldb_private; |
| 18 | |
| 19 | class WatchpointAlgorithmsTest : public WatchpointAlgorithms { |
| 20 | public: |
| 21 | using WatchpointAlgorithms::PowerOf2Watchpoints; |
| 22 | using WatchpointAlgorithms::Region; |
| 23 | }; |
| 24 | |
| 25 | struct testcase { |
| 26 | WatchpointAlgorithmsTest::Region user; // What the user requested |
| 27 | std::vector<WatchpointAlgorithmsTest::Region> |
| 28 | hw; // The hardware watchpoints we'll use |
| 29 | }; |
| 30 | |
| 31 | void check_testcase(testcase test, |
| 32 | std::vector<WatchpointAlgorithmsTest::Region> result, |
| 33 | size_t min_byte_size, size_t max_byte_size, |
| 34 | uint32_t address_byte_size) { |
| 35 | |
| 36 | EXPECT_EQ(result.size(), test.hw.size()); |
| 37 | for (size_t i = 0; i < result.size(); i++) { |
| 38 | EXPECT_EQ(result[i].addr, test.hw[i].addr); |
| 39 | EXPECT_EQ(result[i].size, test.hw[i].size); |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | TEST(WatchpointAlgorithmsTests, PowerOf2Watchpoints) { |
| 44 | |
| 45 | // clang-format off |
| 46 | std::vector<testcase> doubleword_max = { |
| 47 | #if defined(__LP64__) |
| 48 | // These two tests don't work if lldb is built on |
| 49 | // a 32-bit system (likely with a 32-bit size_t). |
| 50 | // A 32-bit lldb debugging a 64-bit process isn't |
| 51 | // critical right now. |
| 52 | { |
| 53 | .user: {.addr: 0x7fffffffe83b, .size: 1}, |
| 54 | .hw: {{.addr: 0x7fffffffe83b, .size: 1}} |
| 55 | }, |
| 56 | { |
| 57 | .user: {.addr: 0x7fffffffe838, .size: 2}, |
| 58 | .hw: {{.addr: 0x7fffffffe838, .size: 2}} |
| 59 | }, |
| 60 | #endif |
| 61 | { |
| 62 | .user: {.addr: 0x1012, .size: 8}, |
| 63 | .hw: {{.addr: 0x1010, .size: 8}, {.addr: 0x1018, .size: 8}} |
| 64 | }, |
| 65 | { |
| 66 | .user: {.addr: 0x1002, .size: 4}, |
| 67 | .hw: {{.addr: 0x1000, .size: 8}} |
| 68 | }, |
| 69 | { |
| 70 | .user: {.addr: 0x1006, .size: 4}, |
| 71 | .hw: {{.addr: 0x1004, .size: 4}, {.addr: 0x1008, .size: 4}} |
| 72 | }, |
| 73 | { |
| 74 | .user: {.addr: 0x1006, .size: 8}, |
| 75 | .hw: {{.addr: 0x1000, .size: 8}, {.addr: 0x1008, .size: 8}} |
| 76 | }, |
| 77 | { |
| 78 | .user: {.addr: 0x1000, .size: 24}, |
| 79 | .hw: {{.addr: 0x1000, .size: 8}, {.addr: 0x1008, .size: 8}, {.addr: 0x1010, .size: 8}} |
| 80 | }, |
| 81 | { |
| 82 | .user: {.addr: 0x1014, .size: 26}, |
| 83 | .hw: {{.addr: 0x1010, .size: 8}, {.addr: 0x1018, .size: 8}, {.addr: 0x1020, .size: 8}, {.addr: 0x1028, .size: 8}} |
| 84 | }, |
| 85 | }; |
| 86 | // clang-format on |
| 87 | for (testcase test : doubleword_max) { |
| 88 | addr_t user_addr = test.user.addr; |
| 89 | size_t user_size = test.user.size; |
| 90 | size_t min_byte_size = 1; |
| 91 | size_t max_byte_size = 8; |
| 92 | size_t address_byte_size = 8; |
| 93 | auto result = WatchpointAlgorithmsTest::PowerOf2Watchpoints( |
| 94 | user_addr, user_size, min_byte_size, max_byte_size, address_byte_size); |
| 95 | |
| 96 | check_testcase(test, result, min_byte_size, max_byte_size, |
| 97 | address_byte_size); |
| 98 | } |
| 99 | |
| 100 | // clang-format off |
| 101 | std::vector<testcase> word_max = { |
| 102 | { |
| 103 | .user: {.addr: 0x00411050, .size: 4}, |
| 104 | .hw: {{.addr: 0x00411050, .size: 4}} |
| 105 | }, |
| 106 | { |
| 107 | .user: {.addr: 0x1002, .size: 4}, |
| 108 | .hw: {{.addr: 0x1000, .size: 4}, {.addr: 0x1004, .size: 4}} |
| 109 | }, |
| 110 | }; |
| 111 | // clang-format on |
| 112 | for (testcase test : word_max) { |
| 113 | addr_t user_addr = test.user.addr; |
| 114 | size_t user_size = test.user.size; |
| 115 | size_t min_byte_size = 1; |
| 116 | size_t max_byte_size = 4; |
| 117 | size_t address_byte_size = 4; |
| 118 | auto result = WatchpointAlgorithmsTest::PowerOf2Watchpoints( |
| 119 | user_addr, user_size, min_byte_size, max_byte_size, address_byte_size); |
| 120 | |
| 121 | check_testcase(test, result, min_byte_size, max_byte_size, |
| 122 | address_byte_size); |
| 123 | } |
| 124 | |
| 125 | // clang-format off |
| 126 | std::vector<testcase> twogig_max = { |
| 127 | { |
| 128 | .user: {.addr: 0x1010, .size: 16}, |
| 129 | .hw: {{.addr: 0x1010, .size: 16}} |
| 130 | }, |
| 131 | { |
| 132 | .user: {.addr: 0x1010, .size: 24}, |
| 133 | .hw: {{.addr: 0x1000, .size: 64}} |
| 134 | }, |
| 135 | |
| 136 | // We increase 36 to the aligned 64 byte size, but |
| 137 | // 0x1000-0x1040 doesn't cover the requested region. Then |
| 138 | // we expand to 128 bytes starting at 0x1000 that does |
| 139 | // cover it. Is this a good tradeoff for a 36 byte region? |
| 140 | { |
| 141 | .user: {.addr: 0x1024, .size: 36}, |
| 142 | .hw: {{.addr: 0x1000, .size: 128}} |
| 143 | }, |
| 144 | { |
| 145 | .user: {.addr: 0x1000, .size: 192}, |
| 146 | .hw: {{.addr: 0x1000, .size: 256}} |
| 147 | }, |
| 148 | { |
| 149 | .user: {.addr: 0x1080, .size: 192}, |
| 150 | .hw: {{.addr: 0x1000, .size: 512}} |
| 151 | }, |
| 152 | |
| 153 | // In this case, our aligned size is 128, and increasing it to 256 |
| 154 | // still can't watch the requested region. The algorithm |
| 155 | // falls back to using two 128 byte watchpoints. |
| 156 | // The alternative would be to use a 1024B watchpoint |
| 157 | // starting at 0x1000, to watch this 120 byte user request. |
| 158 | // |
| 159 | // This still isn't ideal. The user is asking to watch 0x12e0-1358 |
| 160 | // and could be optimally handled by a |
| 161 | // 16-byte watchpoint at 0x12e0 and a 128-byte watchpoint at 0x1300 |
| 162 | { |
| 163 | .user: {.addr: 0x12e0, .size: 120}, |
| 164 | .hw: {{.addr: 0x1280, .size: 128}, {.addr: 0x1300, .size: 128}} |
| 165 | }, |
| 166 | }; |
| 167 | // clang-format on |
| 168 | for (testcase test : twogig_max) { |
| 169 | addr_t user_addr = test.user.addr; |
| 170 | size_t user_size = test.user.size; |
| 171 | size_t min_byte_size = 1; |
| 172 | size_t max_byte_size = INT32_MAX; |
| 173 | size_t address_byte_size = 8; |
| 174 | auto result = WatchpointAlgorithmsTest::PowerOf2Watchpoints( |
| 175 | user_addr, user_size, min_byte_size, max_byte_size, address_byte_size); |
| 176 | |
| 177 | check_testcase(test, result, min_byte_size, max_byte_size, |
| 178 | address_byte_size); |
| 179 | } |
| 180 | |
| 181 | } |
| 182 | |