| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include <math.h> // for isnan. | 5 #include <math.h> // for isnan. |
| 6 #include <setjmp.h> | 6 #include <setjmp.h> |
| 7 #include <stdlib.h> | 7 #include <stdlib.h> |
| 8 | 8 |
| 9 #include "vm/globals.h" | 9 #include "vm/globals.h" |
| 10 #if defined(TARGET_ARCH_ARM64) | 10 #if defined(TARGET_ARCH_ARM64) |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 DEFINE_FLAG(int, stop_sim_at, 0, "Address to stop simulator at."); | 28 DEFINE_FLAG(int, stop_sim_at, 0, "Address to stop simulator at."); |
| 29 | 29 |
| 30 | 30 |
| 31 // This macro provides a platform independent use of sscanf. The reason for | 31 // This macro provides a platform independent use of sscanf. The reason for |
| 32 // SScanF not being implemented in a platform independent way through | 32 // SScanF not being implemented in a platform independent way through |
| 33 // OS in the same way as SNPrint is that the Windows C Run-Time | 33 // OS in the same way as SNPrint is that the Windows C Run-Time |
| 34 // Library does not provide vsscanf. | 34 // Library does not provide vsscanf. |
| 35 #define SScanF sscanf // NOLINT | 35 #define SScanF sscanf // NOLINT |
| 36 | 36 |
| 37 | 37 |
| 38 // The SimulatorDebugger class is used by the simulator while debugging |
| 39 // simulated ARM64 code. |
| 40 class SimulatorDebugger { |
| 41 public: |
| 42 explicit SimulatorDebugger(Simulator* sim); |
| 43 ~SimulatorDebugger(); |
| 44 |
| 45 void Stop(Instr* instr, const char* message); |
| 46 void Debug(); |
| 47 char* ReadLine(const char* prompt); |
| 48 |
| 49 private: |
| 50 Simulator* sim_; |
| 51 |
| 52 bool GetValue(char* desc, int64_t* value); |
| 53 // TODO(zra): GetVValue for doubles. |
| 54 // TODO(zra): Breakpoints. |
| 55 }; |
| 56 |
| 57 |
| 58 SimulatorDebugger::SimulatorDebugger(Simulator* sim) { |
| 59 sim_ = sim; |
| 60 } |
| 61 |
| 62 |
| 63 SimulatorDebugger::~SimulatorDebugger() { |
| 64 } |
| 65 |
| 66 void SimulatorDebugger::Stop(Instr* instr, const char* message) { |
| 67 OS::Print("Simulator hit %s\n", message); |
| 68 Debug(); |
| 69 } |
| 70 |
| 71 |
| 72 static Register LookupCpuRegisterByName(const char* name) { |
| 73 static const char* kNames[] = { |
| 74 "r0", "r1", "r2", "r3", |
| 75 "r4", "r5", "r6", "r7", |
| 76 "r8", "r9", "r10", "r11", |
| 77 "r12", "r13", "r14", "r15", |
| 78 "r16", "r17", "r18", "r19", |
| 79 "r20", "r21", "r22", "r23", |
| 80 "r24", "r25", "r26", "r27", |
| 81 "r28", "r29", "r30", |
| 82 |
| 83 "ip0", "ip1", "pp", "ctx", "fp", "lr", "sp", "zr", |
| 84 }; |
| 85 static const Register kRegisters[] = { |
| 86 R0, R1, R2, R3, R4, R5, R6, R7, |
| 87 R8, R9, R10, R11, R12, R13, R14, R15, |
| 88 R16, R17, R18, R19, R20, R21, R22, R23, |
| 89 R24, R25, R26, R27, R28, R29, R30, |
| 90 |
| 91 IP0, IP1, PP, CTX, FP, LR, SP, ZR, |
| 92 }; |
| 93 ASSERT(ARRAY_SIZE(kNames) == ARRAY_SIZE(kRegisters)); |
| 94 for (unsigned i = 0; i < ARRAY_SIZE(kNames); i++) { |
| 95 if (strcmp(kNames[i], name) == 0) { |
| 96 return kRegisters[i]; |
| 97 } |
| 98 } |
| 99 return kNoRegister; |
| 100 } |
| 101 |
| 102 |
| 103 bool SimulatorDebugger::GetValue(char* desc, int64_t* value) { |
| 104 Register reg = LookupCpuRegisterByName(desc); |
| 105 if (reg != kNoRegister) { |
| 106 *value = sim_->get_register(reg); |
| 107 return true; |
| 108 } |
| 109 if (desc[0] == '*') { |
| 110 int64_t addr; |
| 111 if (GetValue(desc + 1, &addr)) { |
| 112 if (Simulator::IsIllegalAddress(addr)) { |
| 113 return false; |
| 114 } |
| 115 *value = *(reinterpret_cast<int64_t*>(addr)); |
| 116 return true; |
| 117 } |
| 118 } |
| 119 if (strcmp("pc", desc) == 0) { |
| 120 *value = sim_->get_pc(); |
| 121 return true; |
| 122 } |
| 123 bool retval = SScanF(desc, "0x%"Px64, value) == 1; |
| 124 if (!retval) { |
| 125 retval = SScanF(desc, "%"Px64, value) == 1; |
| 126 } |
| 127 return retval; |
| 128 } |
| 129 |
| 130 |
| 131 void SimulatorDebugger::Debug() { |
| 132 intptr_t last_pc = -1; |
| 133 bool done = false; |
| 134 |
| 135 #define COMMAND_SIZE 63 |
| 136 #define ARG_SIZE 255 |
| 137 |
| 138 #define STR(a) #a |
| 139 #define XSTR(a) STR(a) |
| 140 |
| 141 char cmd[COMMAND_SIZE + 1]; |
| 142 char arg1[ARG_SIZE + 1]; |
| 143 char arg2[ARG_SIZE + 1]; |
| 144 |
| 145 // make sure to have a proper terminating character if reaching the limit |
| 146 cmd[COMMAND_SIZE] = 0; |
| 147 arg1[ARG_SIZE] = 0; |
| 148 arg2[ARG_SIZE] = 0; |
| 149 |
| 150 // TODO(zra): Undo all set breakpoints while running in the debugger shell. |
| 151 // This will make them invisible to all commands. |
| 152 // UndoBreakpoints(); |
| 153 |
| 154 while (!done) { |
| 155 if (last_pc != sim_->get_pc()) { |
| 156 last_pc = sim_->get_pc(); |
| 157 if (Simulator::IsIllegalAddress(last_pc)) { |
| 158 OS::Print("pc is out of bounds: 0x%" Px "\n", last_pc); |
| 159 } else { |
| 160 Disassembler::Disassemble(last_pc, last_pc + Instr::kInstrSize); |
| 161 } |
| 162 } |
| 163 char* line = ReadLine("sim> "); |
| 164 if (line == NULL) { |
| 165 FATAL("ReadLine failed"); |
| 166 } else { |
| 167 // Use sscanf to parse the individual parts of the command line. At the |
| 168 // moment no command expects more than two parameters. |
| 169 int args = SScanF(line, |
| 170 "%" XSTR(COMMAND_SIZE) "s " |
| 171 "%" XSTR(ARG_SIZE) "s " |
| 172 "%" XSTR(ARG_SIZE) "s", |
| 173 cmd, arg1, arg2); |
| 174 if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) { |
| 175 OS::Print("c/cont -- continue execution\n" |
| 176 "disasm -- disassemble instrs at current pc location\n" |
| 177 " other variants are:\n" |
| 178 " disasm <address>\n" |
| 179 " disasm <address> <number_of_instructions>\n" |
| 180 " by default 10 instrs are disassembled\n" |
| 181 "gdb -- transfer control to gdb\n" |
| 182 "h/help -- print this help string\n" |
| 183 "p/print <reg or value or *addr> -- print integer value\n" |
| 184 "po/printobject <*reg or *addr> -- print object\n" |
| 185 "si/stepi -- single step an instruction\n" |
| 186 "q/quit -- Quit the debugger and exit the program\n"); |
| 187 } else if ((strcmp(cmd, "quit") == 0) || (strcmp(cmd, "q") == 0)) { |
| 188 OS::Print("Quitting\n"); |
| 189 OS::Exit(0); |
| 190 } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) { |
| 191 sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); |
| 192 } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) { |
| 193 // Execute the one instruction we broke at with breakpoints disabled. |
| 194 sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc())); |
| 195 // Leave the debugger shell. |
| 196 done = true; |
| 197 } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) { |
| 198 if (args == 2) { |
| 199 int64_t value; |
| 200 if (GetValue(arg1, &value)) { |
| 201 OS::Print("%s: %"Pu64" 0x%"Px64"\n", arg1, value, value); |
| 202 } else { |
| 203 OS::Print("%s unrecognized\n", arg1); |
| 204 } |
| 205 } else { |
| 206 OS::Print("print <reg or value or *addr>\n"); |
| 207 } |
| 208 } else if ((strcmp(cmd, "po") == 0) || |
| 209 (strcmp(cmd, "printobject") == 0)) { |
| 210 if (args == 2) { |
| 211 int64_t value; |
| 212 // Make the dereferencing '*' optional. |
| 213 if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) || |
| 214 GetValue(arg1, &value)) { |
| 215 if (Isolate::Current()->heap()->Contains(value)) { |
| 216 OS::Print("%s: \n", arg1); |
| 217 #if defined(DEBUG) |
| 218 const Object& obj = Object::Handle( |
| 219 reinterpret_cast<RawObject*>(value)); |
| 220 obj.Print(); |
| 221 #endif // defined(DEBUG) |
| 222 } else { |
| 223 OS::Print("0x%"Px64" is not an object reference\n", value); |
| 224 } |
| 225 } else { |
| 226 OS::Print("%s unrecognized\n", arg1); |
| 227 } |
| 228 } else { |
| 229 OS::Print("printobject <*reg or *addr>\n"); |
| 230 } |
| 231 } else if (strcmp(cmd, "disasm") == 0) { |
| 232 int64_t start = 0; |
| 233 int64_t end = 0; |
| 234 if (args == 1) { |
| 235 start = sim_->get_pc(); |
| 236 end = start + (10 * Instr::kInstrSize); |
| 237 } else if (args == 2) { |
| 238 if (GetValue(arg1, &start)) { |
| 239 // no length parameter passed, assume 10 instructions |
| 240 if (Simulator::IsIllegalAddress(start)) { |
| 241 // If start isn't a valid address, warn and use PC instead |
| 242 OS::Print("First argument yields invalid address: 0x%"Px64"\n", |
| 243 start); |
| 244 OS::Print("Using PC instead"); |
| 245 start = sim_->get_pc(); |
| 246 } |
| 247 end = start + (10 * Instr::kInstrSize); |
| 248 } |
| 249 } else { |
| 250 intptr_t length; |
| 251 if (GetValue(arg1, &start) && GetValue(arg2, &length)) { |
| 252 if (Simulator::IsIllegalAddress(start)) { |
| 253 // If start isn't a valid address, warn and use PC instead |
| 254 OS::Print("First argument yields invalid address: 0x%"Px64"\n", |
| 255 start); |
| 256 OS::Print("Using PC instead\n"); |
| 257 start = sim_->get_pc(); |
| 258 } |
| 259 end = start + (length * Instr::kInstrSize); |
| 260 } |
| 261 } |
| 262 Disassembler::Disassemble(start, end); |
| 263 } else if (strcmp(cmd, "gdb") == 0) { |
| 264 OS::Print("relinquishing control to gdb\n"); |
| 265 OS::DebugBreak(); |
| 266 OS::Print("regaining control from gdb\n"); |
| 267 } else { |
| 268 OS::Print("Unknown command: %s\n", cmd); |
| 269 } |
| 270 } |
| 271 delete[] line; |
| 272 } |
| 273 |
| 274 // TODO(zra): Add all the breakpoints back to stop execution and enter the |
| 275 // debugger shell when hit. |
| 276 // RedoBreakpoints(); |
| 277 |
| 278 #undef COMMAND_SIZE |
| 279 #undef ARG_SIZE |
| 280 |
| 281 #undef STR |
| 282 #undef XSTR |
| 283 } |
| 284 |
| 285 |
| 286 char* SimulatorDebugger::ReadLine(const char* prompt) { |
| 287 char* result = NULL; |
| 288 char line_buf[256]; |
| 289 intptr_t offset = 0; |
| 290 bool keep_going = true; |
| 291 OS::Print("%s", prompt); |
| 292 while (keep_going) { |
| 293 if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) { |
| 294 // fgets got an error. Just give up. |
| 295 if (result != NULL) { |
| 296 delete[] result; |
| 297 } |
| 298 return NULL; |
| 299 } |
| 300 intptr_t len = strlen(line_buf); |
| 301 if (len > 1 && |
| 302 line_buf[len - 2] == '\\' && |
| 303 line_buf[len - 1] == '\n') { |
| 304 // When we read a line that ends with a "\" we remove the escape and |
| 305 // append the remainder. |
| 306 line_buf[len - 2] = '\n'; |
| 307 line_buf[len - 1] = 0; |
| 308 len -= 1; |
| 309 } else if ((len > 0) && (line_buf[len - 1] == '\n')) { |
| 310 // Since we read a new line we are done reading the line. This |
| 311 // will exit the loop after copying this buffer into the result. |
| 312 keep_going = false; |
| 313 } |
| 314 if (result == NULL) { |
| 315 // Allocate the initial result and make room for the terminating '\0' |
| 316 result = new char[len + 1]; |
| 317 if (result == NULL) { |
| 318 // OOM, so cannot readline anymore. |
| 319 return NULL; |
| 320 } |
| 321 } else { |
| 322 // Allocate a new result with enough room for the new addition. |
| 323 intptr_t new_len = offset + len + 1; |
| 324 char* new_result = new char[new_len]; |
| 325 if (new_result == NULL) { |
| 326 // OOM, free the buffer allocated so far and return NULL. |
| 327 delete[] result; |
| 328 return NULL; |
| 329 } else { |
| 330 // Copy the existing input into the new array and set the new |
| 331 // array as the result. |
| 332 memmove(new_result, result, offset); |
| 333 delete[] result; |
| 334 result = new_result; |
| 335 } |
| 336 } |
| 337 // Copy the newly read line into the result. |
| 338 memmove(result + offset, line_buf, len); |
| 339 offset += len; |
| 340 } |
| 341 ASSERT(result != NULL); |
| 342 result[offset] = '\0'; |
| 343 return result; |
| 344 } |
| 345 |
| 346 |
| 38 Simulator::Simulator() { | 347 Simulator::Simulator() { |
| 39 // Setup simulator support first. Some of this information is needed to | 348 // Setup simulator support first. Some of this information is needed to |
| 40 // setup the architecture state. | 349 // setup the architecture state. |
| 41 // We allocate the stack here, the size is computed as the sum of | 350 // We allocate the stack here, the size is computed as the sum of |
| 42 // the size specified by the user and the buffer space needed for | 351 // the size specified by the user and the buffer space needed for |
| 43 // handling stack overflow exceptions. To be safe in potential | 352 // handling stack overflow exceptions. To be safe in potential |
| 44 // stack underflows we also add some underflow buffer space. | 353 // stack underflows we also add some underflow buffer space. |
| 45 stack_ = new char[(Isolate::GetSpecifiedStackSize() + | 354 stack_ = new char[(Isolate::GetSpecifiedStackSize() + |
| 46 Isolate::kStackSizeBuffer + | 355 Isolate::kStackSizeBuffer + |
| 47 kSimulatorStackUnderflowSize)]; | 356 kSimulatorStackUnderflowSize)]; |
| (...skipping 562 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 610 } | 919 } |
| 611 const int64_t imm19 = instr->SImm19Field(); | 920 const int64_t imm19 = instr->SImm19Field(); |
| 612 const int64_t dest = get_pc() + (imm19 << 2); | 921 const int64_t dest = get_pc() + (imm19 << 2); |
| 613 if (ConditionallyExecute(instr)) { | 922 if (ConditionallyExecute(instr)) { |
| 614 set_pc(dest); | 923 set_pc(dest); |
| 615 } | 924 } |
| 616 } | 925 } |
| 617 | 926 |
| 618 | 927 |
| 619 void Simulator::DecodeExceptionGen(Instr* instr) { | 928 void Simulator::DecodeExceptionGen(Instr* instr) { |
| 620 UnimplementedInstruction(instr); | 929 if ((instr->Bits(0, 2) == 1) && (instr->Bits(2, 3) == 0) && |
| 930 (instr->Bits(21, 3) == 0)) { |
| 931 // Format(instr, "svc 'imm16"); |
| 932 UnimplementedInstruction(instr); |
| 933 } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) && |
| 934 (instr->Bits(21, 3) == 1)) { |
| 935 // Format(instr, "brk 'imm16"); |
| 936 UnimplementedInstruction(instr); |
| 937 } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) && |
| 938 (instr->Bits(21, 3) == 2)) { |
| 939 // Format(instr, "hlt 'imm16"); |
| 940 uint16_t imm = static_cast<uint16_t>(instr->Imm16Field()); |
| 941 if (imm == kImmExceptionIsDebug) { |
| 942 SimulatorDebugger dbg(this); |
| 943 const char* message = *reinterpret_cast<const char**>( |
| 944 reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize); |
| 945 set_pc(get_pc() + Instr::kInstrSize); |
| 946 dbg.Stop(instr, message); |
| 947 } else if (imm == kImmExceptionIsPrintf) { |
| 948 const char* message = *reinterpret_cast<const char**>( |
| 949 reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize); |
| 950 OS::Print("Simulator hit: %s", message); |
| 951 } else { |
| 952 UnimplementedInstruction(instr); |
| 953 } |
| 954 } |
| 621 } | 955 } |
| 622 | 956 |
| 623 | 957 |
| 624 void Simulator::DecodeSystem(Instr* instr) { | 958 void Simulator::DecodeSystem(Instr* instr) { |
| 625 if ((instr->Bits(0, 8) == 0x5f) && (instr->Bits(12, 4) == 2) && | 959 if ((instr->Bits(0, 8) == 0x5f) && (instr->Bits(12, 4) == 2) && |
| 626 (instr->Bits(16, 3) == 3) && (instr->Bits(19, 2) == 0) && | 960 (instr->Bits(16, 3) == 3) && (instr->Bits(19, 2) == 0) && |
| 627 (instr->Bit(21) == 0)) { | 961 (instr->Bit(21) == 0)) { |
| 628 if (instr->Bits(8, 4) == 0) { | 962 if (instr->Bits(8, 4) == 0) { |
| 629 // Format(instr, "nop"); | 963 // Format(instr, "nop"); |
| 630 } else { | 964 } else { |
| (...skipping 792 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1423 int64_t return_value; | 1757 int64_t return_value; |
| 1424 return_value = get_register(R0); | 1758 return_value = get_register(R0); |
| 1425 return return_value; | 1759 return return_value; |
| 1426 } | 1760 } |
| 1427 | 1761 |
| 1428 } // namespace dart | 1762 } // namespace dart |
| 1429 | 1763 |
| 1430 #endif // !defined(HOST_ARCH_ARM64) | 1764 #endif // !defined(HOST_ARCH_ARM64) |
| 1431 | 1765 |
| 1432 #endif // defined TARGET_ARCH_ARM64 | 1766 #endif // defined TARGET_ARCH_ARM64 |
| OLD | NEW |