diff --git a/src/scripting/vm/jit.cpp b/src/scripting/vm/jit.cpp index 319130aa45..4523143d76 100644 --- a/src/scripting/vm/jit.cpp +++ b/src/scripting/vm/jit.cpp @@ -26,9 +26,7 @@ JitFuncPtr JitCompile(VMScriptFunction *sfunc) code.setLogger(&logger); JitCompiler compiler(&code, sfunc); - CCFunc *func = compiler.Codegen(); - - return reinterpret_cast(AddJitFunction(&code, func)); + return reinterpret_cast(AddJitFunction(&code, &compiler)); } catch (const CRecoverableError &e) { diff --git a/src/scripting/vm/jit.h b/src/scripting/vm/jit.h index 618f37d4ba..e420585090 100644 --- a/src/scripting/vm/jit.h +++ b/src/scripting/vm/jit.h @@ -5,3 +5,4 @@ JitFuncPtr JitCompile(VMScriptFunction *func); void JitDumpLog(FILE *file, VMScriptFunction *func); +FString JitCaptureStackTrace(); diff --git a/src/scripting/vm/jit_runtime.cpp b/src/scripting/vm/jit_runtime.cpp index 68b7988a02..b120328dc4 100644 --- a/src/scripting/vm/jit_runtime.cpp +++ b/src/scripting/vm/jit_runtime.cpp @@ -2,6 +2,19 @@ #include "jit.h" #include "jitintern.h" +#ifndef WIN32 +#include +#endif + +struct JitFuncInfo +{ + FString name; + FString filename; + void *start; + void *end; +}; + +static TArray JitDebugInfo; static TArray JitBlocks; static TArray JitFrames; static size_t JitBlockPos = 0; @@ -230,10 +243,12 @@ static TArray CreateUnwindInfoWindows(asmjit::CCFunc *func) return info; } -void *AddJitFunction(asmjit::CodeHolder* code, asmjit::CCFunc *func) +void *AddJitFunction(asmjit::CodeHolder* code, JitCompiler *compiler) { using namespace asmjit; + CCFunc *func = compiler->Codegen(); + size_t codeSize = code->getCodeSize(); if (codeSize == 0) return nullptr; @@ -278,6 +293,8 @@ void *AddJitFunction(asmjit::CodeHolder* code, asmjit::CCFunc *func) I_Error("RtlAddFunctionTable failed"); #endif + JitDebugInfo.Push({ compiler->GetScriptFunction()->PrintableName, compiler->GetScriptFunction()->SourceFileName, startaddr, endaddr }); + return p; } @@ -664,10 +681,12 @@ static TArray CreateUnwindInfoUnix(asmjit::CCFunc *func, unsigned int & return stream; } -void *AddJitFunction(asmjit::CodeHolder* code, asmjit::CCFunc *func) +void *AddJitFunction(asmjit::CodeHolder* code, JitCompiler *compiler) { using namespace asmjit; + CCFunc *func = compiler->Codegen(); + size_t codeSize = code->getCodeSize(); if (codeSize == 0) return nullptr; @@ -764,8 +783,98 @@ void JitRelease() { asmjit::OSUtils::releaseVirtualMemory(p, 1024 * 1024); } + JitDebugInfo.Clear(); JitFrames.Clear(); JitBlocks.Clear(); JitBlockPos = 0; JitBlockSize = 0; } + +static int CaptureStackTrace(int max_frames, void **out_frames) +{ + memset(out_frames, 0, sizeof(void *) * max_frames); + +#ifdef _WIN64 + // RtlCaptureStackBackTrace doesn't support RtlAddFunctionTable.. + + CONTEXT context; + RtlCaptureContext(&context); + + UNWIND_HISTORY_TABLE history; + memset(&history, 0, sizeof(UNWIND_HISTORY_TABLE)); + + ULONG64 establisherframe = 0; + PVOID handlerdata = nullptr; + + int frame; + for (frame = 0; frame < max_frames; frame++) + { + ULONG64 imagebase; + PRUNTIME_FUNCTION rtfunc = RtlLookupFunctionEntry(context.Rip, &imagebase, &history); + + KNONVOLATILE_CONTEXT_POINTERS nvcontext; + memset(&nvcontext, 0, sizeof(KNONVOLATILE_CONTEXT_POINTERS)); + if (!rtfunc) + { + // Leaf function + context.Rip = (ULONG64)(*(PULONG64)context.Rsp); + context.Rsp += 8; + } + else + { + RtlVirtualUnwind(UNW_FLAG_NHANDLER, imagebase, context.Rip, rtfunc, &context, &handlerdata, &establisherframe, &nvcontext); + } + + if (!context.Rip) + break; + + out_frames[frame] = (void*)context.Rip; + } + return frame; + +#elif defined(WIN32) + // JIT isn't supported here, so just do nothing. + return 0;//return RtlCaptureStackBackTrace(0, MIN(max_frames, 32), out_frames, nullptr); +#else + return backtrace(out_frames, max_frames); +#endif +} + +FString JitGetStackFrameName(void *pc) +{ + FString s; + + for (unsigned int i = 0; i < JitDebugInfo.Size(); i++) + { + const auto &info = JitDebugInfo[i]; + if (pc >= info.start && pc < info.end) + { + int line = -1; + /*for (unsigned int j = 0; j < info.lines.Size(); j++) + { + if (info.lines[j].pc <= pc) + line = info.lines[j].line; + }*/ + + if (line == -1) + s.Format("Called from %s at %s\n", info.name.GetChars(), info.filename.GetChars()); + else + s.Format("Called from %s at %s, line %d\n", info.name.GetChars(), info.filename.GetChars(), line); + } + } + + return s; +} + +FString JitCaptureStackTrace() +{ + void *frames[32]; + int numframes = CaptureStackTrace(32, frames); + + FString s; + for (int i = 0; i < numframes; i++) + { + s += JitGetStackFrameName(frames[i]); + } + return s; +} diff --git a/src/scripting/vm/jitintern.h b/src/scripting/vm/jitintern.h index 2f67e6ed35..89eb3dd63d 100644 --- a/src/scripting/vm/jitintern.h +++ b/src/scripting/vm/jitintern.h @@ -31,6 +31,7 @@ public: JitCompiler(asmjit::CodeHolder *code, VMScriptFunction *sfunc) : cc(code), sfunc(sfunc) { } asmjit::CCFunc *Codegen(); + VMScriptFunction *GetScriptFunction() { return sfunc; } private: // Declare EmitXX functions for the opcodes: @@ -308,5 +309,5 @@ public: } }; -void *AddJitFunction(asmjit::CodeHolder* code, asmjit::CCFunc *func); +void *AddJitFunction(asmjit::CodeHolder* code, JitCompiler *compiler); asmjit::CodeInfo GetHostCodeInfo(); diff --git a/src/scripting/vm/vmframe.cpp b/src/scripting/vm/vmframe.cpp index 66af710f25..5ada4d87e0 100644 --- a/src/scripting/vm/vmframe.cpp +++ b/src/scripting/vm/vmframe.cpp @@ -658,7 +658,11 @@ CVMAbortException::CVMAbortException(EVMAbortException reason, const char *morei size_t len = strlen(m_Message); myvsnprintf(m_Message + len, MAX_ERRORTEXT - len, moreinfo, ap); } - stacktrace = ""; + + if (vm_jit) + stacktrace = JitCaptureStackTrace(); + else + stacktrace = ""; } // Print this only once on the first catch block.