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