Path: blob/master/dep/vixl/src/aarch64/debugger-aarch64.cc
4261 views
// Copyright 2023, VIXL authors1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are met:5//6// * Redistributions of source code must retain the above copyright notice,7// this list of conditions and the following disclaimer.8// * Redistributions in binary form must reproduce the above copyright notice,9// this list of conditions and the following disclaimer in the documentation10// and/or other materials provided with the distribution.11// * Neither the name of ARM Limited nor the names of its contributors may be12// used to endorse or promote products derived from this software without13// specific prior written permission.14//15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND16// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED17// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE18// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE19// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR21// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER22// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,23// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE24// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2526#ifdef VIXL_INCLUDE_SIMULATOR_AARCH642728#include "debugger-aarch64.h"2930#include <cerrno>31#include <cmath>32#include <cstring>33#include <errno.h>34#include <limits>35#include <unistd.h>3637namespace vixl {38namespace aarch64 {394041Debugger::Debugger(Simulator* sim)42: sim_(sim), input_stream_(&std::cin), ostream_(sim->GetOutputStream()) {43// Register all basic debugger commands.44RegisterCmd<HelpCmd>();45RegisterCmd<BreakCmd>();46RegisterCmd<StepCmd>();47RegisterCmd<ContinueCmd>();48RegisterCmd<PrintCmd>();49RegisterCmd<TraceCmd>();50RegisterCmd<GdbCmd>();51}525354template <class T>55void Debugger::RegisterCmd() {56auto new_command = std::make_unique<T>(sim_);5758// Check that the new command word and alias, don't already exist.59std::string_view new_cmd_word = new_command->GetCommandWord();60std::string_view new_cmd_alias = new_command->GetCommandAlias();61for (const auto& cmd : debugger_cmds_) {62std::string_view cmd_word = cmd->GetCommandWord();63std::string_view cmd_alias = cmd->GetCommandAlias();6465if (new_cmd_word == cmd_word) {66VIXL_ABORT_WITH_MSG("Command word matches an existing command word.");67} else if (new_cmd_word == cmd_alias) {68VIXL_ABORT_WITH_MSG("Command word matches an existing command alias.");69}7071if (new_cmd_alias != "") {72if (new_cmd_alias == cmd_word) {73VIXL_ABORT_WITH_MSG("Command alias matches an existing command word.");74} else if (new_cmd_alias == cmd_alias) {75VIXL_ABORT_WITH_MSG("Command alias matches an existing command alias.");76}77}78}7980debugger_cmds_.push_back(std::move(new_command));81}828384bool Debugger::IsAtBreakpoint() const {85return IsBreakpoint(reinterpret_cast<uint64_t>(sim_->ReadPc()));86}878889void Debugger::Debug() {90DebugReturn done = DebugContinue;91while (done == DebugContinue) {92// Disassemble the next instruction to execute.93PrintDisassembler print_disasm = PrintDisassembler(ostream_);94print_disasm.Disassemble(sim_->ReadPc());9596// Read the command line.97fprintf(ostream_, "sim> ");98std::string line;99std::getline(*input_stream_, line);100101// Remove all control characters from the command string.102line.erase(std::remove_if(line.begin(),103line.end(),104[](char c) { return std::iscntrl(c); }),105line.end());106107// 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.109if (input_stream_ != &std::cin) {110fprintf(ostream_, "%s\n", line.c_str());111}112113// Parse the command into tokens.114std::vector<std::string> tokenized_cmd = Tokenize(line);115if (!tokenized_cmd.empty()) {116done = ExecDebugCommand(tokenized_cmd);117}118}119}120121122std::optional<uint64_t> Debugger::ParseUint64String(std::string_view uint64_str,123int base) {124// Clear any previous errors.125errno = 0;126127// strtoull uses 0 to indicate that no conversion was possible so first128// check that the string isn't zero.129if (IsZeroUint64String(uint64_str, base)) {130return 0;131}132133// Cannot use stoi as it might not be possible to use exceptions.134char* end;135uint64_t value = std::strtoull(uint64_str.data(), &end, base);136if (value == 0 || *end != '\0' || errno == ERANGE) {137return std::nullopt;138}139140return value;141}142143144std::optional<Debugger::RegisterParsedFormat> Debugger::ParseRegString(145std::string_view reg_str) {146// A register should only have 2 (e.g: X0) or 3 (e.g: X31) characters.147if (reg_str.size() < 2 || reg_str.size() > 3) {148return std::nullopt;149}150151// Check for aliases of registers.152if (reg_str == "lr") {153return {{'X', kLinkRegCode}};154} else if (reg_str == "sp") {155return {{'X', kSpRegCode}};156}157158unsigned max_reg_num;159char reg_prefix = std::toupper(reg_str.front());160switch (reg_prefix) {161case 'W':162VIXL_FALLTHROUGH();163case 'X':164max_reg_num = kNumberOfRegisters - 1;165break;166case 'V':167max_reg_num = kNumberOfVRegisters - 1;168break;169case 'Z':170max_reg_num = kNumberOfZRegisters - 1;171break;172case 'P':173max_reg_num = kNumberOfPRegisters - 1;174break;175default:176return std::nullopt;177}178179std::string_view str_code = reg_str.substr(1, reg_str.size());180auto reg_code = ParseUint64String(str_code, 10);181if (!reg_code) {182return std::nullopt;183}184185if (*reg_code > max_reg_num) {186return std::nullopt;187}188189return {{reg_prefix, *reg_code}};190}191192193void Debugger::PrintUsage() {194for (const auto& cmd : debugger_cmds_) {195// Print commands in the following format:196// foo / f197// foo <arg>198// A description of the foo command.199//200201std::string_view cmd_word = cmd->GetCommandWord();202std::string_view cmd_alias = cmd->GetCommandAlias();203if (cmd_alias != "") {204fprintf(ostream_, "%s / %s\n", cmd_word.data(), cmd_alias.data());205} else {206fprintf(ostream_, "%s\n", cmd_word.data());207}208209std::string_view args_str = cmd->GetArgsString();210if (args_str != "") {211fprintf(ostream_, "\t%s %s\n", cmd_word.data(), args_str.data());212}213214std::string_view description = cmd->GetDescription();215if (description != "") {216fprintf(ostream_, "\t%s\n", description.data());217}218}219}220221222std::vector<std::string> Debugger::Tokenize(std::string_view input_line,223char separator) {224std::vector<std::string> words;225226if (input_line.empty()) {227return words;228}229230for (auto separator_pos = input_line.find(separator);231separator_pos != input_line.npos;232separator_pos = input_line.find(separator)) {233// Skip consecutive, repeated separators.234if (separator_pos != 0) {235words.push_back(std::string{input_line.substr(0, separator_pos)});236}237238// Remove characters up to and including the separator.239input_line.remove_prefix(separator_pos + 1);240}241242// Add the rest of the string to the vector.243words.push_back(std::string{input_line});244245return words;246}247248249DebugReturn Debugger::ExecDebugCommand(250const std::vector<std::string>& tokenized_cmd) {251std::string cmd_word = tokenized_cmd.front();252for (const auto& cmd : debugger_cmds_) {253if (cmd_word == cmd->GetCommandWord() ||254cmd_word == cmd->GetCommandAlias()) {255const std::vector<std::string> args(tokenized_cmd.begin() + 1,256tokenized_cmd.end());257258// Call the handler for the command and pass the arguments.259return cmd->Action(args);260}261}262263fprintf(ostream_, "Error: command '%s' not found\n", cmd_word.c_str());264return DebugContinue;265}266267268bool Debugger::IsZeroUint64String(std::string_view uint64_str, int base) {269// Remove any hex prefixes.270if (base == 0 || base == 16) {271std::string_view prefix = uint64_str.substr(0, 2);272if (prefix == "0x" || prefix == "0X") {273uint64_str.remove_prefix(2);274}275}276277if (uint64_str.empty()) {278return false;279}280281// Check all remaining digits in the string for anything other than zero.282for (char c : uint64_str) {283if (c != '0') {284return false;285}286}287288return true;289}290291292DebuggerCmd::DebuggerCmd(Simulator* sim,293std::string cmd_word,294std::string cmd_alias,295std::string args_str,296std::string description)297: sim_(sim),298ostream_(sim->GetOutputStream()),299command_word_(cmd_word),300command_alias_(cmd_alias),301args_str_(args_str),302description_(description) {}303304305DebugReturn HelpCmd::Action(const std::vector<std::string>& args) {306USE(args);307sim_->GetDebugger()->PrintUsage();308return DebugContinue;309}310311312DebugReturn BreakCmd::Action(const std::vector<std::string>& args) {313if (args.size() != 1) {314fprintf(ostream_, "Error: Use `break <address>` to set a breakpoint\n");315return DebugContinue;316}317318std::string arg = args.front();319auto break_addr = Debugger::ParseUint64String(arg);320if (!break_addr) {321fprintf(ostream_, "Error: Use `break <address>` to set a breakpoint\n");322return DebugContinue;323}324325if (sim_->GetDebugger()->IsBreakpoint(*break_addr)) {326sim_->GetDebugger()->RemoveBreakpoint(*break_addr);327fprintf(ostream_,328"Breakpoint successfully removed at: 0x%" PRIx64 "\n",329*break_addr);330} else {331sim_->GetDebugger()->RegisterBreakpoint(*break_addr);332fprintf(ostream_,333"Breakpoint successfully added at: 0x%" PRIx64 "\n",334*break_addr);335}336337return DebugContinue;338}339340341DebugReturn StepCmd::Action(const std::vector<std::string>& args) {342if (args.size() > 1) {343fprintf(ostream_,344"Error: use `step [number]` to step an optional number of"345" instructions\n");346return DebugContinue;347}348349// Step 1 instruction by default.350std::optional<uint64_t> number_of_instructions_to_execute{1};351352if (args.size() == 1) {353// Parse the argument to step that number of instructions.354std::string arg = args.front();355number_of_instructions_to_execute = Debugger::ParseUint64String(arg);356if (!number_of_instructions_to_execute) {357fprintf(ostream_,358"Error: use `step [number]` to step an optional number of"359" instructions\n");360return DebugContinue;361}362}363364while (!sim_->IsSimulationFinished() &&365*number_of_instructions_to_execute > 0) {366sim_->ExecuteInstruction();367(*number_of_instructions_to_execute)--;368369// The first instruction has already been printed by Debug() so only370// enable instruction tracing after the first instruction has been371// executed.372sim_->SetTraceParameters(sim_->GetTraceParameters() | LOG_DISASM);373}374375// Disable instruction tracing after all instructions have been executed.376sim_->SetTraceParameters(sim_->GetTraceParameters() & ~LOG_DISASM);377378if (sim_->IsSimulationFinished()) {379fprintf(ostream_,380"Debugger at the end of simulation, leaving simulator...\n");381return DebugExit;382}383384return DebugContinue;385}386387388DebugReturn ContinueCmd::Action(const std::vector<std::string>& args) {389USE(args);390391fprintf(ostream_, "Continuing...\n");392393if (sim_->GetDebugger()->IsAtBreakpoint()) {394// This breakpoint has already been hit, so execute it before continuing.395sim_->ExecuteInstruction();396}397398return DebugExit;399}400401402DebugReturn PrintCmd::Action(const std::vector<std::string>& args) {403if (args.size() != 1) {404fprintf(ostream_,405"Error: use `print <register|all>` to print the contents of a"406" specific register or all registers.\n");407return DebugContinue;408}409410if (args.front() == "all") {411sim_->PrintRegisters();412sim_->PrintZRegisters();413} else if (args.front() == "system") {414sim_->PrintSystemRegisters();415} else if (args.front() == "ffr") {416sim_->PrintFFR();417} else {418auto reg = Debugger::ParseRegString(args.front());419if (!reg) {420fprintf(ostream_,421"Error: incorrect register format, use e.g: X0, x0, etc...\n");422return DebugContinue;423}424425// Ensure the stack pointer is printed instead of the zero register.426if ((*reg).second == kSpRegCode) {427(*reg).second = kSPRegInternalCode;428}429430// Registers are printed in different ways depending on their type.431switch ((*reg).first) {432case 'W':433sim_->PrintRegister(434(*reg).second,435static_cast<Simulator::PrintRegisterFormat>(436Simulator::PrintRegisterFormat::kPrintWReg |437Simulator::PrintRegisterFormat::kPrintRegPartial));438break;439case 'X':440sim_->PrintRegister((*reg).second,441Simulator::PrintRegisterFormat::kPrintXReg);442break;443case 'V':444sim_->PrintVRegister((*reg).second);445break;446case 'Z':447sim_->PrintZRegister((*reg).second);448break;449case 'P':450sim_->PrintPRegister((*reg).second);451break;452default:453// ParseRegString should only allow valid register characters.454VIXL_UNREACHABLE();455}456}457458return DebugContinue;459}460461462DebugReturn TraceCmd::Action(const std::vector<std::string>& args) {463if (args.size() != 0) {464fprintf(ostream_, "Error: use `trace` to toggle tracing of registers.\n");465return DebugContinue;466}467468int trace_params = sim_->GetTraceParameters();469if ((trace_params & LOG_ALL) != LOG_ALL) {470fprintf(ostream_,471"Enabling disassembly, registers and memory write tracing\n");472sim_->SetTraceParameters(trace_params | LOG_ALL);473} else {474fprintf(ostream_,475"Disabling disassembly, registers and memory write tracing\n");476sim_->SetTraceParameters(trace_params & ~LOG_ALL);477}478479return DebugContinue;480}481482483DebugReturn GdbCmd::Action(const std::vector<std::string>& args) {484if (args.size() != 0) {485fprintf(ostream_,486"Error: use `gdb` to enter GDB from the simulator debugger.\n");487return DebugContinue;488}489490HostBreakpoint();491return DebugContinue;492}493494495} // namespace aarch64496} // namespace vixl497498#endif // VIXL_INCLUDE_SIMULATOR_AARCH64499500501