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