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