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
38namespace vixl {
39namespace aarch64 {
40
41
42Debugger::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
61template <class T>
62void 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
99bool Debugger::IsAtBreakpoint() const {
100  return IsBreakpoint(reinterpret_cast<uint64_t>(sim_->ReadPc()));
101}
102
103
104void 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
141std::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
163std::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
212void 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
241std::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
268DebugReturn 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
287bool 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
311DebuggerCmd::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
328DebugReturn HelpCmd::Action(const std::vector<std::string>& args) {
329  USE(args);
330  sim_->GetDebugger()->PrintUsage();
331  return DebugContinue;
332}
333
334
335DebugReturn 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
364DebugReturn 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
411DebugReturn 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
425DebugReturn 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
485DebugReturn 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
506DebugReturn 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