// Copyright 2014, VIXL authors // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of ARM Limited nor the names of its contributors may be // used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY ARM LIMITED AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL ARM LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64 #include "debugger-aarch64.h" namespace vixl { namespace aarch64 { // List of commands supported by the debugger. #define DEBUG_COMMAND_LIST(C) \ C(HelpCommand) \ C(ContinueCommand) \ C(StepCommand) \ C(SkipCommand) \ C(DisasmCommand) \ C(PrintCommand) \ C(ExamineCommand) // Debugger command lines are broken up in token of different type to make // processing easier later on. class Token { public: virtual ~Token() {} // Token type. virtual bool IsRegister() const { return false; } virtual bool IsFPRegister() const { return false; } virtual bool IsIdentifier() const { return false; } virtual bool IsAddress() const { return false; } virtual bool IsInteger() const { return false; } virtual bool IsFormat() const { return false; } virtual bool IsUnknown() const { return false; } // Token properties. virtual bool CanAddressMemory() const { return false; } virtual uint8_t* ToAddress(Debugger* debugger) const = 0; virtual void Print(FILE* out = stdout) const = 0; static Token* Tokenize(const char* arg); }; // Tokens often hold one value. template class ValueToken : public Token { public: explicit ValueToken(T value) : value_(value) {} ValueToken() {} T value() const { return value_; } VIXL_NO_RETURN virtual uint8_t* ToAddress(Debugger* debugger) const VIXL_OVERRIDE { USE(debugger); VIXL_ABORT(); } protected: T value_; }; // Integer registers (X or W) and their aliases. // Format: wn or xn with 0 <= n < 32 or a name in the aliases list. class RegisterToken : public ValueToken { public: explicit RegisterToken(const Register reg) : ValueToken(reg) {} virtual bool IsRegister() const VIXL_OVERRIDE { return true; } virtual bool CanAddressMemory() const VIXL_OVERRIDE { return value().Is64Bits(); } virtual uint8_t* ToAddress(Debugger* debugger) const VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; const char* Name() const; static Token* Tokenize(const char* arg); static RegisterToken* Cast(Token* tok) { VIXL_ASSERT(tok->IsRegister()); return reinterpret_cast(tok); } private: static const int kMaxAliasNumber = 4; static const char* kXAliases[kNumberOfRegisters][kMaxAliasNumber]; static const char* kWAliases[kNumberOfRegisters][kMaxAliasNumber]; }; // Floating point registers (D or S). // Format: sn or dn with 0 <= n < 32. class FPRegisterToken : public ValueToken { public: explicit FPRegisterToken(const FPRegister fpreg) : ValueToken(fpreg) {} virtual bool IsFPRegister() const VIXL_OVERRIDE { return true; } virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; static Token* Tokenize(const char* arg); static FPRegisterToken* Cast(Token* tok) { VIXL_ASSERT(tok->IsFPRegister()); return reinterpret_cast(tok); } }; // Non-register identifiers. // Format: Alphanumeric string starting with a letter. class IdentifierToken : public ValueToken { public: explicit IdentifierToken(const char* name) { size_t size = strlen(name) + 1; value_ = new char[size]; strncpy(value_, name, size); } virtual ~IdentifierToken() { delete[] value_; } virtual bool IsIdentifier() const VIXL_OVERRIDE { return true; } virtual bool CanAddressMemory() const VIXL_OVERRIDE { return strcmp(value(), "pc") == 0; } virtual uint8_t* ToAddress(Debugger* debugger) const VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; static Token* Tokenize(const char* arg); static IdentifierToken* Cast(Token* tok) { VIXL_ASSERT(tok->IsIdentifier()); return reinterpret_cast(tok); } }; // 64-bit address literal. // Format: 0x... with up to 16 hexadecimal digits. class AddressToken : public ValueToken { public: explicit AddressToken(uint8_t* address) : ValueToken(address) {} virtual bool IsAddress() const VIXL_OVERRIDE { return true; } virtual bool CanAddressMemory() const VIXL_OVERRIDE { return true; } virtual uint8_t* ToAddress(Debugger* debugger) const VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; static Token* Tokenize(const char* arg); static AddressToken* Cast(Token* tok) { VIXL_ASSERT(tok->IsAddress()); return reinterpret_cast(tok); } }; // 64-bit decimal integer literal. // Format: n. class IntegerToken : public ValueToken { public: explicit IntegerToken(int64_t value) : ValueToken(value) {} virtual bool IsInteger() const VIXL_OVERRIDE { return true; } virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; static Token* Tokenize(const char* arg); static IntegerToken* Cast(Token* tok) { VIXL_ASSERT(tok->IsInteger()); return reinterpret_cast(tok); } }; // Literal describing how to print a chunk of data (up to 64 bits). // Format: .ln // where l (letter) is one of // * x: hexadecimal // * s: signed integer // * u: unsigned integer // * f: floating point // * i: instruction // and n (size) is one of 8, 16, 32 and 64. n should be omitted for // instructions. class FormatToken : public Token { public: FormatToken() {} virtual bool IsFormat() const VIXL_OVERRIDE { return true; } virtual int SizeOf() const = 0; virtual char GetTypeCode() const = 0; virtual void PrintData(void* data, FILE* out = stdout) const = 0; virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE = 0; VIXL_NO_RETURN virtual uint8_t* ToAddress(Debugger* debugger) const VIXL_OVERRIDE { USE(debugger); VIXL_ABORT(); } static Token* Tokenize(const char* arg); static FormatToken* Cast(Token* tok) { VIXL_ASSERT(tok->IsFormat()); return reinterpret_cast(tok); } }; template class Format : public FormatToken { public: Format(const char* fmt, char type_code) : fmt_(fmt), type_code_(type_code) {} virtual int SizeOf() const VIXL_OVERRIDE { return sizeof(T); } virtual char GetTypeCode() const VIXL_OVERRIDE { return type_code_; } virtual void PrintData(void* data, FILE* out = stdout) const VIXL_OVERRIDE { T value; memcpy(&value, data, sizeof(value)); fprintf(out, fmt_, value); } virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; private: const char* fmt_; char type_code_; }; // Tokens which don't fit any of the above. class UnknownToken : public Token { public: explicit UnknownToken(const char* arg) { size_t size = strlen(arg) + 1; unknown_ = new char[size]; strncpy(unknown_, arg, size); } virtual ~UnknownToken() { delete[] unknown_; } VIXL_NO_RETURN virtual uint8_t* ToAddress(Debugger* debugger) const VIXL_OVERRIDE { USE(debugger); VIXL_ABORT(); } virtual bool IsUnknown() const VIXL_OVERRIDE { return true; } virtual void Print(FILE* out = stdout) const VIXL_OVERRIDE; private: char* unknown_; }; // All debugger commands must subclass DebugCommand and implement Run, Print // and Build. Commands must also define kHelp and kAliases. class DebugCommand { public: explicit DebugCommand(Token* name) : name_(IdentifierToken::Cast(name)) {} DebugCommand() : name_(NULL) {} virtual ~DebugCommand() { delete name_; } const char* name() { return name_->value(); } // Run the command on the given debugger. // Return `true` if control should be given back to the debugger. virtual bool Run(Debugger* debugger) = 0; virtual void Print(FILE* out = stdout); static bool Match(const char* name, const char** aliases); static DebugCommand* Parse(char* line); static void PrintHelp(const char** aliases, const char* args, const char* help); private: IdentifierToken* name_; }; // For all commands below see their respective kHelp and kAliases in // debugger-aarch64.cc class HelpCommand : public DebugCommand { public: explicit HelpCommand(Token* name) : DebugCommand(name) {} virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; }; class ContinueCommand : public DebugCommand { public: explicit ContinueCommand(Token* name) : DebugCommand(name) {} virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; }; class StepCommand : public DebugCommand { public: StepCommand(Token* name, IntegerToken* count) : DebugCommand(name), count_(count) {} virtual ~StepCommand() { delete count_; } int64_t count() { return count_->value(); } virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) VIXL_OVERRIDE; static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; private: IntegerToken* count_; }; class SkipCommand : public DebugCommand { public: SkipCommand(Token* name, IntegerToken* count) : DebugCommand(name), count_(count) {} virtual ~SkipCommand() { delete count_; } int64_t count() { return count_->value(); } virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) VIXL_OVERRIDE; static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; private: IntegerToken* count_; }; class DisasmCommand : public DebugCommand { public: static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; }; class PrintCommand : public DebugCommand { public: PrintCommand(Token* name, Token* target, FormatToken* format) : DebugCommand(name), target_(target), format_(format) {} virtual ~PrintCommand() { delete target_; delete format_; } Token* target() { return target_; } FormatToken* format() { return format_; } virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) VIXL_OVERRIDE; static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; private: Token* target_; FormatToken* format_; }; class ExamineCommand : public DebugCommand { public: ExamineCommand(Token* name, Token* target, FormatToken* format, IntegerToken* count) : DebugCommand(name), target_(target), format_(format), count_(count) {} virtual ~ExamineCommand() { delete target_; delete format_; delete count_; } Token* target() { return target_; } FormatToken* format() { return format_; } IntegerToken* count() { return count_; } virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; virtual void Print(FILE* out = stdout) VIXL_OVERRIDE; static DebugCommand* Build(std::vector args); static const char* kHelp; static const char* kAliases[]; static const char* kArguments; private: Token* target_; FormatToken* format_; IntegerToken* count_; }; // Commands which name does not match any of the known commnand. class UnknownCommand : public DebugCommand { public: explicit UnknownCommand(std::vector args) : args_(args) {} virtual ~UnknownCommand(); virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; private: std::vector args_; }; // Commands which name match a known command but the syntax is invalid. class InvalidCommand : public DebugCommand { public: InvalidCommand(std::vector args, int index, const char* cause) : args_(args), index_(index), cause_(cause) {} virtual ~InvalidCommand(); virtual bool Run(Debugger* debugger) VIXL_OVERRIDE; private: std::vector args_; int index_; const char* cause_; }; const char* HelpCommand::kAliases[] = {"help", NULL}; const char* HelpCommand::kArguments = NULL; const char* HelpCommand::kHelp = " Print this help."; const char* ContinueCommand::kAliases[] = {"continue", "c", NULL}; const char* ContinueCommand::kArguments = NULL; const char* ContinueCommand::kHelp = " Resume execution."; const char* StepCommand::kAliases[] = {"stepi", "si", NULL}; const char* StepCommand::kArguments = "[n = 1]"; const char* StepCommand::kHelp = " Execute n next instruction(s)."; const char* SkipCommand::kAliases[] = {"skip", NULL}; const char* SkipCommand::kArguments = "[n = 1]"; const char* SkipCommand::kHelp = " Skip the next n instruction(s)."; const char* DisasmCommand::kAliases[] = {"disasm", "di", NULL}; const char* DisasmCommand::kArguments = "[n = 10]"; const char* DisasmCommand::kHelp = " Disassemble n instruction(s) at pc.\n" " This command is equivalent to x pc.i [n = 10]."; const char* PrintCommand::kAliases[] = {"print", "p", NULL}; const char* PrintCommand::kArguments = "[.format]"; const char* PrintCommand::kHelp = " Print the given entity according to the given format.\n" " The format parameter only affects individual registers; it is ignored\n" " for other entities.\n" " can be one of the following:\n" " * A register name (such as x0, s1, ...).\n" " * 'regs', to print all integer (W and X) registers.\n" " * 'fpregs' to print all floating-point (S and D) registers.\n" " * 'sysregs' to print all system registers (including NZCV).\n" " * 'pc' to print the current program counter.\n"; const char* ExamineCommand::kAliases[] = {"m", "mem", "x", NULL}; const char* ExamineCommand::kArguments = "[.format] [n = 10]"; const char* ExamineCommand::kHelp = " Examine memory. Print n items of memory at address according to\n" " the given [.format].\n" " Addr can be an immediate address, a register name or pc.\n" " Format is made of a type letter: 'x' (hexadecimal), 's' (signed), 'u'\n" " (unsigned), 'f' (floating point), i (instruction) and a size in bits\n" " when appropriate (8, 16, 32, 64)\n" " E.g 'x sp.x64' will print 10 64-bit words from the stack in\n" " hexadecimal format."; const char* RegisterToken::kXAliases[kNumberOfRegisters][kMaxAliasNumber] = {{"x0", NULL}, {"x1", NULL}, {"x2", NULL}, {"x3", NULL}, {"x4", NULL}, {"x5", NULL}, {"x6", NULL}, {"x7", NULL}, {"x8", NULL}, {"x9", NULL}, {"x10", NULL}, {"x11", NULL}, {"x12", NULL}, {"x13", NULL}, {"x14", NULL}, {"x15", NULL}, {"ip0", "x16", NULL}, {"ip1", "x17", NULL}, {"x18", "pr", NULL}, {"x19", NULL}, {"x20", NULL}, {"x21", NULL}, {"x22", NULL}, {"x23", NULL}, {"x24", NULL}, {"x25", NULL}, {"x26", NULL}, {"x27", NULL}, {"x28", NULL}, {"fp", "x29", NULL}, {"lr", "x30", NULL}, {"sp", NULL}}; const char* RegisterToken::kWAliases[kNumberOfRegisters][kMaxAliasNumber] = {{"w0", NULL}, {"w1", NULL}, {"w2", NULL}, {"w3", NULL}, {"w4", NULL}, {"w5", NULL}, {"w6", NULL}, {"w7", NULL}, {"w8", NULL}, {"w9", NULL}, {"w10", NULL}, {"w11", NULL}, {"w12", NULL}, {"w13", NULL}, {"w14", NULL}, {"w15", NULL}, {"w16", NULL}, {"w17", NULL}, {"w18", NULL}, {"w19", NULL}, {"w20", NULL}, {"w21", NULL}, {"w22", NULL}, {"w23", NULL}, {"w24", NULL}, {"w25", NULL}, {"w26", NULL}, {"w27", NULL}, {"w28", NULL}, {"w29", NULL}, {"w30", NULL}, {"wsp", NULL}}; Debugger::Debugger(Decoder* decoder, FILE* stream) : Simulator(decoder, stream), debugger_active_(false), steps_(0), last_command_(NULL) { disasm_ = new PrintDisassembler(stdout); printer_ = new Decoder(); printer_->AppendVisitor(disasm_); } Debugger::~Debugger() { delete disasm_; delete printer_; } void Debugger::Run() { // Flush any written registers before executing anything, so that // manually-set registers are logged _before_ the first instruction. LogAllWrittenRegisters(); while (pc_ != kEndOfSimAddress) { if (IsDebuggerActive()) RunDebuggerShell(); ExecuteInstruction(); } } void Debugger::PrintInstructions(const void* address, int64_t count, const char* prefix) { if (count == 0) { return; } const Instruction* from = Instruction::CastConst(address); if (count < 0) { count = -count; from -= (count - 1) * kInstructionSize; } const Instruction* to = from + count * kInstructionSize; for (const Instruction* current = from; current < to; current = current->GetNextInstruction()) { printf("%s", prefix); printer_->Decode(current); } } void Debugger::PrintMemory(const uint8_t* address, const FormatToken* format, int64_t count) { if (count == 0) { return; } const uint8_t* from = address; int size = format->SizeOf(); if (count < 0) { count = -count; from -= (count - 1) * size; } const uint8_t* to = from + count * size; for (const uint8_t* current = from; current < to; current += size) { if (((current - from) % 8) == 0) { printf("\n%p: ", reinterpret_cast(current)); } uint64_t data = Memory::Read(current); format->PrintData(&data); printf(" "); } printf("\n\n"); } void Debugger::PrintRegister(const Register& target_reg, const char* name, const FormatToken* format) { const uint64_t reg_size = target_reg.GetSizeInBits(); const uint64_t format_size = format->SizeOf() * 8; const uint64_t count = reg_size / format_size; const uint64_t mask = 0xffffffffffffffff >> (64 - format_size); const uint64_t reg_value = ReadRegister(target_reg.GetCode(), Reg31IsStackPointer); VIXL_ASSERT(count > 0); printf("%s = ", name); for (uint64_t i = 1; i <= count; i++) { uint64_t data = reg_value >> (reg_size - (i * format_size)); data &= mask; format->PrintData(&data); printf(" "); } printf("\n"); } // TODO(all): fix this for vector registers. void Debugger::PrintFPRegister(const FPRegister& target_fpreg, const FormatToken* format) { const unsigned fpreg_size = target_fpreg.GetSizeInBits(); const uint64_t format_size = format->SizeOf() * 8; const uint64_t count = fpreg_size / format_size; const uint64_t mask = 0xffffffffffffffff >> (64 - format_size); const uint64_t fpreg_value = ReadVRegister(fpreg_size, target_fpreg.GetCode()); VIXL_ASSERT(count > 0); if (target_fpreg.Is32Bits()) { printf("s%u = ", target_fpreg.GetCode()); } else { printf("d%u = ", target_fpreg.GetCode()); } for (uint64_t i = 1; i <= count; i++) { uint64_t data = fpreg_value >> (fpreg_size - (i * format_size)); data &= mask; format->PrintData(&data); printf(" "); } printf("\n"); } void Debugger::VisitException(const Instruction* instr) { switch (instr->Mask(ExceptionMask)) { case BRK: DoBreakpoint(instr); return; case HLT: VIXL_FALLTHROUGH(); default: Simulator::VisitException(instr); } } // Read a command. A command will be at most kMaxDebugShellLine char long and // ends with '\n\0'. // TODO: Should this be a utility function? char* Debugger::ReadCommandLine(const char* prompt, char* buffer, int length) { int fgets_calls = 0; char* end = NULL; printf("%s", prompt); fflush(stdout); do { if (fgets(buffer, length, stdin) == NULL) { printf(" ** Error while reading command. **\n"); return NULL; } fgets_calls++; end = strchr(buffer, '\n'); } while (end == NULL); if (fgets_calls != 1) { printf(" ** Command too long. **\n"); return NULL; } // Remove the newline from the end of the command. VIXL_ASSERT(end[1] == '\0'); VIXL_ASSERT((end - buffer) < (length - 1)); end[0] = '\0'; return buffer; } void Debugger::RunDebuggerShell() { if (IsDebuggerActive()) { if (steps_ > 0) { // Finish stepping first. --steps_; return; } PrintNextInstruction(); bool done = false; while (!done) { char buffer[kMaxDebugShellLine]; char* line = ReadCommandLine("vixl> ", buffer, kMaxDebugShellLine); if (line == NULL) continue; // An error occurred. DebugCommand* command = DebugCommand::Parse(line); if (command != NULL) { last_command_ = command; } if (last_command_ != NULL) { done = last_command_->Run(this); } else { printf("No previous command to run!\n"); } } } } void Debugger::DoBreakpoint(const Instruction* instr) { VIXL_ASSERT(instr->Mask(ExceptionMask) == BRK); printf("Hit breakpoint at pc=%p.\n", reinterpret_cast(instr)); ActivateDebugger(); } static bool StringToUInt64(uint64_t* value, const char* line, int base = 10) { char* endptr = NULL; errno = 0; // Reset errors. uint64_t parsed = strtoul(line, &endptr, base); if (errno == ERANGE) { // Overflow. return false; } if (endptr == line) { // No digits were parsed. return false; } if (*endptr != '\0') { // Non-digit characters present at the end. return false; } *value = parsed; return true; } static bool StringToInt64(int64_t* value, const char* line, int base = 10) { char* endptr = NULL; errno = 0; // Reset errors. int64_t parsed = strtol(line, &endptr, base); if (errno == ERANGE) { // Overflow, undeflow. return false; } if (endptr == line) { // No digits were parsed. return false; } if (*endptr != '\0') { // Non-digit characters present at the end. return false; } *value = parsed; return true; } Token* Token::Tokenize(const char* arg) { if ((arg == NULL) || (*arg == '\0')) { return NULL; } // The order is important. For example Identifier::Tokenize would consider // any register to be a valid identifier. Token* token = RegisterToken::Tokenize(arg); if (token != NULL) { return token; } token = FPRegisterToken::Tokenize(arg); if (token != NULL) { return token; } token = IdentifierToken::Tokenize(arg); if (token != NULL) { return token; } token = AddressToken::Tokenize(arg); if (token != NULL) { return token; } token = IntegerToken::Tokenize(arg); if (token != NULL) { return token; } return new UnknownToken(arg); } uint8_t* RegisterToken::ToAddress(Debugger* debugger) const { VIXL_ASSERT(CanAddressMemory()); uint64_t reg_value = debugger->ReadXRegister(value().GetCode(), Reg31IsStackPointer); uint8_t* address = NULL; memcpy(&address, ®_value, sizeof(address)); return address; } void RegisterToken::Print(FILE* out) const { VIXL_ASSERT(value().IsValid()); fprintf(out, "[Register %s]", Name()); } const char* RegisterToken::Name() const { if (value().Is32Bits()) { return kWAliases[value().GetCode()][0]; } else { return kXAliases[value().GetCode()][0]; } } Token* RegisterToken::Tokenize(const char* arg) { for (unsigned i = 0; i < kNumberOfRegisters; i++) { // Is it a X register or alias? for (const char** current = kXAliases[i]; *current != NULL; current++) { if (strcmp(arg, *current) == 0) { return new RegisterToken(Register::GetXRegFromCode(i)); } } // Is it a W register or alias? for (const char** current = kWAliases[i]; *current != NULL; current++) { if (strcmp(arg, *current) == 0) { return new RegisterToken(Register::GetWRegFromCode(i)); } } } return NULL; } void FPRegisterToken::Print(FILE* out) const { VIXL_ASSERT(value().IsValid()); char prefix = value().Is32Bits() ? 's' : 'd'; fprintf(out, "[FPRegister %c%" PRIu32 "]", prefix, value().GetCode()); } Token* FPRegisterToken::Tokenize(const char* arg) { if (strlen(arg) < 2) { return NULL; } switch (*arg) { case 's': case 'd': const char* cursor = arg + 1; uint64_t code = 0; if (!StringToUInt64(&code, cursor)) { return NULL; } if (code > kNumberOfFPRegisters) { return NULL; } VRegister fpreg = NoVReg; switch (*arg) { case 's': fpreg = VRegister::GetSRegFromCode(static_cast(code)); break; case 'd': fpreg = VRegister::GetDRegFromCode(static_cast(code)); break; default: VIXL_UNREACHABLE(); } return new FPRegisterToken(fpreg); } return NULL; } uint8_t* IdentifierToken::ToAddress(Debugger* debugger) const { VIXL_ASSERT(CanAddressMemory()); const Instruction* pc_value = debugger->ReadPc(); uint8_t* address = NULL; memcpy(&address, &pc_value, sizeof(address)); return address; } void IdentifierToken::Print(FILE* out) const { fprintf(out, "[Identifier %s]", value()); } Token* IdentifierToken::Tokenize(const char* arg) { if (!isalpha(arg[0])) { return NULL; } const char* cursor = arg + 1; while ((*cursor != '\0') && isalnum(*cursor)) { ++cursor; } if (*cursor == '\0') { return new IdentifierToken(arg); } return NULL; } uint8_t* AddressToken::ToAddress(Debugger* debugger) const { USE(debugger); return value(); } void AddressToken::Print(FILE* out) const { fprintf(out, "[Address %p]", reinterpret_cast(value())); } Token* AddressToken::Tokenize(const char* arg) { if ((strlen(arg) < 3) || (arg[0] != '0') || (arg[1] != 'x')) { return NULL; } uint64_t ptr = 0; if (!StringToUInt64(&ptr, arg, 16)) { return NULL; } uint8_t* address = reinterpret_cast(ptr); return new AddressToken(address); } void IntegerToken::Print(FILE* out) const { fprintf(out, "[Integer %" PRId64 "]", value()); } Token* IntegerToken::Tokenize(const char* arg) { int64_t value = 0; if (!StringToInt64(&value, arg)) { return NULL; } return new IntegerToken(value); } Token* FormatToken::Tokenize(const char* arg) { size_t length = strlen(arg); switch (arg[0]) { case 'x': case 's': case 'u': case 'f': if (length == 1) return NULL; break; case 'i': if (length == 1) return new Format("%08" PRIx32, 'i'); VIXL_FALLTHROUGH(); default: return NULL; } char* endptr = NULL; errno = 0; // Reset errors. uint64_t count = strtoul(arg + 1, &endptr, 10); if (errno != 0) { // Overflow, etc. return NULL; } if (endptr == arg) { // No digits were parsed. return NULL; } if (*endptr != '\0') { // There are unexpected (non-digit) characters after the number. return NULL; } switch (arg[0]) { case 'x': switch (count) { case 8: return new Format("%02" PRIx8, 'x'); case 16: return new Format("%04" PRIx16, 'x'); case 32: return new Format("%08" PRIx32, 'x'); case 64: return new Format("%016" PRIx64, 'x'); default: return NULL; } case 's': switch (count) { case 8: return new Format("%4" PRId8, 's'); case 16: return new Format("%6" PRId16, 's'); case 32: return new Format("%11" PRId32, 's'); case 64: return new Format("%20" PRId64, 's'); default: return NULL; } case 'u': switch (count) { case 8: return new Format("%3" PRIu8, 'u'); case 16: return new Format("%5" PRIu16, 'u'); case 32: return new Format("%10" PRIu32, 'u'); case 64: return new Format("%20" PRIu64, 'u'); default: return NULL; } case 'f': switch (count) { case 32: return new Format("%13g", 'f'); case 64: return new Format("%13g", 'f'); default: return NULL; } default: VIXL_UNREACHABLE(); return NULL; } } template void Format::Print(FILE* out) const { unsigned size = sizeof(T) * 8; fprintf(out, "[Format %c%u - %s]", type_code_, size, fmt_); } void UnknownToken::Print(FILE* out) const { fprintf(out, "[Unknown %s]", unknown_); } void DebugCommand::Print(FILE* out) { fprintf(out, "%s", name()); } bool DebugCommand::Match(const char* name, const char** aliases) { for (const char** current = aliases; *current != NULL; current++) { if (strcmp(name, *current) == 0) { return true; } } return false; } DebugCommand* DebugCommand::Parse(char* line) { std::vector args; for (char* chunk = strtok(line, " \t"); chunk != NULL; chunk = strtok(NULL, " \t")) { char* dot = strchr(chunk, '.'); if (dot != NULL) { // 'Token.format'. Token* format = FormatToken::Tokenize(dot + 1); if (format != NULL) { *dot = '\0'; args.push_back(Token::Tokenize(chunk)); args.push_back(format); } else { // Error while parsing the format, push the UnknownToken so an error // can be accurately reported. args.push_back(Token::Tokenize(chunk)); } } else { args.push_back(Token::Tokenize(chunk)); } } if (args.size() == 0) { return NULL; } if (!args[0]->IsIdentifier()) { return new InvalidCommand(args, 0, "command name is not valid"); } const char* name = IdentifierToken::Cast(args[0])->value(); #define RETURN_IF_MATCH(Command) \ if (Match(name, Command::kAliases)) { \ return Command::Build(args); \ } DEBUG_COMMAND_LIST(RETURN_IF_MATCH); #undef RETURN_IF_MATCH return new UnknownCommand(args); } void DebugCommand::PrintHelp(const char** aliases, const char* args, const char* help) { VIXL_ASSERT(aliases[0] != NULL); VIXL_ASSERT(help != NULL); printf("\n----\n\n"); for (const char** current = aliases; *current != NULL; current++) { if (args != NULL) { printf("%s %s\n", *current, args); } else { printf("%s\n", *current); } } printf("\n%s\n", help); } bool HelpCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); USE(debugger); #define PRINT_HELP(Command) \ DebugCommand::PrintHelp(Command::kAliases, \ Command::kArguments, \ Command::kHelp); DEBUG_COMMAND_LIST(PRINT_HELP); #undef PRINT_HELP printf("\n----\n\n"); return false; } DebugCommand* HelpCommand::Build(std::vector args) { if (args.size() != 1) { return new InvalidCommand(args, -1, "too many arguments"); } return new HelpCommand(args[0]); } bool ContinueCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); debugger->DeactivateDebugger(); return true; } DebugCommand* ContinueCommand::Build(std::vector args) { if (args.size() != 1) { return new InvalidCommand(args, -1, "too many arguments"); } return new ContinueCommand(args[0]); } bool StepCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); // To avoid recursive calls to the debugger shell when hitting breakpoints // while stepping, stepping is implemented by telling the debugger how many // instructions to execute before starting the shell again. int64_t steps = count(); if (steps <= 0) { if (steps < 0) { printf(" ** invalid value for steps: %" PRId64 " (<0) **\n", steps); } // Execute nothing and stay in the shell. return false; } else { debugger->SetSteps(steps - 1); // Relinquish control to the debugger. It will execute the next instruction, // followed by `steps - 1` instructions, before starting the shell again. // (Unless another breakpoint is hit in the meantime.) return true; } } void StepCommand::Print(FILE* out) { fprintf(out, "%s %" PRId64 "", name(), count()); } DebugCommand* StepCommand::Build(std::vector args) { IntegerToken* count = NULL; switch (args.size()) { case 1: { // step [1] count = new IntegerToken(1); break; } case 2: { // step n Token* first = args[1]; if (!first->IsInteger()) { return new InvalidCommand(args, 1, "expects int"); } count = IntegerToken::Cast(first); break; } default: return new InvalidCommand(args, -1, "too many arguments"); } return new StepCommand(args[0], count); } bool SkipCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); int64_t steps = count(); if (steps < 0) { printf(" ** invalid value for steps: %" PRId64 " (<0) **\n", steps); } else { printf("Skipping over %" PRId64 " instructions:\n", steps); debugger->PrintInstructions(debugger->ReadPc(), steps, "Skip: "); debugger->WritePc(debugger->ReadPc() + steps * kInstructionSize); debugger->PrintNextInstruction(); } return false; } void SkipCommand::Print(FILE* out) { fprintf(out, "%s %" PRId64 "", name(), count()); } DebugCommand* SkipCommand::Build(std::vector args) { IntegerToken* count = NULL; switch (args.size()) { case 1: { // step [1] count = new IntegerToken(1); break; } case 2: { // step n Token* first = args[1]; if (!first->IsInteger()) { return new InvalidCommand(args, 1, "expects int"); } count = IntegerToken::Cast(first); break; } default: return new InvalidCommand(args, -1, "too many arguments"); } return new SkipCommand(args[0], count); } DebugCommand* DisasmCommand::Build(std::vector args) { IntegerToken* count = NULL; switch (args.size()) { case 1: { // disasm [10] count = new IntegerToken(10); break; } case 2: { // disasm n Token* first = args[1]; if (!first->IsInteger()) { return new InvalidCommand(args, 1, "expects int"); } count = IntegerToken::Cast(first); break; } default: return new InvalidCommand(args, -1, "too many arguments"); } Token* target = new IdentifierToken("pc"); FormatToken* format = new Format("%08" PRIx32, 'i'); return new ExamineCommand(args[0], target, format, count); } void PrintCommand::Print(FILE* out) { fprintf(out, "%s ", name()); target()->Print(out); if (format() != NULL) format()->Print(out); } bool PrintCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); Token* tok = target(); if (tok->IsIdentifier()) { char* identifier = IdentifierToken::Cast(tok)->value(); if (strcmp(identifier, "regs") == 0) { debugger->PrintRegisters(); } else if (strcmp(identifier, "fpregs") == 0) { debugger->PrintVRegisters(); } else if (strcmp(identifier, "sysregs") == 0) { debugger->PrintSystemRegisters(); } else if (strcmp(identifier, "pc") == 0) { printf("pc = %16p\n", reinterpret_cast(debugger->ReadPc())); } else { printf(" ** Unknown identifier to print: %s **\n", identifier); } return false; } FormatToken* format_tok = format(); VIXL_ASSERT(format_tok != NULL); if (format_tok->GetTypeCode() == 'i') { // TODO(all): Add support for instruction disassembly. printf(" ** unsupported format: instructions **\n"); return false; } if (tok->IsRegister()) { RegisterToken* reg_tok = RegisterToken::Cast(tok); Register reg = reg_tok->value(); debugger->PrintRegister(reg, reg_tok->Name(), format_tok); return false; } if (tok->IsFPRegister()) { FPRegister fpreg = FPRegisterToken::Cast(tok)->value(); debugger->PrintFPRegister(fpreg, format_tok); return false; } VIXL_UNREACHABLE(); return false; } DebugCommand* PrintCommand::Build(std::vector args) { if (args.size() < 2) { return new InvalidCommand(args, -1, "too few arguments"); } Token* target = args[1]; if (!target->IsRegister() && !target->IsFPRegister() && !target->IsIdentifier()) { return new InvalidCommand(args, 1, "expects reg or identifier"); } FormatToken* format = NULL; int target_size = 0; if (target->IsRegister()) { Register reg = RegisterToken::Cast(target)->value(); target_size = reg.GetSizeInBytes(); } else if (target->IsFPRegister()) { FPRegister fpreg = FPRegisterToken::Cast(target)->value(); target_size = fpreg.GetSizeInBytes(); } // If the target is an identifier there must be no format. This is checked // in the switch statement below. switch (args.size()) { case 2: { if (target->IsRegister()) { switch (target_size) { case 4: format = new Format("%08" PRIx32, 'x'); break; case 8: format = new Format("%016" PRIx64, 'x'); break; default: VIXL_UNREACHABLE(); } } else if (target->IsFPRegister()) { switch (target_size) { case 4: format = new Format("%8g", 'f'); break; case 8: format = new Format("%8g", 'f'); break; default: VIXL_UNREACHABLE(); } } break; } case 3: { if (target->IsIdentifier()) { return new InvalidCommand(args, 2, "format is only allowed with registers"); } Token* second = args[2]; if (!second->IsFormat()) { return new InvalidCommand(args, 2, "expects format"); } format = FormatToken::Cast(second); if (format->SizeOf() > target_size) { return new InvalidCommand(args, 2, "format too wide"); } break; } default: return new InvalidCommand(args, -1, "too many arguments"); } return new PrintCommand(args[0], target, format); } bool ExamineCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); uint8_t* address = target()->ToAddress(debugger); int64_t amount = count()->value(); if (format()->GetTypeCode() == 'i') { debugger->PrintInstructions(address, amount); } else { debugger->PrintMemory(address, format(), amount); } return false; } void ExamineCommand::Print(FILE* out) { fprintf(out, "%s ", name()); format()->Print(out); target()->Print(out); } DebugCommand* ExamineCommand::Build(std::vector args) { if (args.size() < 2) { return new InvalidCommand(args, -1, "too few arguments"); } Token* target = args[1]; if (!target->CanAddressMemory()) { return new InvalidCommand(args, 1, "expects address"); } FormatToken* format = NULL; IntegerToken* count = NULL; switch (args.size()) { case 2: { // mem addr[.x64] [10] format = new Format("%016" PRIx64, 'x'); count = new IntegerToken(10); break; } case 3: { // mem addr.format [10] // mem addr[.x64] n Token* second = args[2]; if (second->IsFormat()) { format = FormatToken::Cast(second); count = new IntegerToken(10); break; } else if (second->IsInteger()) { format = new Format("%016" PRIx64, 'x'); count = IntegerToken::Cast(second); } else { return new InvalidCommand(args, 2, "expects format or integer"); } VIXL_UNREACHABLE(); break; } case 4: { // mem addr.format n Token* second = args[2]; Token* third = args[3]; if (!second->IsFormat() || !third->IsInteger()) { return new InvalidCommand(args, -1, "expects addr[.format] [n]"); } format = FormatToken::Cast(second); count = IntegerToken::Cast(third); break; } default: return new InvalidCommand(args, -1, "too many arguments"); } return new ExamineCommand(args[0], target, format, count); } UnknownCommand::~UnknownCommand() { const size_t size = args_.size(); for (size_t i = 0; i < size; ++i) { delete args_[i]; } } bool UnknownCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); USE(debugger); printf(" ** Unknown Command:"); const size_t size = args_.size(); for (size_t i = 0; i < size; ++i) { printf(" "); args_[i]->Print(stdout); } printf(" **\n"); return false; } InvalidCommand::~InvalidCommand() { const size_t size = args_.size(); for (size_t i = 0; i < size; ++i) { delete args_[i]; } } bool InvalidCommand::Run(Debugger* debugger) { VIXL_ASSERT(debugger->IsDebuggerActive()); USE(debugger); printf(" ** Invalid Command:"); const size_t size = args_.size(); for (size_t i = 0; i < size; ++i) { printf(" "); if (i == static_cast(index_)) { printf(">>"); args_[i]->Print(stdout); printf("<<"); } else { args_[i]->Print(stdout); } } printf(" **\n"); printf(" ** %s\n", cause_); return false; } } // namespace aarch64 } // namespace vixl #endif // VIXL_INCLUDE_SIMULATOR_AARCH64