您如何在程序中將本機映射到 IL 指令指針
在使用 .NET 框架的非託管 API 來分析 .NET 程序內程序時,是否可以查找與提供給 StackSnapshotCallback 函式的本機指令指針相關的 IL 指令指針?
可能很明顯,我正在拍攝目前堆棧的快照,並希望在堆棧轉儲中提供文件和行號資訊。託管堆棧資源管理器通過查詢
ISymUnmanagedMethod::GetSequencePoints. 這很好,但是序列點與偏移量相關聯,到目前為止我假設這些是從方法開始的偏移量(在中間語言中)。在對他的部落格文章Profiler stack walk: Basics and beyond的後續評論中,David Broman 表示可以使用
ICorDebugCode::GetILToNativeMapping. 但是,這並不理想,因為獲取此介面需要從另一個調試器程序附加到我的程序。我想避免這一步,因為我想在拍攝這些快照時繼續能夠從 Visual Studio 調試器中執行我的應用程序。它可以更輕鬆地點擊輸出視窗中的行號並轉到有問題的程式碼。
該功能是可能的……您可以在託管程式碼內部隨意吐出一個行號堆棧跟踪,唯一的問題是它是否可訪問。另外,我不想使用
System::Diagnostics::StackTraceorSystem::Environment::StackTrace功能,因為出於性能原因,我需要延遲堆棧的實際轉儲..所以節省用於稍後解析方法名稱和程式碼位置的成本是可取的.. . 以及混合本機和託管幀的能力。
為了從提供的本地指令指針轉換為
ICorProfilerInfo2::DoStackSnapshot中間語言方法偏移量,您必須採取兩個步驟,因為DoStackSnapshot提供了FunctionID本地指令指針作為虛擬記憶體地址。第 1 步,是將指令指針轉換為本機程式碼方法偏移量。(從 JITed 方法開始的偏移量)。這可以通過
ICorProfilerInfo2::GetCodeInfo2ULONG32 pcIL(0xffffffff); HRESULT hr(E_FAIL); COR_PRF_CODE_INFO* codeInfo(NULL); COR_DEBUG_IL_TO_NATIVE_MAP* map(NULL); ULONG32 cItem(0); UINT_PTR nativePCOffset(0xffffffff); if (SUCCEEDED(hr = pInfo->GetCodeInfo2(functioId, 0, &cItem, NULL)) && (NULL != (codeInfo = new COR_PRF_CODE_INFO[cItem]))) { if (SUCCEEDED(hr = pInfo->GetCodeInfo2(functionId, cItem, &cItem, codeInfo))) { COR_PRF_CODE_INFO *pCur(codeInfo), *pEnd(codeInfo + cItem); nativePCOffset = 0; for (; pCur < pEnd; pCur++) { // 'ip' is the UINT_PTR passed to the StackSnapshotCallback as named in // the docs I am looking at if ((ip >= pCur->startAddress) && (ip < (pCur->startAddress + pCur->size))) { nativePCOffset += (instructionPtr - pCur->startAddress); break; } else { nativePCOffset += pCur->size; } } } delete[] codeInfo; codeInfo = NULL; }第 2 步。一旦你從 natvie 程式碼方法的開頭有了一個偏移量,你可以使用它來轉換為從中間語言方法開始的偏移量,使用
ICorProfilerInfo2::GetILToNativeMapping.if ((nativePCOffset != -1) && SUCCEEDED(hr = pInfo->GetILToNativeMapping(functionId, 0, &cItem, NULL)) && (NULL != (map = new COR_DEBUG_IL_TO_NATIVE_MAP[cItem]))) { if (SUCCEEDED(pInfo->GetILToNativeMapping(functionId, cItem, &cItem, map))) { COR_DEBUG_IL_TO_NATIVE_MAP* mapCurrent = map + (cItem - 1); for (;mapCurrent >= map; mapCurrent--) { if ((mapCurrent->nativeStartOffset <= nativePCOffset) && (mapCurrent->nativeEndOffset > nativePCOffset)) { pcIL = mapCurrent->ilOffset; break; } } } delete[] map; map = NULL; }然後可以使用符號 API 將程式碼位置映射到文件和行號
感謝Mithun Shanbhag指導尋找解決方案。