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