mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Headless (and main): Improve crash reporting
This commit is contained in:
1 parent
22ac7ad119
commit
2d0e54f422
11 files changed
+78
-41
No files matched your search
+16
-16
@@ -28,7 +28,7 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
u8 codeByte2 = 0;
|
||||
|
||||
//Check for regular prefix
|
||||
info.operandSize = 4;
|
||||
info.operandSizeInBytes = 4;
|
||||
info.zeroExtend = false;
|
||||
info.signExtend = false;
|
||||
info.hasImmediate = false;
|
||||
@@ -47,7 +47,7 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
|
||||
if (*codePtr == 0x66)
|
||||
{
|
||||
info.operandSize = 2;
|
||||
info.operandSizeInBytes = 2;
|
||||
codePtr++;
|
||||
}
|
||||
else if (*codePtr == 0x67)
|
||||
@@ -68,7 +68,7 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
rex = *codePtr;
|
||||
if (rex & 8) //REX.W
|
||||
{
|
||||
info.operandSize = 8;
|
||||
info.operandSizeInBytes = 8;
|
||||
}
|
||||
codePtr++;
|
||||
}
|
||||
@@ -167,19 +167,19 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
|
||||
case MOVE_16_32BIT: //move 16 or 32-bit immediate, easiest case for writes
|
||||
{
|
||||
if (info.operandSize == 2)
|
||||
if (info.operandSizeInBytes == 2)
|
||||
{
|
||||
info.hasImmediate = true;
|
||||
info.immediate = *(u16*)codePtr;
|
||||
codePtr += 2;
|
||||
}
|
||||
else if (info.operandSize == 4)
|
||||
else if (info.operandSizeInBytes == 4)
|
||||
{
|
||||
info.hasImmediate = true;
|
||||
info.immediate = *(u32*)codePtr;
|
||||
codePtr += 4;
|
||||
}
|
||||
else if (info.operandSize == 8)
|
||||
else if (info.operandSizeInBytes == 8)
|
||||
{
|
||||
info.zeroExtend = true;
|
||||
info.immediate = *(u32*)codePtr;
|
||||
@@ -213,36 +213,36 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
{
|
||||
case MOVZX_BYTE: //movzx on byte
|
||||
info.zeroExtend = true;
|
||||
info.operandSize = 1;
|
||||
info.operandSizeInBytes = 1;
|
||||
break;
|
||||
case MOVZX_SHORT: //movzx on short
|
||||
info.zeroExtend = true;
|
||||
info.operandSize = 2;
|
||||
info.operandSizeInBytes = 2;
|
||||
break;
|
||||
case MOVSX_BYTE: //movsx on byte
|
||||
info.signExtend = true;
|
||||
info.operandSize = 1;
|
||||
info.operandSizeInBytes = 1;
|
||||
break;
|
||||
case MOVSX_SHORT: //movsx on short
|
||||
info.signExtend = true;
|
||||
info.operandSize = 2;
|
||||
info.operandSizeInBytes = 2;
|
||||
break;
|
||||
case MOVUPS_MOVSS_FROM_RM: //movups/movss xmm, xmm/m (load)
|
||||
info.instructionClass = hasF3Prefix ? InstructionClass::FP : InstructionClass::FP_SIMD;
|
||||
info.operandSize = hasF3Prefix ? 4 : 16;
|
||||
info.operandSizeInBytes = hasF3Prefix ? 4 : 16;
|
||||
break;
|
||||
case MOVUPS_MOVSS_TO_RM: //movups/movss xmm/m, xmm (store)
|
||||
info.instructionClass = hasF3Prefix ? InstructionClass::FP : InstructionClass::FP_SIMD;
|
||||
info.operandSize = hasF3Prefix ? 4 : 16;
|
||||
info.operandSizeInBytes = hasF3Prefix ? 4 : 16;
|
||||
info.isMemoryWrite = true;
|
||||
break;
|
||||
case MOVAPS_FROM_RM: //movaps xmm, xmm/m (load)
|
||||
info.instructionClass = InstructionClass::FP_SIMD;
|
||||
info.operandSize = 16;
|
||||
info.operandSizeInBytes = 16;
|
||||
break;
|
||||
case MOVAPS_TO_RM: //movaps xmm/m, xmm (store)
|
||||
info.instructionClass = InstructionClass::FP_SIMD;
|
||||
info.operandSize = 16;
|
||||
info.operandSizeInBytes = 16;
|
||||
info.isMemoryWrite = true;
|
||||
break;
|
||||
default:
|
||||
@@ -250,9 +250,9 @@ bool X86AnalyzeMOV(const unsigned char *codePtr, LSInstructionInfo &info)
|
||||
}
|
||||
break;
|
||||
case 0x8a:
|
||||
if (info.operandSize == 4)
|
||||
if (info.operandSizeInBytes == 4)
|
||||
{
|
||||
info.operandSize = 1;
|
||||
info.operandSizeInBytes = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -27,9 +27,8 @@ enum class InstructionClass {
|
||||
FP_SIMD, // Full vector register (movups, movaps, movdqa, ...)
|
||||
};
|
||||
|
||||
struct LSInstructionInfo
|
||||
{
|
||||
int operandSize; //1, 2, 4, 8 (in bytes, despite the field name suggesting bits)
|
||||
struct LSInstructionInfo {
|
||||
int operandSizeInBytes; // 1, 2, 4, 8 (in bytes, despite the field name suggesting bits)
|
||||
int instructionSize;
|
||||
int regOperandReg;
|
||||
int otherReg;
|
||||
@@ -41,6 +40,8 @@ struct LSInstructionInfo
|
||||
u64 immediate;
|
||||
s32 displacement;
|
||||
InstructionClass instructionClass;
|
||||
|
||||
int OperandSizeInBytes() const { return operandSizeInBytes; }
|
||||
};
|
||||
|
||||
struct ModRM
|
||||
|
||||
+27
-4
@@ -246,6 +246,7 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe
|
||||
}
|
||||
PRINT_STDOUT(" --state=FILE load state from FILE\n");
|
||||
|
||||
PRINT_STDOUT(" --cpu=CPU use the specified CPU core (interpreter, ir, jit, jit-ir)\n");
|
||||
PRINT_STDOUT(" -i use the interpreter\n");
|
||||
PRINT_STDOUT(" -r use IR interpreter\n");
|
||||
PRINT_STDOUT(" -j use JIT\n");
|
||||
@@ -290,6 +291,7 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe
|
||||
// Actually might want to reconsider given Android...
|
||||
CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], CmdLineMode mode) {
|
||||
this->mode = mode;
|
||||
constexpr std::string_view cpuBackendStr = "--cpu=";
|
||||
constexpr std::string_view gpuBackendStr = "--graphics=";
|
||||
constexpr std::string_view configOption = "--config=";
|
||||
constexpr std::string_view controlsOption = "--controlconfig=";
|
||||
@@ -413,7 +415,27 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C
|
||||
} else {
|
||||
// Bad value, report error and exit.
|
||||
PRINT_STDERR("Invalid value for --graphics=: %s", restOfOption.c_str());
|
||||
return CommandLineParseResult::Exit;
|
||||
return CommandLineParseResult::Error;
|
||||
}
|
||||
} else if (startsWith(argv[i], cpuBackendStr)) {
|
||||
const std::string restOfOption = argv[i] + cpuBackendStr.size();
|
||||
// Force software rendering off, as picking gles implies HW acceleration.
|
||||
// We could add more options for software such as "software-gles",
|
||||
// "software-vulkan" and "software-d3d11", or something similar.
|
||||
// For now, software rendering force-activates OpenGL.
|
||||
double glVersionTemp = 0.0f;
|
||||
if (restOfOption == "interpreter") {
|
||||
cpuCore = CPUCore::INTERPRETER;
|
||||
} else if (restOfOption == "jit") {
|
||||
cpuCore = CPUCore::JIT;
|
||||
} else if (restOfOption == "jit-ir") {
|
||||
cpuCore = CPUCore::JIT_IR;
|
||||
} else if (restOfOption == "ir") {
|
||||
cpuCore = CPUCore::IR_INTERPRETER;
|
||||
} else {
|
||||
// Bad value, report error and exit.
|
||||
PRINT_STDERR("Invalid value for --cpu=: %s", restOfOption.c_str());
|
||||
return CommandLineParseResult::Error;
|
||||
}
|
||||
} else if (startsWith(argv[i], configOption)) {
|
||||
configFilename = std::string(argv[i] + configOption.size());
|
||||
@@ -429,10 +451,11 @@ CommandLineParseResult CommandLineOptions::Parse(int argc, const char *argv[], C
|
||||
continue;
|
||||
} else {
|
||||
PRINT_STDERR("Error: --ignore requires an argument.\n");
|
||||
return CommandLineParseResult::Exit;
|
||||
return CommandLineParseResult::Error;
|
||||
}
|
||||
} else {
|
||||
// Report unknown argument later once this is complete.
|
||||
} else if (startsWith(argv[i], "--")) {
|
||||
PRINT_STDERR("Error: Unknown parameter: %s\n", argv[i]);
|
||||
return CommandLineParseResult::Error;
|
||||
}
|
||||
// To the next argument.
|
||||
i++;
|
||||
|
||||
+18
-6
@@ -665,13 +665,25 @@ static std::string ModuleAddressSuffix(u32 address) {
|
||||
|
||||
void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string_view additionalInfo) {
|
||||
// In jit, we only flush PC when bIgnoreBadMemAccess is off.
|
||||
|
||||
char pcDetails[128];
|
||||
pcDetails[0] = 0;
|
||||
if ((CPUCore)g_Config.iCpuCore == CPUCore::INTERPRETER) {
|
||||
snprintf(pcDetails, sizeof(pcDetails), " PC %08x%s RA %08x%s",
|
||||
pc, ModuleAddressSuffix(pc).c_str(),
|
||||
switch ((CPUCore)g_Config.iCpuCore) {
|
||||
case CPUCore::INTERPRETER:
|
||||
snprintf(pcDetails, sizeof(pcDetails), "Interpreter: PC %08x%s RA %08x%s",
|
||||
currentMIPS->pc, ModuleAddressSuffix(currentMIPS->pc).c_str(),
|
||||
currentMIPS->r[MIPS_REG_RA], ModuleAddressSuffix(currentMIPS->r[MIPS_REG_RA]).c_str());
|
||||
break;
|
||||
case CPUCore::JIT:
|
||||
snprintf(pcDetails, sizeof(pcDetails), "JIT: (PC approximate)=%08x%s", pc, ModuleAddressSuffix(pc).c_str());
|
||||
break;
|
||||
case CPUCore::JIT_IR:
|
||||
snprintf(pcDetails, sizeof(pcDetails), "JIT_IR: (PC approximate)=%08x%s", pc, ModuleAddressSuffix(pc).c_str());
|
||||
break;
|
||||
case CPUCore::IR_INTERPRETER:
|
||||
snprintf(pcDetails, sizeof(pcDetails), "IR_INTERPRETER: (PC approximate)=%08x%s", pc, ModuleAddressSuffix(pc).c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const std::string addressSuffix = ModuleAddressSuffix(address);
|
||||
@@ -691,14 +703,14 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy
|
||||
|
||||
const char *desc = MemoryExceptionTypeAsString(type);
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "%s: SIGSEGV pc=%08x%s (size %08x) %sHost:%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, STR_VIEW(additionalInfo));
|
||||
snprintf(msg, sizeof(msg), "%s: SIGSEGV at %08x%s (size: %d bytes) %s\nHost:%.*s", desc, address, addressSuffix.c_str(), accessSize, pcDetails, STR_VIEW(additionalInfo));
|
||||
if (action == ExceptionAction::Ignore) {
|
||||
Core_SendDebugOutput(LogLevel::LWARNING, msg);
|
||||
return;
|
||||
}
|
||||
const std::string stackTrace = FormatStackTrace(WalkCurrentStack(-1));
|
||||
// Do the most detailed logging we can.
|
||||
Core_SendDebugOutput(LogLevel::LERROR, StringFromFormat("%sCall stack:\n%s", msg, stackTrace.c_str()));
|
||||
Core_SendDebugOutput(LogLevel::LERROR, StringFromFormat("%sMIPS call stack:\n%s", msg, stackTrace.c_str()));
|
||||
if (action == ExceptionAction::Break) {
|
||||
MIPSExceptionInfo &e = g_exceptionInfo;
|
||||
e = {};
|
||||
|
||||
+6
-9
@@ -255,11 +255,11 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
|
||||
std::string infoString = "";
|
||||
std::string temp;
|
||||
if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr(codePtr, temp)) {
|
||||
infoString += temp + "\n";
|
||||
infoString += temp + " ";
|
||||
}
|
||||
temp.clear();
|
||||
if (DisassembleNativeAt(codePtr, instructionSize, &temp)) {
|
||||
infoString += temp + "\n";
|
||||
infoString += "(" + temp + ") ";
|
||||
}
|
||||
|
||||
// Either bIgnoreBadMemAccess is off, or we failed recovery analysis.
|
||||
@@ -267,7 +267,7 @@ bool HandleFault(uintptr_t hostAddress, void *ctx) {
|
||||
uint32_t approximatePC = currentMIPS->pc;
|
||||
// TODO: Determine access size from the disassembled native instruction. We have some partial info already,
|
||||
// just need to clean it up.
|
||||
Core_MemoryException(guestAddress, 0, approximatePC, type, infoString);
|
||||
Core_MemoryException(guestAddress, info.OperandSizeInBytes(), approximatePC, type, infoString);
|
||||
|
||||
// There's a small chance we can resume from this type of crash.
|
||||
g_lastCrashAddress = codePtr;
|
||||
@@ -320,16 +320,13 @@ std::vector<MIPSStackWalk::StackFrame> WalkCurrentStack(int threadID) {
|
||||
std::string FormatStackTrace(const std::vector<MIPSStackWalk::StackFrame> &frames) {
|
||||
std::stringstream str;
|
||||
for (const auto &frame : frames) {
|
||||
if (frame.pc == 0xFFFFFFFF) {
|
||||
// Bottom of stack, probably.
|
||||
continue;
|
||||
}
|
||||
const u32 frameEntry = frame.entry == 0xFFFFFFFF ? 0 : frame.entry;
|
||||
std::string desc = g_symbolMap->GetDescription(frame.entry);
|
||||
char moduleDesc[96];
|
||||
if (DescribeKernelModuleAddress(frame.entry, moduleDesc, sizeof(moduleDesc))) {
|
||||
str << StringFromFormat("%s [%s] (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), moduleDesc, frame.entry, frame.pc - frame.entry, frame.pc, frame.sp);
|
||||
str << StringFromFormat("%s [%s] (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), moduleDesc, frameEntry, frame.pc - frameEntry, frame.pc, frame.sp);
|
||||
} else {
|
||||
str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frame.entry, frame.pc - frame.entry, frame.pc, frame.sp);
|
||||
str << StringFromFormat("%s (%08x+%03x, pc: %08x sp: %08x)\n", desc.c_str(), frameEntry, frame.pc - frameEntry, frame.pc, frame.sp);
|
||||
}
|
||||
}
|
||||
return str.str();
|
||||
|
||||
@@ -41,7 +41,7 @@ struct Arm64LSInstructionInfo {
|
||||
int Rn;
|
||||
int Rm;
|
||||
|
||||
// TODO: more.
|
||||
int OperandSizeInBytes() const { return 1 << size; }
|
||||
};
|
||||
|
||||
bool Arm64AnalyzeLoadStore(uint64_t addr, uint32_t op, Arm64LSInstructionInfo *info);
|
||||
@@ -45,6 +45,7 @@ struct ArmLSInstructionInfo {
|
||||
int Rm;
|
||||
|
||||
// TODO: more.
|
||||
int OperandSizeInBytes() const { return 1 << size; }
|
||||
};
|
||||
|
||||
bool ArmAnalyzeLoadStore(uint32_t addr, uint32_t op, ArmLSInstructionInfo *info);
|
||||
@@ -2761,6 +2761,8 @@ struct LoongArch64LSInstructionInfo {
|
||||
bool isFPLoadStore;
|
||||
int size; // 0 = 8-bit, 1 = 16-bit, 2 = 32-bit, 3 = 64-bit
|
||||
bool isMemoryWrite;
|
||||
|
||||
int OperandSizeInBytes() const { return size; }
|
||||
};
|
||||
|
||||
uint32_t la_assemble(Ins *ins);
|
||||
|
||||
@@ -601,6 +601,7 @@ struct RiscVLSInstructionInfo {
|
||||
bool isFPLoadStore;
|
||||
int size; // Size of the access, in bytes (1, 2, 4, or 8).
|
||||
bool isMemoryWrite;
|
||||
int OperandSizeInBytes() const { return size; }
|
||||
};
|
||||
|
||||
bool RiscVAnalyzeLoadStore(uint64_t addr, uint32_t word, RiscVLSInstructionInfo *info);
|
||||
|
||||
@@ -630,7 +630,7 @@ int main(int argc, const char* argv[]) {
|
||||
// Somehow this affects the test execution of pspautotests/tests/gpu/vertices/morph.prx, even though
|
||||
// we actually set the cpu core in CoreParameter below. Probably because we end up using the JIT vs non-JIT
|
||||
// vertex decoder.
|
||||
g_Config.iCpuCore = 0;
|
||||
g_Config.iCpuCore = (int)cpuCore;
|
||||
|
||||
// NOTE: In headless mode, we never save the config. This is just for this run.
|
||||
g_Config.iDumpFileTypes = 0;
|
||||
|
||||
@@ -32,7 +32,7 @@ static bool CheckAnalyze(const Gen::XEmitter &emit, bool expectWrite, Instructio
|
||||
EXPECT_EQ_INT(info.instructionSize, (int)(emit.GetCodePointer() - prevStart));
|
||||
EXPECT_EQ_INT(info.isMemoryWrite, expectWrite);
|
||||
EXPECT_EQ_INT((int)info.instructionClass, (int)expectClass);
|
||||
EXPECT_EQ_INT(info.operandSize, expectOperandSize);
|
||||
EXPECT_EQ_INT(info.operandSizeInBytes, expectOperandSize);
|
||||
EXPECT_EQ_INT(info.zeroExtend, expectZeroExtend);
|
||||
EXPECT_EQ_INT(info.signExtend, expectSignExtend);
|
||||
return true;
|
||||
|
||||
Reference in new issue
Block a user