MIPS: Added the stop() instruction with same behavior as on Arm simulator.

The already working watchpoint break mechanism has been extended to handle "stop" instructions, with text messages.

Explanation (also in constants-mips.h):
On MIPS Simulator breakpoints can have different codes:
- Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints, the simulator will run through them and print the registers.
- Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop() instructions (see Assembler::stop()).
- Breaks larger than kMaxStopCode are simple breaks, dropping you into the debugger.

The current values are 31 for kMaxWatchpointCode and 127 for kMaxStopCode.
From the user's point of view this works the same way as the ARM stop instruction except for the break code usage detailed above.

Ported commits: r5723 (3ba78d24)

BUG=
TEST=

Review URL: http://codereview.chromium.org//7062014

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8069 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2011-05-26 07:46:18 +00:00
parent 02c4e8bfcb
commit 64c610727d
7 changed files with 275 additions and 36 deletions

View File

@ -1336,13 +1336,37 @@ void Assembler::lui(Register rd, int32_t j) {
//-------------Misc-instructions--------------
// Break / Trap instructions.
void Assembler::break_(uint32_t code) {
void Assembler::break_(uint32_t code, bool break_as_stop) {
ASSERT((code & ~0xfffff) == 0);
// We need to invalidate breaks that could be stops as well because the
// simulator expects a char pointer after the stop instruction.
// See constants-mips.h for explanation.
ASSERT((break_as_stop &&
code <= kMaxStopCode &&
code > kMaxWatchpointCode) ||
(!break_as_stop &&
(code > kMaxStopCode ||
code <= kMaxWatchpointCode)));
Instr break_instr = SPECIAL | BREAK | (code << 6);
emit(break_instr);
}
void Assembler::stop(const char* msg, uint32_t code) {
ASSERT(code > kMaxWatchpointCode);
ASSERT(code <= kMaxStopCode);
#if defined(V8_HOST_ARCH_MIPS)
break_(0x54321);
#else // V8_HOST_ARCH_MIPS
BlockTrampolinePoolFor(2);
// The Simulator will handle the stop instruction and get the message address.
// On MIPS stop() is just a special kind of break_().
break_(code, true);
emit(reinterpret_cast<Instr>(msg));
#endif
}
void Assembler::tge(Register rs, Register rt, uint16_t code) {
ASSERT(is_uint10(code));
Instr instr = SPECIAL | TGE | rs.code() << kRsShift

View File

@ -675,7 +675,8 @@ class Assembler : public AssemblerBase {
//-------------Misc-instructions--------------
// Break / Trap instructions.
void break_(uint32_t code);
void break_(uint32_t code, bool break_as_stop = false);
void stop(const char* msg, uint32_t code = kMaxStopCode);
void tge(Register rs, Register rt, uint16_t code);
void tgeu(Register rs, Register rt, uint16_t code);
void tlt(Register rs, Register rt, uint16_t code);

View File

@ -158,6 +158,18 @@ enum SoftwareInterruptCodes {
call_rt_redirected = 0xfffff
};
// On MIPS Simulator breakpoints can have different codes:
// - Breaks between 0 and kMaxWatchpointCode are treated as simple watchpoints,
// the simulator will run through them and print the registers.
// - Breaks between kMaxWatchpointCode and kMaxStopCode are treated as stop()
// instructions (see Assembler::stop()).
// - Breaks larger than kMaxStopCode are simple breaks, dropping you into the
// debugger.
static const uint32_t kMaxWatchpointCode = 31;
static const uint32_t kMaxStopCode = 127;
STATIC_ASSERT(kMaxWatchpointCode < kMaxStopCode);
// ----- Fields offset and length.
static const int kOpcodeShift = 26;
static const int kOpcodeBits = 6;

View File

@ -670,14 +670,6 @@ void MacroAssembler::li(Register rd, Operand j, bool gen2instr) {
}
// Exception-generating instructions and debugging support.
void MacroAssembler::stop(const char* msg) {
// TO_UPGRADE: Just a break for now. Maybe we could upgrade it.
// We use the 0x54321 value to be able to find it easily when reading memory.
break_(0x54321);
}
void MacroAssembler::MultiPush(RegList regs) {
int16_t NumSaved = 0;
int16_t NumToPush = NumberOfBitsSet(regs);

View File

@ -459,9 +459,6 @@ DECLARE_NOTARGET_PROTOTYPE(Ret)
li(dst, Operand(value), gen2instr);
}
// Exception-generating instructions and debugging support.
void stop(const char* msg);
// Push multiple registers on the stack.
// Registers are saved in numerical order, with higher numbered registers
// saved in higher memory addresses.

View File

@ -126,16 +126,29 @@ static void InitializeCoverage() {
void MipsDebugger::Stop(Instruction* instr) {
UNIMPLEMENTED_MIPS();
char* str = reinterpret_cast<char*>(instr->InstructionBits());
if (strlen(str) > 0) {
// Get the stop code.
uint32_t code = instr->Bits(25, 6);
// Retrieve the encoded address, which comes just after this stop.
char** msg_address =
reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize);
char* msg = *msg_address;
ASSERT(msg != NULL);
// Update this stop description.
if (!watched_stops[code].desc) {
watched_stops[code].desc = msg;
}
if (strlen(msg) > 0) {
if (coverage_log != NULL) {
fprintf(coverage_log, "%s\n", str);
fflush(coverage_log);
}
instr->SetInstructionBits(0x0); // Overwrite with nop.
// Overwrite the instruction and address with nops.
instr->SetInstructionBits(kNopInstr);
reinterpret_cast<Instr*>(msg_address)->SetInstructionBits(kNopInstr);
}
sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize);
sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstructionSize);
}
@ -147,9 +160,17 @@ static void InitializeCoverage() {}
void MipsDebugger::Stop(Instruction* instr) {
const char* str = reinterpret_cast<char*>(instr->InstructionBits());
PrintF("Simulator hit %s\n", str);
sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize);
// Get the stop code.
uint32_t code = instr->Bits(25, 6);
// Retrieve the encoded address, which comes just after this stop.
char* msg = *reinterpret_cast<char**>(sim_->get_pc() +
Instruction::kInstrSize);
// Update this stop description.
if (!sim_->watched_stops[code].desc) {
sim_->watched_stops[code].desc = msg;
}
PrintF("Simulator hit %s (%u)\n", msg, code);
sim_->set_pc(sim_->get_pc() + 2 * Instruction::kInstrSize);
Debug();
}
#endif // GENERATED_CODE_COVERAGE
@ -585,8 +606,67 @@ void MipsDebugger::Debug() {
}
} else if (strcmp(cmd, "flags") == 0) {
PrintF("No flags on MIPS !\n");
} else if (strcmp(cmd, "unstop") == 0) {
PrintF("Unstop command not implemented on MIPS.");
} else if (strcmp(cmd, "stop") == 0) {
int32_t value;
intptr_t stop_pc = sim_->get_pc() -
2 * Instruction::kInstrSize;
Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc);
Instruction* msg_address =
reinterpret_cast<Instruction*>(stop_pc +
Instruction::kInstrSize);
if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
// Remove the current stop.
if (sim_->IsStopInstruction(stop_instr)) {
stop_instr->SetInstructionBits(kNopInstr);
msg_address->SetInstructionBits(kNopInstr);
} else {
PrintF("Not at debugger stop.\n");
}
} else if (argc == 3) {
// Print information about all/the specified breakpoint(s).
if (strcmp(arg1, "info") == 0) {
if (strcmp(arg2, "all") == 0) {
PrintF("Stop information:\n");
for (uint32_t i = kMaxWatchpointCode + 1;
i <= kMaxStopCode;
i++) {
sim_->PrintStopInfo(i);
}
} else if (GetValue(arg2, &value)) {
sim_->PrintStopInfo(value);
} else {
PrintF("Unrecognized argument.\n");
}
} else if (strcmp(arg1, "enable") == 0) {
// Enable all/the specified breakpoint(s).
if (strcmp(arg2, "all") == 0) {
for (uint32_t i = kMaxWatchpointCode + 1;
i <= kMaxStopCode;
i++) {
sim_->EnableStop(i);
}
} else if (GetValue(arg2, &value)) {
sim_->EnableStop(value);
} else {
PrintF("Unrecognized argument.\n");
}
} else if (strcmp(arg1, "disable") == 0) {
// Disable all/the specified breakpoint(s).
if (strcmp(arg2, "all") == 0) {
for (uint32_t i = kMaxWatchpointCode + 1;
i <= kMaxStopCode;
i++) {
sim_->DisableStop(i);
}
} else if (GetValue(arg2, &value)) {
sim_->DisableStop(value);
} else {
PrintF("Unrecognized argument.\n");
}
}
} else {
PrintF("Wrong usage. Use help command for more information.\n");
}
} else if ((strcmp(cmd, "stat") == 0) || (strcmp(cmd, "st") == 0)) {
// Print registers and disassemble.
PrintAllRegs();
@ -652,9 +732,26 @@ void MipsDebugger::Debug() {
PrintF(" set a break point on the address\n");
PrintF("del\n");
PrintF(" delete the breakpoint\n");
PrintF("unstop\n");
PrintF(" ignore the stop instruction at the current location");
PrintF(" from now on\n");
PrintF("stop feature:\n");
PrintF(" Description:\n");
PrintF(" Stops are debug instructions inserted by\n");
PrintF(" the Assembler::stop() function.\n");
PrintF(" When hitting a stop, the Simulator will\n");
PrintF(" stop and and give control to the Debugger.\n");
PrintF(" All stop codes are watched:\n");
PrintF(" - They can be enabled / disabled: the Simulator\n");
PrintF(" will / won't stop when hitting them.\n");
PrintF(" - The Simulator keeps track of how many times they \n");
PrintF(" are met. (See the info command.) Going over a\n");
PrintF(" disabled stop still increases its counter. \n");
PrintF(" Commands:\n");
PrintF(" stop info all/<code> : print infos about number <code>\n");
PrintF(" or all stop(s).\n");
PrintF(" stop enable/disable all/<code> : enables / disables\n");
PrintF(" all or number <code> stop(s)\n");
PrintF(" stop unstop\n");
PrintF(" ignore the stop instruction at the current location\n");
PrintF(" from now on\n");
} else {
PrintF("Unknown command: %s\n", cmd);
}
@ -1288,7 +1385,7 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
// the break_ instruction, or several variants of traps. All
// Are "SPECIAL" class opcode, and are distinuished by function.
int32_t func = instr->FunctionFieldRaw();
int32_t code = (func == BREAK) ? instr->Bits(25, 6) : -1;
uint32_t code = (func == BREAK) ? instr->Bits(25, 6) : -1;
// We first check if we met a call_rt_redirected.
if (instr->InstructionBits() == rtCallRedirInstr) {
@ -1440,14 +1537,13 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
set_register(ra, saved_ra);
set_pc(get_register(ra));
} else if (func == BREAK && code >= 0 && code < 32) {
// First 32 break_ codes interpreted as debug-markers/watchpoints.
MipsDebugger dbg(this);
++break_count_;
PrintF("\n---- break %d marker: %3d (instr count: %8d) ----------"
"----------------------------------",
code, break_count_, icount_);
dbg.PrintAllRegs(); // Print registers and continue running.
} else if (func == BREAK && code <= kMaxStopCode) {
if (IsWatchpoint(code)) {
PrintWatchpoint(code);
} else {
IncreaseStopCounter(code);
HandleStop(code, instr);
}
} else {
// All remaining break_ codes, and all traps are handled here.
MipsDebugger dbg(this);
@ -1456,6 +1552,99 @@ void Simulator::SoftwareInterrupt(Instruction* instr) {
}
// Stop helper functions.
bool Simulator::IsWatchpoint(uint32_t code) {
return (code <= kMaxWatchpointCode);
}
void Simulator::PrintWatchpoint(uint32_t code) {
MipsDebugger dbg(this);
++break_count_;
PrintF("\n---- break %d marker: %3d (instr count: %8d) ----------"
"----------------------------------",
code, break_count_, icount_);
dbg.PrintAllRegs(); // Print registers and continue running.
}
void Simulator::HandleStop(uint32_t code, Instruction* instr) {
// Stop if it is enabled, otherwise go on jumping over the stop
// and the message address.
if (IsEnabledStop(code)) {
MipsDebugger dbg(this);
dbg.Stop(instr);
} else {
set_pc(get_pc() + 2 * Instruction::kInstrSize);
}
}
bool Simulator::IsStopInstruction(Instruction* instr) {
int32_t func = instr->FunctionFieldRaw();
uint32_t code = static_cast<uint32_t>(instr->Bits(25, 6));
return (func == BREAK) && code > kMaxWatchpointCode && code <= kMaxStopCode;
}
bool Simulator::IsEnabledStop(uint32_t code) {
ASSERT(code <= kMaxStopCode);
ASSERT(code > kMaxWatchpointCode);
return !(watched_stops[code].count & kStopDisabledBit);
}
void Simulator::EnableStop(uint32_t code) {
if (!IsEnabledStop(code)) {
watched_stops[code].count &= ~kStopDisabledBit;
}
}
void Simulator::DisableStop(uint32_t code) {
if (IsEnabledStop(code)) {
watched_stops[code].count |= kStopDisabledBit;
}
}
void Simulator::IncreaseStopCounter(uint32_t code) {
ASSERT(code <= kMaxStopCode);
if ((watched_stops[code].count & ~(1 << 31)) == 0x7fffffff) {
PrintF("Stop counter for code %i has overflowed.\n"
"Enabling this code and reseting the counter to 0.\n", code);
watched_stops[code].count = 0;
EnableStop(code);
} else {
watched_stops[code].count++;
}
}
// Print a stop status.
void Simulator::PrintStopInfo(uint32_t code) {
if (code <= kMaxWatchpointCode) {
PrintF("That is a watchpoint, not a stop.\n");
return;
} else if (code > kMaxStopCode) {
PrintF("Code too large, only %u stops can be used\n", kMaxStopCode + 1);
return;
}
const char* state = IsEnabledStop(code) ? "Enabled" : "Disabled";
int32_t count = watched_stops[code].count & ~kStopDisabledBit;
// Don't print the state of unused breakpoints.
if (count != 0) {
if (watched_stops[code].desc) {
PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n",
code, code, state, count, watched_stops[code].desc);
} else {
PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n",
code, code, state, count);
}
}
}
void Simulator::SignalExceptions() {
for (int i = 1; i < kNumExceptions; i++) {
if (exceptions[i] != 0) {

View File

@ -289,6 +289,18 @@ class Simulator {
// Used for breakpoints and traps.
void SoftwareInterrupt(Instruction* instr);
// Stop helper functions.
bool IsWatchpoint(uint32_t code);
void PrintWatchpoint(uint32_t code);
void HandleStop(uint32_t code, Instruction* instr);
bool IsStopInstruction(Instruction* instr);
bool IsEnabledStop(uint32_t code);
void EnableStop(uint32_t code);
void DisableStop(uint32_t code);
void IncreaseStopCounter(uint32_t code);
void PrintStopInfo(uint32_t code);
// Executes one instruction.
void InstructionDecode(Instruction* instr);
// Execute one instruction placed in a branch delay slot.
@ -354,6 +366,19 @@ class Simulator {
// Registered breakpoints.
Instruction* break_pc_;
Instr break_instr_;
// Stop is disabled if bit 31 is set.
static const uint32_t kStopDisabledBit = 1 << 31;
// A stop is enabled, meaning the simulator will stop when meeting the
// instruction, if bit 31 of watched_stops[code].count is unset.
// The value watched_stops[code].count & ~(1 << 31) indicates how many times
// the breakpoint was hit or gone through.
struct StopCountAndDesc {
uint32_t count;
char* desc;
};
StopCountAndDesc watched_stops[kMaxStopCode + 1];
};
@ -398,4 +423,3 @@ class SimulatorStack : public v8::internal::AllStatic {
#endif // !defined(USE_SIMULATOR)
#endif // V8_MIPS_SIMULATOR_MIPS_H_