Как читать стек вызовов? - PullRequest
2 голосов
/ 31 июля 2009

У нас есть родное приложение C ++, работающее через COM + на сервере Windows 2003. Недавно я заметил от средства просмотра событий, что его исключения генерируются, в частности, исключение C0000005, которое, согласно http://blogs.msdn.com/calvin_hsia/archive/2004/06/30/170344.aspx, означает, что процесс пытается записать в память не в пределах своего адресного пространства, иначе нарушение доступа.

Запись в средстве просмотра событий обеспечивает стек вызовов:

LibFmwk! UTIL_GetDateFromLogByDayDirectory (char const *, класс utilCDate &) + 0xa26c LibFmwk! UTIL_GetDateFromLogByDayDirectory (char const *, класс utilCDate &) + 0x8af4 LibFmwk! UTIL_GetDateFromLogByDayDirectory (char const *, класс utilCDate &) + 0x13a1 LibFmwk! UtilCLogController :: GetFLFInfoLevel (void) const + 0x1070 LibFmwk! UtilCLogController :: GetFLFInfoLevel (void) const + 0x186

Теперь я понимаю, что это дает мне имена методов, чтобы посмотреть на них, но у меня возникает ощущение, что адрес в конце каждой строки (например, + 0xa26c) пытается указать мне конкретную строку или инструкцию в этом методе. .

Итак, мои вопросы:

  1. Кто-нибудь знает, как я мог бы использовать этот адрес или любую другую информацию в стеке вызовов, чтобы определить, на какую строку в коде он переключается?
  2. Есть ли какие-нибудь ресурсы, которые я мог бы прочитать, чтобы лучше понять стеки вызовов,
  3. Существуют ли какие-либо бесплатные / открытые инструменты, которые могут помочь в анализе стека вызовов, возможно, путем присоединения к файлу символов отладки и / или двоичным файлам?

Edit: По запросу, вот метод, который, кажется, вызывает проблему:

BOOL UTIL_GetDateFromLogByDayDirectory(LPCSTR pszDir, utilCDate& oDate)
{
BOOL bRet = FALSE;

if ((pszDir[0] == '%') &&
    ::isdigit(pszDir[1]) && ::isdigit(pszDir[2]) &&
    ::isdigit(pszDir[3]) && ::isdigit(pszDir[4]) &&
    ::isdigit(pszDir[5]) && ::isdigit(pszDir[6]) &&
    ::isdigit(pszDir[7]) && ::isdigit(pszDir[8]) &&
    !pszDir[9])
{
    char acCopy[9];
    ::memcpy(acCopy, pszDir + 1, 8);
    acCopy[8] = '\0';

    int iDay = ::atoi(&acCopy[6]);
    acCopy[6] = '\0';
    int iMonth = ::atoi(&acCopy[4]);
    acCopy[4] = '\0';
    int iYear = ::atoi(&acCopy[0]);

    oDate.Set(iDay, iMonth, iYear);

    bRet = TRUE;
}

return (bRet);

}

Это код, написанный более 10 лет назад одним из членов нашей компании, который давно ушел, поэтому я не предполагаю точно знать, что он делает, но я знаю, что он участвует в процессе переименования каталога журналов. от «Сегодня» до конкретной даты, например % 20090329. Индексирование массива, memcpy и адрес операторов делают его довольно подозрительным.

Другая проблема, с которой мы сталкиваемся, заключается в том, что это происходит только в производственной системе, мы никогда не смогли воспроизвести ее в наших тестовых системах или системах разработки, что позволило бы нам подключить отладчик.

Очень ценится! Andy

Ответы [ 3 ]

5 голосов
/ 06 августа 2009

Другие говорили это между строк, но не явно. посмотрите на:

LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate &) + 0xa26c

Смещение 0xa26c равно огромное , далеко после конца функции. очевидно, что отладчик не имеет надлежащих символов для LibFmwk, поэтому вместо этого он полагается на экспорт DLL и показывает смещение относительно ближайшего, которое он может найти.

