1 // Copyright 2023, VIXL authors
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright notice,
8 // this list of conditions and the following disclaimer.
9 // * Redistributions in binary form must reproduce the above copyright notice,
10 // this list of conditions and the following disclaimer in the documentation
11 // and/or other materials provided with the distribution.
12 // * Neither the name of ARM Limited nor the names of its contributors may be
13 // used to endorse or promote products derived from this software without
14 // specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
28
29 #include "debugger-aarch64.h"
30
31 #include <cerrno>
32 #include <cmath>
33 #include <cstring>
34 #include <errno.h>
35 #include <limits>
36 #include <unistd.h>
37
38 namespace vixl {
39 namespace aarch64 {
40
41
Debugger(Simulator* sim)42 Debugger::Debugger(Simulator* sim)
43 : sim_(sim)
44 #ifdef VIXL_USE_PANDA_ALLOC
45 , debugger_cmds_(sim->GetAllocator().Adapter())
46 , breakpoints_(sim->GetAllocator().Adapter())
47 #endif
48 , input_stream_(&std::cin)
49 , ostream_(sim->GetOutputStream()) {
50 // Register all basic debugger commands.
51 RegisterCmd<HelpCmd>();
52 RegisterCmd<BreakCmd>();
53 RegisterCmd<StepCmd>();
54 RegisterCmd<ContinueCmd>();
55 RegisterCmd<PrintCmd>();
56 RegisterCmd<TraceCmd>();
57 RegisterCmd<GdbCmd>();
58 }
59
60
61 template <class T>
RegisterCmd()62 void Debugger::RegisterCmd() {
63 #ifndef VIXL_USE_PANDA_ALLOC
64 auto new_command = std::make_unique<T>(sim_);
65 #else
66 auto* new_command = sim_->GetAllocator().New<T>(sim_);
67 #endif
68
69 // Check that the new command word and alias, don't already exist.
70 std::string_view new_cmd_word = new_command->GetCommandWord();
71 std::string_view new_cmd_alias = new_command->GetCommandAlias();
72 for (const auto& cmd : debugger_cmds_) {
73 std::string_view cmd_word = cmd->GetCommandWord();
74 std::string_view cmd_alias = cmd->GetCommandAlias();
75
76 if (new_cmd_word == cmd_word) {
77 VIXL_ABORT_WITH_MSG("Command word matches an existing command word.");
78 } else if (new_cmd_word == cmd_alias) {
79 VIXL_ABORT_WITH_MSG("Command word matches an existing command alias.");
80 }
81
82 if (new_cmd_alias != "") {
83 if (new_cmd_alias == cmd_word) {
84 VIXL_ABORT_WITH_MSG("Command alias matches an existing command word.");
85 } else if (new_cmd_alias == cmd_alias) {
86 VIXL_ABORT_WITH_MSG("Command alias matches an existing command alias.");
87 }
88 }
89 }
90
91 #ifndef VIXL_USE_PANDA_ALLOC
92 debugger_cmds_.push_back(std::move(new_command));
93 #else
94 debugger_cmds_.push_back(new_command);
95 #endif
96 }
97
98
IsAtBreakpoint() const99 bool Debugger::IsAtBreakpoint() const {
100 return IsBreakpoint(reinterpret_cast<uint64_t>(sim_->ReadPc()));
101 }
102
103
Debug()104 void Debugger::Debug() {
105 DebugReturn done = DebugContinue;
106 while (done == DebugContinue) {
107 // Disassemble the next instruction to execute.
108 #ifndef PANDA_BUILD
109 PrintDisassembler print_disasm = PrintDisassembler(ostream_);
110 #else
111 PrintDisassembler print_disasm = PrintDisassembler(sim_->GetAllocator(), ostream_);
112 #endif
113 print_disasm.Disassemble(sim_->ReadPc());
114
115 // Read the command line.
116 fprintf(ostream_, "sim> ");
117 std::string line;
118 std::getline(*input_stream_, line);
119
120 // Remove all control characters from the command string.
121 line.erase(std::remove_if(line.begin(),
122 line.end(),
123 [](char c) { return std::iscntrl(c); }),
124 line.end());
125
126 // Assume input from std::cin has already been output (e.g: by a terminal)
127 // but input from elsewhere (e.g: from a testing input stream) has not.
128 if (input_stream_ != &std::cin) {
129 fprintf(ostream_, "%s\n", line.c_str());
130 }
131
132 // Parse the command into tokens.
133 std::vector<std::string> tokenized_cmd = Tokenize(line);
134 if (!tokenized_cmd.empty()) {
135 done = ExecDebugCommand(tokenized_cmd);
136 }
137 }
138 }
139
140
ParseUint64String(std::string_view uint64_str, int base)141 std::optional<uint64_t> Debugger::ParseUint64String(std::string_view uint64_str,
142 int base) {
143 // Clear any previous errors.
144 errno = 0;
145
146 // strtoull uses 0 to indicate that no conversion was possible so first
147 // check that the string isn't zero.
148 if (IsZeroUint64String(uint64_str, base)) {
149 return 0;
150 }
151
152 // Cannot use stoi as it might not be possible to use exceptions.
153 char* end;
154 uint64_t value = std::strtoull(uint64_str.data(), &end, base);
155 if (value == 0 || *end != '\0' || errno == ERANGE) {
156 return std::nullopt;
157 }
158
159 return value;
160 }
161
162
ParseRegString( std::string_view reg_str)163 std::optional<Debugger::RegisterParsedFormat> Debugger::ParseRegString(
164 std::string_view reg_str) {
165 // A register should only have 2 (e.g: X0) or 3 (e.g: X31) characters.
166 if (reg_str.size() < 2 || reg_str.size() > 3) {
167 return std::nullopt;
168 }
169
170 // Check for aliases of registers.
171 if (reg_str == "lr") {
172 return {{'X', kLinkRegCode}};
173 } else if (reg_str == "sp") {
174 return {{'X', kSpRegCode}};
175 }
176
177 unsigned max_reg_num;
178 char reg_prefix = std::toupper(reg_str.front());
179 switch (reg_prefix) {
180 case 'W':
181 VIXL_FALLTHROUGH();
182 case 'X':
183 max_reg_num = kNumberOfRegisters - 1;
184 break;
185 case 'V':
186 max_reg_num = kNumberOfVRegisters - 1;
187 break;
188 case 'Z':
189 max_reg_num = kNumberOfZRegisters - 1;
190 break;
191 case 'P':
192 max_reg_num = kNumberOfPRegisters - 1;
193 break;
194 default:
195 return std::nullopt;
196 }
197
198 std::string_view str_code = reg_str.substr(1, reg_str.size());
199 auto reg_code = ParseUint64String(str_code, 10);
200 if (!reg_code) {
201 return std::nullopt;
202 }
203
204 if (*reg_code > max_reg_num) {
205 return std::nullopt;
206 }
207
208 return {{reg_prefix, *reg_code}};
209 }
210
211
PrintUsage()212 void Debugger::PrintUsage() {
213 for (const auto& cmd : debugger_cmds_) {
214 // Print commands in the following format:
215 // foo / f
216 // foo <arg>
217 // A description of the foo command.
218 //
219
220 std::string_view cmd_word = cmd->GetCommandWord();
221 std::string_view cmd_alias = cmd->GetCommandAlias();
222 if (cmd_alias != "") {
223 fprintf(ostream_, "%s / %s\n", cmd_word.data(), cmd_alias.data());
224 } else {
225 fprintf(ostream_, "%s\n", cmd_word.data());
226 }
227
228 std::string_view args_str = cmd->GetArgsString();
229 if (args_str != "") {
230 fprintf(ostream_, "\t%s %s\n", cmd_word.data(), args_str.data());
231 }
232
233 std::string_view description = cmd->GetDescription();
234 if (description != "") {
235 fprintf(ostream_, "\t%s\n", description.data());
236 }
237 }
238 }
239
240
Tokenize(std::string_view input_line, char separator)241 std::vector<std::string> Debugger::Tokenize(std::string_view input_line,
242 char separator) {
243 std::vector<std::string> words;
244
245 if (input_line.empty()) {
246 return words;
247 }
248
249 for (auto separator_pos = input_line.find(separator);
250 separator_pos != input_line.npos;
251 separator_pos = input_line.find(separator)) {
252 // Skip consecutive, repeated separators.
253 if (separator_pos != 0) {
254 words.push_back(std::string{input_line.substr(0, separator_pos)});
255 }
256
257 // Remove characters up to and including the separator.
258 input_line.remove_prefix(separator_pos + 1);
259 }
260
261 // Add the rest of the string to the vector.
262 words.push_back(std::string{input_line});
263
264 return words;
265 }
266
267
ExecDebugCommand( const std::vector<std::string>& tokenized_cmd)268 DebugReturn Debugger::ExecDebugCommand(
269 const std::vector<std::string>& tokenized_cmd) {
270 std::string cmd_word = tokenized_cmd.front();
271 for (const auto& cmd : debugger_cmds_) {
272 if (cmd_word == cmd->GetCommandWord() ||
273 cmd_word == cmd->GetCommandAlias()) {
274 const std::vector<std::string> args(tokenized_cmd.begin() + 1,
275 tokenized_cmd.end());
276
277 // Call the handler for the command and pass the arguments.
278 return cmd->Action(args);
279 }
280 }
281
282 fprintf(ostream_, "Error: command '%s' not found\n", cmd_word.c_str());
283 return DebugContinue;
284 }
285
286
IsZeroUint64String(std::string_view uint64_str, int base)287 bool Debugger::IsZeroUint64String(std::string_view uint64_str, int base) {
288 // Remove any hex prefixes.
289 if (base == 0 || base == 16) {
290 std::string_view prefix = uint64_str.substr(0, 2);
291 if (prefix == "0x" || prefix == "0X") {
292 uint64_str.remove_prefix(2);
293 }
294 }
295
296 if (uint64_str.empty()) {
297 return false;
298 }
299
300 // Check all remaining digits in the string for anything other than zero.
301 for (char c : uint64_str) {
302 if (c != '0') {
303 return false;
304 }
305 }
306
307 return true;
308 }
309
310
DebuggerCmd(Simulator* sim, std::string_view cmd_word, std::string_view cmd_alias, std::string_view args_str, std::string_view description)311 DebuggerCmd::DebuggerCmd(Simulator* sim,
312 std::string_view cmd_word,
313 std::string_view cmd_alias,
314 std::string_view args_str,
315 std::string_view description)
316 :
317 #ifdef PANDA_BUILD
318 allocator_(sim->GetAllocator()),
319 #endif
320 sim_(sim),
321 ostream_(sim->GetOutputStream()),
322 command_word_(cmd_word, allocator_.Adapter()),
323 command_alias_(cmd_alias, allocator_.Adapter()),
324 args_str_(args_str, allocator_.Adapter()),
325 description_(description, allocator_.Adapter()) {}
326
327
Action(const std::vector<std::string>& args)328 DebugReturn HelpCmd::Action(const std::vector<std::string>& args) {
329 USE(args);
330 sim_->GetDebugger()->PrintUsage();
331 return DebugContinue;
332 }
333
334
Action(const std::vector<std::string>& args)335 DebugReturn BreakCmd::Action(const std::vector<std::string>& args) {
336 if (args.size() != 1) {
337 fprintf(ostream_, "Error: Use `break <address>` to set a breakpoint\n");
338 return DebugContinue;
339 }
340
341 std::string arg = args.front();
342 auto break_addr = Debugger::ParseUint64String(arg);
343 if (!break_addr) {
344 fprintf(ostream_, "Error: Use `break <address>` to set a breakpoint\n");
345 return DebugContinue;
346 }
347
348 if (sim_->GetDebugger()->IsBreakpoint(*break_addr)) {
349 sim_->GetDebugger()->RemoveBreakpoint(*break_addr);
350 fprintf(ostream_,
351 "Breakpoint successfully removed at: 0x%" PRIx64 "\n",
352 *break_addr);
353 } else {
354 sim_->GetDebugger()->RegisterBreakpoint(*break_addr);
355 fprintf(ostream_,
356 "Breakpoint successfully added at: 0x%" PRIx64 "\n",
357 *break_addr);
358 }
359
360 return DebugContinue;
361 }
362
363
Action(const std::vector<std::string>& args)364 DebugReturn StepCmd::Action(const std::vector<std::string>& args) {
365 if (args.size() > 1) {
366 fprintf(ostream_,
367 "Error: use `step [number]` to step an optional number of"
368 " instructions\n");
369 return DebugContinue;
370 }
371
372 // Step 1 instruction by default.
373 std::optional<uint64_t> number_of_instructions_to_execute{1};
374
375 if (args.size() == 1) {
376 // Parse the argument to step that number of instructions.
377 std::string arg = args.front();
378 number_of_instructions_to_execute = Debugger::ParseUint64String(arg);
379 if (!number_of_instructions_to_execute) {
380 fprintf(ostream_,
381 "Error: use `step [number]` to step an optional number of"
382 " instructions\n");
383 return DebugContinue;
384 }
385 }
386
387 while (!sim_->IsSimulationFinished() &&
388 *number_of_instructions_to_execute > 0) {
389 sim_->ExecuteInstruction();
390 (*number_of_instructions_to_execute)--;
391
392 // The first instruction has already been printed by Debug() so only
393 // enable instruction tracing after the first instruction has been
394 // executed.
395 sim_->SetTraceParameters(sim_->GetTraceParameters() | LOG_DISASM);
396 }
397
398 // Disable instruction tracing after all instructions have been executed.
399 sim_->SetTraceParameters(sim_->GetTraceParameters() & ~LOG_DISASM);
400
401 if (sim_->IsSimulationFinished()) {
402 fprintf(ostream_,
403 "Debugger at the end of simulation, leaving simulator...\n");
404 return DebugExit;
405 }
406
407 return DebugContinue;
408 }
409
410
Action(const std::vector<std::string>& args)411 DebugReturn ContinueCmd::Action(const std::vector<std::string>& args) {
412 USE(args);
413
414 fprintf(ostream_, "Continuing...\n");
415
416 if (sim_->GetDebugger()->IsAtBreakpoint()) {
417 // This breakpoint has already been hit, so execute it before continuing.
418 sim_->ExecuteInstruction();
419 }
420
421 return DebugExit;
422 }
423
424
Action(const std::vector<std::string>& args)425 DebugReturn PrintCmd::Action(const std::vector<std::string>& args) {
426 if (args.size() != 1) {
427 fprintf(ostream_,
428 "Error: use `print <register|all>` to print the contents of a"
429 " specific register or all registers.\n");
430 return DebugContinue;
431 }
432
433 if (args.front() == "all") {
434 sim_->PrintRegisters();
435 sim_->PrintZRegisters();
436 } else if (args.front() == "system") {
437 sim_->PrintSystemRegisters();
438 } else if (args.front() == "ffr") {
439 sim_->PrintFFR();
440 } else {
441 auto reg = Debugger::ParseRegString(args.front());
442 if (!reg) {
443 fprintf(ostream_,
444 "Error: incorrect register format, use e.g: X0, x0, etc...\n");
445 return DebugContinue;
446 }
447
448 // Ensure the stack pointer is printed instead of the zero register.
449 if ((*reg).second == kSpRegCode) {
450 (*reg).second = kSPRegInternalCode;
451 }
452
453 // Registers are printed in different ways depending on their type.
454 switch ((*reg).first) {
455 case 'W':
456 sim_->PrintRegister(
457 (*reg).second,
458 static_cast<Simulator::PrintRegisterFormat>(
459 Simulator::PrintRegisterFormat::kPrintWReg |
460 Simulator::PrintRegisterFormat::kPrintRegPartial));
461 break;
462 case 'X':
463 sim_->PrintRegister((*reg).second,
464 Simulator::PrintRegisterFormat::kPrintXReg);
465 break;
466 case 'V':
467 sim_->PrintVRegister((*reg).second);
468 break;
469 case 'Z':
470 sim_->PrintZRegister((*reg).second);
471 break;
472 case 'P':
473 sim_->PrintPRegister((*reg).second);
474 break;
475 default:
476 // ParseRegString should only allow valid register characters.
477 VIXL_UNREACHABLE();
478 }
479 }
480
481 return DebugContinue;
482 }
483
484
Action(const std::vector<std::string>& args)485 DebugReturn TraceCmd::Action(const std::vector<std::string>& args) {
486 if (args.size() != 0) {
487 fprintf(ostream_, "Error: use `trace` to toggle tracing of registers.\n");
488 return DebugContinue;
489 }
490
491 int trace_params = sim_->GetTraceParameters();
492 if ((trace_params & LOG_ALL) != LOG_ALL) {
493 fprintf(ostream_,
494 "Enabling disassembly, registers and memory write tracing\n");
495 sim_->SetTraceParameters(trace_params | LOG_ALL);
496 } else {
497 fprintf(ostream_,
498 "Disabling disassembly, registers and memory write tracing\n");
499 sim_->SetTraceParameters(trace_params & ~LOG_ALL);
500 }
501
502 return DebugContinue;
503 }
504
505
Action(const std::vector<std::string>& args)506 DebugReturn GdbCmd::Action(const std::vector<std::string>& args) {
507 if (args.size() != 0) {
508 fprintf(ostream_,
509 "Error: use `gdb` to enter GDB from the simulator debugger.\n");
510 return DebugContinue;
511 }
512
513 HostBreakpoint();
514 return DebugContinue;
515 }
516
517
518 } // namespace aarch64
519 } // namespace vixl
520
521 #endif // VIXL_INCLUDE_SIMULATOR_AARCH64
522