Как вы отображаете нативный указатель на инструкцию IL в процессе - PullRequest
11 голосов
/ 13 октября 2008

При использовании неуправляемого API для .NET Framework для профилирования внутрипроцессного процесса .NET можно ли искать указатель инструкций IL, который соответствует собственному указателю инструкций, предоставленному функции StackSnapshotCallback?

Как очевидно, я делаю снимок текущего стека и хотел бы предоставить информацию о файле и номере строки в дампе стека. Managed Stack Explorer делает это, запрашивая ISymUnmanagedMethod::GetSequencePoints. Это здорово, но точки последовательности связаны со смещениями, и я до сих пор предполагал, что это смещения с начала метода (на промежуточном языке).

В последующем комментарии к своему сообщению в блоге Прогулка по стеку профилировщика: основы и далее , Дэвид Броман указывает, что это сопоставление может быть достигнуто с помощью ICorDebugCode::GetILToNativeMapping. Однако это не идеально, поскольку для получения этого интерфейса требуется присоединение к моему процессу другого процесса отладчика.

Я бы хотел избежать этого шага, потому что я хотел бы продолжать иметь возможность запускать свое приложение из отладчика Visual Studio, пока я делаю эти снимки. Это упрощает нажатие на номер строки в окне вывода и переход к соответствующему коду.

Функциональность возможна .... вы можете выплюнуть трассировку стека с нумерацией строк по желанию внутри управляемого кода, единственный вопрос, доступен ли он. Кроме того, я не хочу использовать функциональность System::Diagnostics::StackTrace или System::Environment::StackTrace, потому что по соображениям производительности мне нужно отложить фактический дамп стека .... таким образом, сохраняя затраты на разрешение имен методов и расположения кода на потом желательно ... наряду с возможностью смешивать собственные и управляемые кадры.

Ответы [ 2 ]

7 голосов
/ 01 ноября 2008

Чтобы преобразовать указатель встроенной команды, предоставленный ICorProfilerInfo2::DoStackSnapshot, в смещение метода промежуточного языка, необходимо выполнить два шага, поскольку DoStackSnapshot предоставляет FunctionID и указатель встроенной инструкции в качестве адреса виртуальной памяти. 1004 *

Шаг 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 символов

Спасибо Митхуну Шанбхагу за руководство в поиске решения.

0 голосов
/ 18 октября 2008
Console.WriteLine("StackTrace: '{0}'", Environment.StackTrace);

Убедитесь, что ваша сборка генерирует символы.

Расширяя обсуждение:

Как очевидно, я делаю снимок текущего стека и хотел бы предоставить информацию о файле и номере строки в дампе стека.

Учитывая это - похоже, единственная причина, по которой вы не подключаетесь к процессу, заключается в том, что вы можете легко отлаживать свой инструмент или его части по мере его разработки. Это IMO - плохое оправдание для того, чтобы не выбрать лучший дизайн (ICorDebug или w / e), когда он доступен. Причина его плохого дизайна в том, что ваш код выполняется в пространстве процессов (предположительно) внешних двоичных файлов, вызывая неприятные (иногда редкие) побочные эффекты (включая повреждение чужих данных) в известных (или хуже - неизвестных) поврежденных состояниях процесса. Этого должно быть достаточно для начала, но даже в противном случае есть несколько крайних случаев с многопоточным кодом и т. Д., Где необходимо обойти дизайн.

Большинство людей обычно спрашивают: «Что вы на самом деле пытаетесь сделать?» как ответ на явно сложный способ ведения дел. В большинстве случаев существует более простой / легкий способ. Написав трассировщик стека для нативного кода, я знаю, что он может запутаться.

Теперь, может быть, у вас все получится, так что - просто мои $ .02

...