Так что, да, возьмите правильные символы, и тогда это будет бриз. UTIL_GetDateFromLogByDayDirectory здесь не виноват.

3 голосов
/ 31 июля 2009

, если вам действительно нужно сопоставить эти адреса с вашими функциями - вам нужно будет работать с файлом .MAP и посмотреть, куда эти адреса действительно указывают.

Но, находясь в вашей ситуации, я бы предпочел исследовать эту проблему в отладчике (например, отладчик MSVS или windbg); в качестве альтернативы (если сбой происходит на сайте клиента), вы можете создать аварийный дамп и изучить его локально - это можно сделать с помощью Windows MiniDumpWriteDump API или утилиты SysInternals ProcDump (http://download.sysinternals.com/Files/procdump.zip).

Убедитесь, что все необходимые файлы символов созданы и доступны (также настройте путь к серверу символов Microsoft, чтобы точки входа Windows DLL также были разрешены).

ИМХО, это именно тот веб-сайт, который вам нужен: http://www.dumpanalysis.org - это лучший ресурс для ответов на все ваши вопросы. Также обратите внимание на этот PDF - http://windbg.info/download/doc/pdf/WinDbg_A_to_Z_color.pdf

1 голос
/ 31 июля 2009

Пункты 2 и 3 легко ответить:

3-я точка. Любой отладчик. Вот для чего они сделаны. Установите ваш отладчик, чтобы сломать это специальное исключение. Вы должны быть в состоянии щелкнуть себя по стеку вызовов и найти различные вызовы в стеке (по крайней мере, Delphi может сделать это, так что Visual Studio также должен это делать). Компиляция без оптимизаций, если это возможно. OllyDBG также может работать - возможно, в сочетании с функциональностью трассировки.

2-я точка. Любая информация о x86 Assembler, Reverseengineering ... Попробуйте: OpenRCE , Документация NASM , Сообщество ASM .

1-я точка. Callstack сообщает вам функции. Я не знаю, написано ли это по порядку или в обратном порядке, поэтому может оказаться, что первая строка - это последняя вызванная функция или первая вызванная функция. Следите за звонками с помощью отладчика. Иногда вы можете переключаться между asm и кодом (в зависимости от отладчика, файлов карты ...). Если у вас нет источника - изучите ассемблер, прочитайте о реверс-инжиниринге. Прочитайте документацию по функциям, которые вы вызываете в сторонних компонентах. Возможно, вы не удовлетворяете предварительному условию.

Если вы можете рассказать немного больше о программе (какие части исходного кода у вас есть, задействован ли библиотечный вызов ?, ...)


Теперь немного чтения кода:

Функция принимает указатель на строку с нулем в конце и ссылку на объект даты. Указатель считается действительным!

Функция проверяет, находится ли строка в определенном формате (%, затем 8 цифр и \ 0). Если это не так, возвращается false. Эта проверка (большое if) обращается к указателю без каких-либо проверок достоверности. Длина не проверяется, и если указатель указывает где-то в дикой природе, доступ к этому пространству. Я не знаю, если более короткая строка вызовет проблемы. Это не должно происходить из-за способа оценки &&.

Затем часть памяти выделяется в стеке. Числовая часть строки копируется в нее (что нормально), и буфер получает завершение \ 0. Атоис извлекает числа. Это будет работать из-за различных начальных местоположений и \ 0-окончания после каждой части. Как-то сложно, но приятно. Некоторые комментарии прояснили бы все.

Эти числа затем вставляются в объект. Он должен быть действительным, поскольку он передается в функцию по ссылке. Я не знаю, можете ли вы передать ссылку на удаленный объект, но если это так, то это может быть и вашей проблемой.

В любом случае - кроме отсутствующей проверки на указатель на строку, эта функция исправна и не является причиной вашей проблемы. Это только место, которое бросает исключение. Поиск аргументов, которые передаются в эту функцию. Они всегда действительны? Пройдите регистрацию.

Надеюсь, я не допустил серьезных ошибок, так как я программист на Delphi. Если я это сделал - не стесняйтесь комментировать.

...