Пишу для Windows на VC++, использую следующую функцию для печати стека вызовов void PrintCallStack( int __ebp ) { int *_ebp = (int*) (__int64) __ebp; if ( _ebp == 0 ) { __asm { mov _ebp, ebp } } LogFile << "----------------" << endl; for (; { if ( IsBadReadPtr( _ebp, sizeof(int*) ) ) break; if ( IsBadReadPtr( _ebp + 1, sizeof(int*) ) ) break; int* call = *(int**) ( _ebp + 1 ); LogFile << "0x" << call << endl; _ebp = *(int**) _ebp; } LogFile << "----------------" << endl; } PrintCallStack( 0 ) - печать текущего стека PrintCallStack( __ebp ) - использую для печати стека с нужного места (например, в фильтре исключений печатаю стек с места возникновения исключения). Просьба к опытным людям высказать свое мнение. Может быть можно что-то улучшить? Спасибо.
Clerk т.к. это Си, а не асм, то не возможно это утверждение не достоверно т.к. это зависит как от версии компилятора, так и от используемых ключей и т.д и т.п. к тому же если использовать классы, то там вообще произвол и ebp может спокойно использоваться как this (если я не ошибаюсь именно VC++ мною за подобным замечался)
Печатаемые стеки правильны, за исключением иногда пропускаемых уровней. Например точка возврата main() в __tmainCRTStartup() может пропечататься, а может и нет. Есть предложения по улучшению?
Тут есть один нюанс: если тебе нужно тупо стек весь, то да, так как ты делаешь -- ок, но полезности в этом почти нет т.к. файл, если я правильно понимаю будет просматриваться после того, как программа отработает, что тебе дадут адреса-цифры, если ты уже не сможешь восстановить базы всех длл? если даже у тебя есть базы всех длл, тебе все равно придется руками восстанавливать стек вызовов, что является занятием не самым приятным и интересным, это по опыту =) dbghelp дает возможность мало того, что напечатать только стек вызовов, так еще и напечатать не только адреса, а и названия функций, при наличии символов(а вот без них -- гораздо хуже =) ), (кстати самому подобный функционал реализовать совсем не так просто, как может казаться) а они же есть, если приложение твое. Ну а про тащить библиотеку -- вообе непонятно, тебе жалко что ли, или какие-то очень жесткие требования к потребляемым ресурсам =) ?
и ещё. кодес твой не будет работать под x64, что в данное время довольно ущербно. самое неприятное, конечно, то, что не печатает имена функций. если ты действительно пишешь на Си++, то должен понимать насколько бесполезна информация об адресах стека вызовов.
Программа работает у клиента, никакой отладочной информация нет, стек вызова в логе нужен при анализе сбоев. Код (Text): файл, если я правильно понимаю будет просматриваться после того, как программа отработает Правильно. Код (Text): не сможешь восстановить базы всех длл Системные dll обычно грузятся на одинаковые адреса, само приложение тоже. Наибольший интерес представляет код в exe. Код (Text): придется руками восстанавливать стек вызовов Будет очень легко определить иерархию вызовов. Код (Text): Ну а про тащить библиотеку -- вообе непонятно, тебе жалко что ли, или какие-то очень жесткие требования к потребляемым ресурсам =) ? Жестких требований нет. Однако без символов польза от dbghelp.dll не больше чем от моей функции (имхо). Так что лучше - грузить dll или вписать 20 строк в код? Код (Text): кодес твой не будет работать под x64 Я в этом пока не разбирался. Наверное код можно переписать для x64?
Velheart Я это упустил, спасибо. Дамп тоже буду делать. Вот и польза от дискуссии получилась. Размер не имеет значения. Загружаете проект и смотрите, что оказалось по указанному адресу. Или вы что-то другое имеете ввиду?
я имею ввиду как по адресам узнать имена функций? если у меня в стеке 50 вызовов, а там ещё рекурсия и где-то посреди рекурсии залез косяк - что толку от одних адресов?
Я вижу в отладчике инструкции одновременно со своим кодом. Код (Text): case WM_SHOWWINDOW: { gui.SetWin( hwnd ); 00411BDD push ebx 00411BDE mov esi,offset gui (466E40h) 00411BE3 call GUI_CLASS::SetWin (4031A0h) gui.PrintLastError( "Нет ошибок" ); 00411BE8 mov edi,offset string "\xcd\xe5\xf2 \xee\xf8\xe8\xe1\xee\xea" (421F44h) 00411BED mov ecx,esi 00411BEF call GUI_CLASS::PrintLastError (403390h)