| Index: runtime/vm/simulator_arm64.cc
|
| ===================================================================
|
| --- runtime/vm/simulator_arm64.cc (revision 42212)
|
| +++ runtime/vm/simulator_arm64.cc (working copy)
|
| @@ -15,7 +15,6 @@
|
|
|
| #include "vm/assembler.h"
|
| #include "vm/constants_arm64.h"
|
| -#include "vm/cpu.h"
|
| #include "vm/disassembler.h"
|
| #include "vm/lockers.h"
|
| #include "vm/native_arguments.h"
|
| @@ -25,7 +24,8 @@
|
| namespace dart {
|
|
|
| DEFINE_FLAG(bool, trace_sim, false, "Trace simulator execution.");
|
| -DEFINE_FLAG(int, stop_sim_at, 0, "Address to stop simulator at.");
|
| +DEFINE_FLAG(int, stop_sim_at, 0,
|
| + "Instruction address or instruction count to stop simulator at.");
|
|
|
|
|
| // This macro provides a platform independent use of sscanf. The reason for
|
| @@ -92,11 +92,28 @@
|
| private:
|
| Simulator* sim_;
|
|
|
| - bool GetValue(char* desc, int64_t* value);
|
| - bool GetSValue(char* desc, int32_t* value);
|
| - bool GetDValue(char* desc, int64_t* value);
|
| + bool GetValue(char* desc, uint64_t* value);
|
| + bool GetSValue(char* desc, uint32_t* value);
|
| + bool GetDValue(char* desc, uint64_t* value);
|
| bool GetQValue(char* desc, simd_value_t* value);
|
| - // TODO(zra): Breakpoints.
|
| +
|
| + static intptr_t GetApproximateTokenIndex(const Code& code, uword pc);
|
| +
|
| + static void PrintDartFrame(uword pc, uword fp, uword sp,
|
| + const Function& function,
|
| + intptr_t token_pos,
|
| + bool is_optimized,
|
| + bool is_inlined);
|
| + void PrintBacktrace();
|
| +
|
| + // Set or delete a breakpoint. Returns true if successful.
|
| + bool SetBreakpoint(Instr* breakpc);
|
| + bool DeleteBreakpoint(Instr* breakpc);
|
| +
|
| + // Undo and redo all breakpoints. This is needed to bracket disassembly and
|
| + // execution to skip past breakpoints when run from the debugger.
|
| + void UndoBreakpoints();
|
| + void RedoBreakpoints();
|
| };
|
|
|
|
|
| @@ -108,6 +125,7 @@
|
| SimulatorDebugger::~SimulatorDebugger() {
|
| }
|
|
|
| +
|
| void SimulatorDebugger::Stop(Instr* instr, const char* message) {
|
| OS::Print("Simulator hit %s\n", message);
|
| Debug();
|
| @@ -155,7 +173,7 @@
|
| }
|
|
|
|
|
| -bool SimulatorDebugger::GetValue(char* desc, int64_t* value) {
|
| +bool SimulatorDebugger::GetValue(char* desc, uint64_t* value) {
|
| Register reg = LookupCpuRegisterByName(desc);
|
| if (reg != kNoRegister) {
|
| if (reg == ZR) {
|
| @@ -166,7 +184,7 @@
|
| return true;
|
| }
|
| if (desc[0] == '*') {
|
| - int64_t addr;
|
| + uint64_t addr;
|
| if (GetValue(desc + 1, &addr)) {
|
| if (Simulator::IsIllegalAddress(addr)) {
|
| return false;
|
| @@ -179,6 +197,10 @@
|
| *value = sim_->get_pc();
|
| return true;
|
| }
|
| + if (strcmp("icount", desc) == 0) {
|
| + *value = sim_->get_icount();
|
| + return true;
|
| + }
|
| bool retval = SScanF(desc, "0x%"Px64, value) == 1;
|
| if (!retval) {
|
| retval = SScanF(desc, "%"Px64, value) == 1;
|
| @@ -187,7 +209,7 @@
|
| }
|
|
|
|
|
| -bool SimulatorDebugger::GetSValue(char* desc, int32_t* value) {
|
| +bool SimulatorDebugger::GetSValue(char* desc, uint32_t* value) {
|
| VRegister vreg = LookupVRegisterByName(desc);
|
| if (vreg != kNoVRegister) {
|
| *value = sim_->get_vregisters(vreg, 0);
|
| @@ -194,12 +216,12 @@
|
| return true;
|
| }
|
| if (desc[0] == '*') {
|
| - int64_t addr;
|
| + uint64_t addr;
|
| if (GetValue(desc + 1, &addr)) {
|
| if (Simulator::IsIllegalAddress(addr)) {
|
| return false;
|
| }
|
| - *value = *(reinterpret_cast<int32_t*>(addr));
|
| + *value = *(reinterpret_cast<uint32_t*>(addr));
|
| return true;
|
| }
|
| }
|
| @@ -207,7 +229,7 @@
|
| }
|
|
|
|
|
| -bool SimulatorDebugger::GetDValue(char* desc, int64_t* value) {
|
| +bool SimulatorDebugger::GetDValue(char* desc, uint64_t* value) {
|
| VRegister vreg = LookupVRegisterByName(desc);
|
| if (vreg != kNoVRegister) {
|
| *value = sim_->get_vregisterd(vreg, 0);
|
| @@ -214,12 +236,12 @@
|
| return true;
|
| }
|
| if (desc[0] == '*') {
|
| - int64_t addr;
|
| + uint64_t addr;
|
| if (GetValue(desc + 1, &addr)) {
|
| if (Simulator::IsIllegalAddress(addr)) {
|
| return false;
|
| }
|
| - *value = *(reinterpret_cast<int64_t*>(addr));
|
| + *value = *(reinterpret_cast<uint64_t*>(addr));
|
| return true;
|
| }
|
| }
|
| @@ -234,7 +256,7 @@
|
| return true;
|
| }
|
| if (desc[0] == '*') {
|
| - int64_t addr;
|
| + uint64_t addr;
|
| if (GetValue(desc + 1, &addr)) {
|
| if (Simulator::IsIllegalAddress(addr)) {
|
| return false;
|
| @@ -247,6 +269,138 @@
|
| }
|
|
|
|
|
| +intptr_t SimulatorDebugger::GetApproximateTokenIndex(const Code& code,
|
| + uword pc) {
|
| + intptr_t token_pos = -1;
|
| + const PcDescriptors& descriptors =
|
| + PcDescriptors::Handle(code.pc_descriptors());
|
| + PcDescriptors::Iterator iter(descriptors, RawPcDescriptors::kAnyKind);
|
| + while (iter.MoveNext()) {
|
| + if (iter.Pc() == pc) {
|
| + return iter.TokenPos();
|
| + } else if ((token_pos <= 0) && (iter.Pc() > pc)) {
|
| + token_pos = iter.TokenPos();
|
| + }
|
| + }
|
| + return token_pos;
|
| +}
|
| +
|
| +
|
| +void SimulatorDebugger::PrintDartFrame(uword pc, uword fp, uword sp,
|
| + const Function& function,
|
| + intptr_t token_pos,
|
| + bool is_optimized,
|
| + bool is_inlined) {
|
| + const Script& script = Script::Handle(function.script());
|
| + const String& func_name = String::Handle(function.QualifiedUserVisibleName());
|
| + const String& url = String::Handle(script.url());
|
| + intptr_t line = -1;
|
| + intptr_t column = -1;
|
| + if (token_pos >= 0) {
|
| + script.GetTokenLocation(token_pos, &line, &column);
|
| + }
|
| + OS::Print("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd
|
| + ":%" Pd ")\n",
|
| + pc, fp, sp,
|
| + is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
|
| + func_name.ToCString(),
|
| + url.ToCString(),
|
| + line, column);
|
| +}
|
| +
|
| +
|
| +void SimulatorDebugger::PrintBacktrace() {
|
| + StackFrameIterator frames(sim_->get_register(FP),
|
| + sim_->get_register(SP),
|
| + sim_->get_pc(),
|
| + StackFrameIterator::kDontValidateFrames);
|
| + StackFrame* frame = frames.NextFrame();
|
| + ASSERT(frame != NULL);
|
| + Function& function = Function::Handle();
|
| + Function& inlined_function = Function::Handle();
|
| + Code& code = Code::Handle();
|
| + Code& unoptimized_code = Code::Handle();
|
| + while (frame != NULL) {
|
| + if (frame->IsDartFrame()) {
|
| + code = frame->LookupDartCode();
|
| + function = code.function();
|
| + if (code.is_optimized()) {
|
| + // For optimized frames, extract all the inlined functions if any
|
| + // into the stack trace.
|
| + InlinedFunctionsIterator it(code, frame->pc());
|
| + while (!it.Done()) {
|
| + // Print each inlined frame with its pc in the corresponding
|
| + // unoptimized frame.
|
| + inlined_function = it.function();
|
| + unoptimized_code = it.code();
|
| + uword unoptimized_pc = it.pc();
|
| + it.Advance();
|
| + if (!it.Done()) {
|
| + PrintDartFrame(unoptimized_pc, frame->fp(), frame->sp(),
|
| + inlined_function,
|
| + GetApproximateTokenIndex(unoptimized_code,
|
| + unoptimized_pc),
|
| + true, true);
|
| + }
|
| + }
|
| + // Print the optimized inlining frame below.
|
| + }
|
| + PrintDartFrame(frame->pc(), frame->fp(), frame->sp(),
|
| + function,
|
| + GetApproximateTokenIndex(code, frame->pc()),
|
| + code.is_optimized(), false);
|
| + } else {
|
| + OS::Print("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s frame\n",
|
| + frame->pc(), frame->fp(), frame->sp(),
|
| + frame->IsEntryFrame() ? "entry" :
|
| + frame->IsExitFrame() ? "exit" :
|
| + frame->IsStubFrame() ? "stub" : "invalid");
|
| + }
|
| + frame = frames.NextFrame();
|
| + }
|
| +}
|
| +
|
| +
|
| +bool SimulatorDebugger::SetBreakpoint(Instr* breakpc) {
|
| + // Check if a breakpoint can be set. If not return without any side-effects.
|
| + if (sim_->break_pc_ != NULL) {
|
| + return false;
|
| + }
|
| +
|
| + // Set the breakpoint.
|
| + sim_->break_pc_ = breakpc;
|
| + sim_->break_instr_ = breakpc->InstructionBits();
|
| + // Not setting the breakpoint instruction in the code itself. It will be set
|
| + // when the debugger shell continues.
|
| + return true;
|
| +}
|
| +
|
| +
|
| +bool SimulatorDebugger::DeleteBreakpoint(Instr* breakpc) {
|
| + if (sim_->break_pc_ != NULL) {
|
| + sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
|
| + }
|
| +
|
| + sim_->break_pc_ = NULL;
|
| + sim_->break_instr_ = 0;
|
| + return true;
|
| +}
|
| +
|
| +
|
| +void SimulatorDebugger::UndoBreakpoints() {
|
| + if (sim_->break_pc_ != NULL) {
|
| + sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
|
| + }
|
| +}
|
| +
|
| +
|
| +void SimulatorDebugger::RedoBreakpoints() {
|
| + if (sim_->break_pc_ != NULL) {
|
| + sim_->break_pc_->SetInstructionBits(Instr::kSimulatorBreakpointInstruction);
|
| + }
|
| +}
|
| +
|
| +
|
| void SimulatorDebugger::Debug() {
|
| intptr_t last_pc = -1;
|
| bool done = false;
|
| @@ -266,9 +420,9 @@
|
| arg1[ARG_SIZE] = 0;
|
| arg2[ARG_SIZE] = 0;
|
|
|
| - // TODO(zra): Undo all set breakpoints while running in the debugger shell.
|
| - // This will make them invisible to all commands.
|
| - // UndoBreakpoints();
|
| + // Undo all set breakpoints while running in the debugger shell. This will
|
| + // make them invisible to all commands.
|
| + UndoBreakpoints();
|
|
|
| while (!done) {
|
| if (last_pc != sim_->get_pc()) {
|
| @@ -297,15 +451,20 @@
|
| " disasm <address>\n"
|
| " disasm <address> <number_of_instructions>\n"
|
| " by default 10 instrs are disassembled\n"
|
| + "del -- delete breakpoints\n"
|
| "flags -- print flag values\n"
|
| "gdb -- transfer control to gdb\n"
|
| "h/help -- print this help string\n"
|
| - "p/print <reg or value or *addr> -- print integer value\n"
|
| + "break <address> -- set break point at specified address\n"
|
| + "p/print <reg or icount or value or *addr> -- print integer\n"
|
| "pf/printfloat <vreg or *addr> --print float value\n"
|
| "pd/printdouble <vreg or *addr> -- print double value\n"
|
| "pq/printquad <vreg or *addr> -- print vector register\n"
|
| "po/printobject <*reg or *addr> -- print object\n"
|
| "si/stepi -- single step an instruction\n"
|
| + "trace -- toggle execution tracing mode\n"
|
| + "bt -- print backtrace\n"
|
| + "unstop -- if current pc is a stop instr make it a nop\n"
|
| "q/quit -- Quit the debugger and exit the program\n");
|
| } else if ((strcmp(cmd, "quit") == 0) || (strcmp(cmd, "q") == 0)) {
|
| OS::Print("Quitting\n");
|
| @@ -319,7 +478,7 @@
|
| done = true;
|
| } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
|
| if (args == 2) {
|
| - int64_t value;
|
| + uint64_t value;
|
| if (GetValue(arg1, &value)) {
|
| OS::Print("%s: %"Pu64" 0x%"Px64"\n", arg1, value, value);
|
| } else {
|
| @@ -331,9 +490,9 @@
|
| } else if ((strcmp(cmd, "pf") == 0) ||
|
| (strcmp(cmd, "printfloat") == 0)) {
|
| if (args == 2) {
|
| - int32_t value;
|
| + uint32_t value;
|
| if (GetSValue(arg1, &value)) {
|
| - float svalue = bit_cast<float, int32_t>(value);
|
| + float svalue = bit_cast<float, uint32_t>(value);
|
| OS::Print("%s: %d 0x%x %.8g\n",
|
| arg1, value, value, svalue);
|
| } else {
|
| @@ -345,9 +504,9 @@
|
| } else if ((strcmp(cmd, "pd") == 0) ||
|
| (strcmp(cmd, "printdouble") == 0)) {
|
| if (args == 2) {
|
| - int64_t long_value;
|
| + uint64_t long_value;
|
| if (GetDValue(arg1, &long_value)) {
|
| - double dvalue = bit_cast<double, int64_t>(long_value);
|
| + double dvalue = bit_cast<double, uint64_t>(long_value);
|
| OS::Print("%s: %"Pu64" 0x%"Px64" %.8g\n",
|
| arg1, long_value, long_value, dvalue);
|
| } else {
|
| @@ -388,7 +547,7 @@
|
| } else if ((strcmp(cmd, "po") == 0) ||
|
| (strcmp(cmd, "printobject") == 0)) {
|
| if (args == 2) {
|
| - int64_t value;
|
| + uint64_t value;
|
| // Make the dereferencing '*' optional.
|
| if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) ||
|
| GetValue(arg1, &value)) {
|
| @@ -409,28 +568,28 @@
|
| OS::Print("printobject <*reg or *addr>\n");
|
| }
|
| } else if (strcmp(cmd, "disasm") == 0) {
|
| - int64_t start = 0;
|
| - int64_t end = 0;
|
| + uint64_t start = 0;
|
| + uint64_t end = 0;
|
| if (args == 1) {
|
| start = sim_->get_pc();
|
| end = start + (10 * Instr::kInstrSize);
|
| } else if (args == 2) {
|
| if (GetValue(arg1, &start)) {
|
| - // no length parameter passed, assume 10 instructions
|
| + // No length parameter passed, assume 10 instructions.
|
| if (Simulator::IsIllegalAddress(start)) {
|
| - // If start isn't a valid address, warn and use PC instead
|
| + // If start isn't a valid address, warn and use PC instead.
|
| OS::Print("First argument yields invalid address: 0x%"Px64"\n",
|
| start);
|
| - OS::Print("Using PC instead");
|
| + OS::Print("Using PC instead\n");
|
| start = sim_->get_pc();
|
| }
|
| end = start + (10 * Instr::kInstrSize);
|
| }
|
| } else {
|
| - int64_t length;
|
| + uint64_t length;
|
| if (GetValue(arg1, &start) && GetValue(arg2, &length)) {
|
| if (Simulator::IsIllegalAddress(start)) {
|
| - // If start isn't a valid address, warn and use PC instead
|
| + // If start isn't a valid address, warn and use PC instead.
|
| OS::Print("First argument yields invalid address: 0x%"Px64"\n",
|
| start);
|
| OS::Print("Using PC instead\n");
|
| @@ -439,7 +598,32 @@
|
| end = start + (length * Instr::kInstrSize);
|
| }
|
| }
|
| - Disassembler::Disassemble(start, end);
|
| + if ((start > 0) && (end > start)) {
|
| + Disassembler::Disassemble(start, end);
|
| + } else {
|
| + OS::Print("disasm [<address> [<number_of_instructions>]]\n");
|
| + }
|
| + } else if (strcmp(cmd, "gdb") == 0) {
|
| + OS::Print("relinquishing control to gdb\n");
|
| + OS::DebugBreak();
|
| + OS::Print("regaining control from gdb\n");
|
| + } else if (strcmp(cmd, "break") == 0) {
|
| + if (args == 2) {
|
| + uint64_t addr;
|
| + if (GetValue(arg1, &addr)) {
|
| + if (!SetBreakpoint(reinterpret_cast<Instr*>(addr))) {
|
| + OS::Print("setting breakpoint failed\n");
|
| + }
|
| + } else {
|
| + OS::Print("%s unrecognized\n", arg1);
|
| + }
|
| + } else {
|
| + OS::Print("break <addr>\n");
|
| + }
|
| + } else if (strcmp(cmd, "del") == 0) {
|
| + if (!DeleteBreakpoint(NULL)) {
|
| + OS::Print("deleting breakpoint failed\n");
|
| + }
|
| } else if (strcmp(cmd, "flags") == 0) {
|
| OS::Print("APSR: ");
|
| OS::Print("N flag: %d; ", sim_->n_flag_);
|
| @@ -446,10 +630,19 @@
|
| OS::Print("Z flag: %d; ", sim_->z_flag_);
|
| OS::Print("C flag: %d; ", sim_->c_flag_);
|
| OS::Print("V flag: %d\n", sim_->v_flag_);
|
| - } else if (strcmp(cmd, "gdb") == 0) {
|
| - OS::Print("relinquishing control to gdb\n");
|
| - OS::DebugBreak();
|
| - OS::Print("regaining control from gdb\n");
|
| + } else if (strcmp(cmd, "unstop") == 0) {
|
| + intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize;
|
| + Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc);
|
| + if (stop_instr->IsExceptionGenOp()) {
|
| + stop_instr->SetInstructionBits(Instr::kNopInstruction);
|
| + } else {
|
| + OS::Print("Not at debugger stop.\n");
|
| + }
|
| + } else if (strcmp(cmd, "trace") == 0) {
|
| + FLAG_trace_sim = !FLAG_trace_sim;
|
| + OS::Print("execution tracing %s\n", FLAG_trace_sim ? "on" : "off");
|
| + } else if (strcmp(cmd, "bt") == 0) {
|
| + PrintBacktrace();
|
| } else {
|
| OS::Print("Unknown command: %s\n", cmd);
|
| }
|
| @@ -457,9 +650,9 @@
|
| delete[] line;
|
| }
|
|
|
| - // TODO(zra): Add all the breakpoints back to stop execution and enter the
|
| - // debugger shell when hit.
|
| - // RedoBreakpoints();
|
| + // Add all the breakpoints back to stop execution and enter the debugger
|
| + // shell when hit.
|
| + RedoBreakpoints();
|
|
|
| #undef COMMAND_SIZE
|
| #undef ARG_SIZE
|
| @@ -806,7 +999,6 @@
|
| void Simulator::HandleIllegalAccess(uword addr, Instr* instr) {
|
| uword fault_pc = get_pc();
|
| uword last_pc = get_last_pc();
|
| - // TODO(zra): drop into debugger.
|
| char buffer[128];
|
| snprintf(buffer, sizeof(buffer),
|
| "illegal memory access at 0x%" Px ", pc=0x%" Px ", last_pc=0x%" Px"\n",
|
| @@ -1478,26 +1670,32 @@
|
| } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) &&
|
| (instr->Bits(21, 3) == 1)) {
|
| // Format(instr, "brk 'imm16");
|
| - UnimplementedInstruction(instr);
|
| + SimulatorDebugger dbg(this);
|
| + uint16_t imm = static_cast<uint16_t>(instr->Imm16Field());
|
| + char buffer[32];
|
| + snprintf(buffer, sizeof(buffer), "brk #0x%x", imm);
|
| + set_pc(get_pc() + Instr::kInstrSize);
|
| + dbg.Stop(instr, buffer);
|
| } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) &&
|
| (instr->Bits(21, 3) == 2)) {
|
| // Format(instr, "hlt 'imm16");
|
| uint16_t imm = static_cast<uint16_t>(instr->Imm16Field());
|
| - if (imm == kImmExceptionIsDebug) {
|
| + if (imm == Instr::kSimulatorMessageCode) {
|
| SimulatorDebugger dbg(this);
|
| const char* message = *reinterpret_cast<const char**>(
|
| reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize);
|
| set_pc(get_pc() + Instr::kInstrSize);
|
| dbg.Stop(instr, message);
|
| - } else if (imm == kImmExceptionIsPrintf) {
|
| - const char* message = *reinterpret_cast<const char**>(
|
| - reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize);
|
| - OS::Print("Simulator hit: %s", message);
|
| - } else if (imm == kImmExceptionIsRedirectedCall) {
|
| + } else if (imm == Instr::kSimulatorBreakCode) {
|
| + SimulatorDebugger dbg(this);
|
| + dbg.Stop(instr, "breakpoint");
|
| + } else if (imm == Instr::kSimulatorRedirectCode) {
|
| DoRedirectedCall(instr);
|
| } else {
|
| UnimplementedInstruction(instr);
|
| }
|
| + } else {
|
| + UnimplementedInstruction(instr);
|
| }
|
| }
|
|
|
| @@ -3122,13 +3320,16 @@
|
| }
|
| } else {
|
| // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when
|
| - // we reach the particular instruction count.
|
| + // we reach the particular instruction count or address.
|
| while (program_counter != kEndSimulatingPC) {
|
| Instr* instr = reinterpret_cast<Instr*>(program_counter);
|
| icount_++;
|
| - if (icount_ == FLAG_stop_sim_at) {
|
| - // TODO(zra): Add a debugger.
|
| - UNIMPLEMENTED();
|
| + if (static_cast<intptr_t>(icount_) == FLAG_stop_sim_at) {
|
| + SimulatorDebugger dbg(this);
|
| + dbg.Stop(instr, "Instruction count reached");
|
| + } else if (reinterpret_cast<intptr_t>(instr) == FLAG_stop_sim_at) {
|
| + SimulatorDebugger dbg(this);
|
| + dbg.Stop(instr, "Instruction address reached");
|
| } else if (IsIllegalAddress(program_counter)) {
|
| HandleIllegalAccess(program_counter, instr);
|
| } else {
|
|
|