Dot-Net

您如何在程序中將本機映射到 IL 指令指針

  • February 28, 2019

在使用 .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::GetCodeInfo2

ULONG32 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指導尋找解決方案。

引用自:https://stackoverflow.com/questions/198754