|
16 | 16 | defined(__unix__) || \
|
17 | 17 | defined(__CYGWIN__) || \
|
18 | 18 | defined(__MACH__)
|
| 19 | +#define RUN_GDB_API_TESTS |
| 20 | +#endif |
19 | 21 | // clang-format on
|
20 | 22 |
|
| 23 | +#ifdef RUN_GDB_API_TESTS |
| 24 | + |
21 | 25 | #include <cstdio>
|
22 | 26 | #include <cstdlib>
|
23 | 27 | #include <regex>
|
|
27 | 31 | #include <iostream>
|
28 | 32 |
|
29 | 33 | #include <memory-analyzer/gdb_api.cpp>
|
30 |
| -#include <string> |
31 | 34 |
|
32 |
| -SCENARIO( |
33 |
| - "gdb_apit uses regex as expected for memory", |
34 |
| - "[core][memory-analyzer]") |
| 35 | +void compile_test_file() |
35 | 36 | {
|
36 |
| - gdb_apit gdb_api(""); |
37 |
| - GIVEN("The result of a struct pointer") |
38 |
| - { |
39 |
| - const std::string line = "$2 = (struct cycle_buffer *) 0x601050 <buffer>"; |
40 |
| - THEN("the result matches the memory address in the output") |
41 |
| - { |
42 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x601050"); |
43 |
| - } |
44 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
45 |
| - { |
46 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x601050"); |
47 |
| - } |
48 |
| - } |
| 37 | + std::string test_file("memory-analyzer/test.c"); |
49 | 38 |
|
50 |
| - GIVEN("The result of a typedef pointer") |
51 |
| - { |
52 |
| - const std::string line = "$4 = (cycle_buffer_entry_t *) 0x602010"; |
53 |
| - THEN("the result matches the memory address in the output") |
54 |
| - { |
55 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x602010"); |
56 |
| - } |
57 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
58 |
| - { |
59 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x602010"); |
60 |
| - } |
61 |
| - } |
| 39 | + std::string cmd("gcc -g -o test "); |
| 40 | + cmd += test_file; |
62 | 41 |
|
63 |
| - GIVEN("The result of a char pointer") |
64 |
| - { |
65 |
| - const std::string line = "$5 = 0x400734 \"snow\""; |
66 |
| - THEN("the result matches the memory address in the output") |
67 |
| - { |
68 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x400734"); |
69 |
| - } |
70 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
71 |
| - { |
72 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x400734"); |
73 |
| - } |
74 |
| - } |
| 42 | + const int r = system(cmd.c_str()); |
| 43 | + REQUIRE(!r); |
| 44 | +} |
75 | 45 |
|
76 |
| - GIVEN("The result of a null pointer") |
| 46 | +class gdb_api_testt : public gdb_apit |
| 47 | +{ |
| 48 | + explicit gdb_api_testt(const char *binary) : gdb_apit(binary) |
77 | 49 | {
|
78 |
| - const std::string line = "$2 = 0x0"; |
79 |
| - THEN("the result matches the memory address in the output") |
80 |
| - { |
81 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x0"); |
82 |
| - } |
83 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
84 |
| - { |
85 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x0"); |
86 |
| - } |
87 | 50 | }
|
88 | 51 |
|
89 |
| - GIVEN("The result of a char pointer at very low address") |
90 |
| - { |
91 |
| - const std::string line = "$34 = 0x007456 \"snow\""; |
92 |
| - THEN("the result matches the memory address and not nullpointer") |
93 |
| - { |
94 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x007456"); |
95 |
| - } |
96 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
97 |
| - { |
98 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x007456"); |
99 |
| - } |
100 |
| - } |
| 52 | + friend void gdb_api_internals_test(); |
| 53 | +}; |
101 | 54 |
|
102 |
| - GIVEN("The result of a char pointer with some more whitespaces") |
103 |
| - { |
104 |
| - const std::string line = "$12 = 0x400752 \"thunder storm\"\n"; |
105 |
| - THEN("the result matches the memory address in the output") |
106 |
| - { |
107 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x400752"); |
108 |
| - } |
109 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
110 |
| - { |
111 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x400752"); |
112 |
| - } |
113 |
| - } |
| 55 | +void gdb_api_internals_test() |
| 56 | +{ |
| 57 | + compile_test_file(); |
114 | 58 |
|
115 |
| - GIVEN("The result of an array pointer") |
| 59 | + SECTION("parse gdb output record") |
116 | 60 | {
|
117 |
| - const std::string line = "$5 = (struct a_sub_type_t (*)[4]) 0x602080"; |
118 |
| - THEN("the result matches the memory address in the output") |
119 |
| - { |
120 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x602080"); |
121 |
| - } |
122 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
123 |
| - { |
124 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x602080"); |
125 |
| - } |
126 |
| - } |
| 61 | + gdb_api_testt gdb_api("test"); |
127 | 62 |
|
128 |
| - GIVEN("A constant struct pointer pointing to 0x0") |
129 |
| - { |
130 |
| - const std::string line = "$3 = (const struct name *) 0x0"; |
131 |
| - THEN("the returned memory address should be 0x0") |
132 |
| - { |
133 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x0"); |
134 |
| - } |
135 |
| - } |
| 63 | + gdb_api_testt::gdb_output_recordt gor; |
136 | 64 |
|
137 |
| - GIVEN("An enum address") |
138 |
| - { |
139 |
| - const std::string line = "$2 = (char *(*)[5]) 0x7e5500 <abc>"; |
140 |
| - THEN("the returned address is the address of the enum") |
141 |
| - { |
142 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x7e5500"); |
143 |
| - } |
| 65 | + gor = gdb_api.parse_gdb_output_record( |
| 66 | + "a = \"1\", b = \"2\", c = {1, 2}, d = [3, 4], e=\"0x0\""); |
| 67 | + |
| 68 | + REQUIRE(gor["a"] == "1"); |
| 69 | + REQUIRE(gor["b"] == "2"); |
| 70 | + REQUIRE(gor["c"] == "{1, 2}"); |
| 71 | + REQUIRE(gor["d"] == "[3, 4]"); |
| 72 | + REQUIRE(gor["e"] == "0x0"); |
144 | 73 | }
|
145 | 74 |
|
146 |
| - GIVEN("The result of an int pointer") |
| 75 | + SECTION("read a line from an input stream") |
147 | 76 | {
|
148 |
| - const std::string line = "$1 = (int *) 0x601088 <e>\n"; |
149 |
| - THEN("the result is the value of memory address of the int pointer") |
150 |
| - { |
151 |
| - REQUIRE(gdb_api.extract_memory(line) == "0x601088"); |
152 |
| - } |
153 |
| - THEN("adding '(gdb) ' to the line doesn't have an influence") |
154 |
| - { |
155 |
| - REQUIRE(gdb_api.extract_memory("(gdb) " + line) == "0x601088"); |
156 |
| - } |
| 77 | + gdb_api_testt gdb_api("test"); |
| 78 | + |
| 79 | + FILE *f = fopen("memory-analyzer/input.txt", "r"); |
| 80 | + gdb_api.input_stream = f; |
| 81 | + |
| 82 | + std::string line; |
| 83 | + |
| 84 | + line = gdb_api.read_next_line(); |
| 85 | + REQUIRE(line == "abc\n"); |
| 86 | + |
| 87 | + line = gdb_api.read_next_line(); |
| 88 | + REQUIRE(line == std::string(1120, 'a') + "\n"); |
| 89 | + |
| 90 | + line = gdb_api.read_next_line(); |
| 91 | + REQUIRE(line == "xyz"); |
157 | 92 | }
|
158 | 93 |
|
159 |
| - GIVEN("Non matching result") |
| 94 | + SECTION("start and exit gdb") |
160 | 95 | {
|
161 |
| - const std::string line = "Something that must not match 0x605940"; |
162 |
| - THEN("an exception is thrown") |
163 |
| - { |
164 |
| - REQUIRE_THROWS_AS( |
165 |
| - gdb_api.extract_memory(line), gdb_interaction_exceptiont); |
166 |
| - } |
| 96 | + gdb_api_testt gdb_api("test"); |
| 97 | + |
| 98 | + gdb_api.create_gdb_process(); |
| 99 | + |
| 100 | + // check input and output streams |
| 101 | + REQUIRE(!ferror(gdb_api.input_stream)); |
| 102 | + REQUIRE(!ferror(gdb_api.output_stream)); |
| 103 | + |
| 104 | + gdb_api.terminate_gdb_process(); |
167 | 105 | }
|
168 | 106 | }
|
169 | 107 |
|
170 |
| -SCENARIO( |
171 |
| - "gdb_apit uses regex as expected for value extraction", |
172 |
| - "[core][memory-analyzer]") |
| 108 | +#endif |
| 109 | + |
| 110 | +TEST_CASE("gdb api internals test", "[core][memory-analyzer]") |
173 | 111 | {
|
174 |
| - gdb_apit gdb_api(""); |
175 |
| - GIVEN("An integer value") |
176 |
| - { |
177 |
| - const std::string line = "$90 = 100"; |
178 |
| - THEN("the result schould be '100'") |
179 |
| - { |
180 |
| - REQUIRE(gdb_api.extract_value(line) == "100"); |
181 |
| - } |
182 |
| - } |
| 112 | +#ifdef RUN_GDB_API_TESTS |
| 113 | + gdb_api_internals_test(); |
| 114 | +#endif |
| 115 | +} |
183 | 116 |
|
184 |
| - GIVEN("A string value") |
185 |
| - { |
186 |
| - const std::string line = "$5 = 0x602010 \"snow\""; |
187 |
| - THEN("the result should be 'snow'") |
188 |
| - { |
189 |
| - REQUIRE(gdb_api.extract_value(line) == "snow"); |
190 |
| - } |
191 |
| - } |
| 117 | +TEST_CASE("gdb api test", "[core][memory-analyzer]") |
| 118 | +{ |
| 119 | +#ifdef RUN_GDB_API_TESTS |
| 120 | + compile_test_file(); |
192 | 121 |
|
193 |
| - GIVEN("A string with withe spaces") |
194 | 122 | {
|
195 |
| - const std::string line = "$1323 = 0x1243253 \"thunder storm\"\n"; |
196 |
| - THEN("the result should be 'thunder storm'") |
| 123 | + gdb_apit gdb_api("test", true); |
| 124 | + gdb_api.create_gdb_process(); |
| 125 | + |
| 126 | + try |
197 | 127 | {
|
198 |
| - REQUIRE(gdb_api.extract_value(line) == "thunder storm"); |
199 |
| - } |
200 |
| - } |
| 128 | + const bool r = gdb_api.run_gdb_to_breakpoint("checkpoint"); |
| 129 | + REQUIRE(r); |
201 | 130 |
|
202 |
| - GIVEN("A byte value") |
203 |
| - { |
204 |
| - const std::string line = "$2 = 243 '\363'"; |
205 |
| - THEN("the result should be 243") |
| 131 | + gdb_api.terminate_gdb_process(); |
| 132 | + } |
| 133 | + catch(const gdb_interaction_exceptiont &e) |
206 | 134 | {
|
207 |
| - REQUIRE(gdb_api.extract_value(line) == "243"); |
| 135 | + std::cerr << "warning: cannot fully unit test GDB API as program cannot " |
| 136 | + << "be run with gdb\n"; |
| 137 | + std::cerr << "warning: this may be due to not having the required " |
| 138 | + << "permissions (e.g., to invoke ptrace() or to disable ASLR)" |
| 139 | + << "\n"; |
| 140 | + std::cerr << "gdb_interaction_exceptiont:" << '\n'; |
| 141 | + std::cerr << e.what() << '\n'; |
| 142 | + |
| 143 | + std::ifstream file("gdb.txt"); |
| 144 | + CHECK_RETURN(file.is_open()); |
| 145 | + std::string line; |
| 146 | + |
| 147 | + std::cerr << "=== gdb log begin ===\n"; |
| 148 | + |
| 149 | + while(getline(file, line)) |
| 150 | + { |
| 151 | + std::cerr << line << '\n'; |
| 152 | + } |
| 153 | + |
| 154 | + file.close(); |
| 155 | + |
| 156 | + std::cerr << "=== gdb log end ===\n"; |
| 157 | + |
| 158 | + gdb_api.terminate_gdb_process(); |
| 159 | + |
| 160 | + return; |
208 | 161 | }
|
209 | 162 | }
|
210 | 163 |
|
211 |
| - GIVEN("A negative int value") |
| 164 | + gdb_apit gdb_api("test"); |
| 165 | + gdb_api.create_gdb_process(); |
| 166 | + |
| 167 | + SECTION("breakpoint is hit") |
212 | 168 | {
|
213 |
| - const std::string line = "$8 = -32"; |
214 |
| - THEN("the result should be -32") |
215 |
| - { |
216 |
| - REQUIRE(gdb_api.extract_value(line) == "-32"); |
217 |
| - } |
| 169 | + const bool r = gdb_api.run_gdb_to_breakpoint("checkpoint"); |
| 170 | + REQUIRE(r); |
218 | 171 | }
|
219 | 172 |
|
220 |
| - GIVEN("An enum value") |
| 173 | + SECTION("breakpoint is not hit") |
221 | 174 | {
|
222 |
| - const std::string line = "$1 = INFO"; |
223 |
| - THEN("the result should be INFO") |
224 |
| - { |
225 |
| - REQUIRE(gdb_api.extract_value(line) == "INFO"); |
226 |
| - } |
| 175 | + const bool r = gdb_api.run_gdb_to_breakpoint("checkpoint2"); |
| 176 | + REQUIRE(!r); |
227 | 177 | }
|
228 | 178 |
|
229 |
| - GIVEN("A void pointer value") |
| 179 | + SECTION("breakpoint does not exist") |
230 | 180 | {
|
231 |
| - const std::string line = "$6 = (const void *) 0x71"; |
232 |
| - THEN("the requried result should be 0x71") |
233 |
| - { |
234 |
| - REQUIRE(gdb_api.extract_value(line) == "0x71"); |
235 |
| - } |
| 181 | + REQUIRE_THROWS_AS( |
| 182 | + gdb_api.run_gdb_to_breakpoint("checkpoint3"), gdb_interaction_exceptiont); |
236 | 183 | }
|
237 | 184 |
|
238 |
| - GIVEN("A gdb response that contains 'cannot access memory'") |
| 185 | + SECTION("query memory") |
239 | 186 | {
|
240 |
| - const std::string line = "Cannot access memory at address 0x71"; |
241 |
| - THEN("a gdb_inaccesible_memoryt excepition must be raised") |
| 187 | + const bool r = gdb_api.run_gdb_to_breakpoint("checkpoint"); |
| 188 | + REQUIRE(r); |
| 189 | + |
| 190 | + REQUIRE(gdb_api.get_value("x") == "8"); |
| 191 | + REQUIRE(gdb_api.get_value("s") == "abc"); |
| 192 | + |
| 193 | + const std::regex regex(R"(0x[1-9a-f][0-9a-f]*)"); |
| 194 | + |
242 | 195 | {
|
243 |
| - REQUIRE_THROWS_AS( |
244 |
| - gdb_api.extract_value(line), gdb_inaccessible_memory_exceptiont); |
| 196 | + std::string address = gdb_api.get_memory("p"); |
| 197 | + REQUIRE(std::regex_match(address, regex)); |
245 | 198 | }
|
246 |
| - } |
247 | 199 |
|
248 |
| - GIVEN("A value that must not match") |
249 |
| - { |
250 |
| - const std::string line = "$90 = must not match 20"; |
251 |
| - THEN("an exception is raised") |
252 | 200 | {
|
253 |
| - REQUIRE_THROWS_AS( |
254 |
| - gdb_api.extract_value(line), gdb_interaction_exceptiont); |
| 201 | + std::string address = gdb_api.get_memory("vp"); |
| 202 | + REQUIRE(std::regex_match(address, regex)); |
255 | 203 | }
|
256 | 204 | }
|
257 |
| -} |
| 205 | + |
| 206 | + gdb_api.terminate_gdb_process(); |
258 | 207 | #endif
|
| 208 | +} |
0 commit comments