вот к примеру у меня упала прога.
(ну допустим обратились вне адресного пространства)
мне нужно получить call stack того места где это произошло.
при этом это release версия приложения. (и мы не в debug'e сидим)
как это реализовать?
вариант наставить в каждой функции exception не подходит.
Здравствуйте, betauser, Вы писали:
B>мне нужно получить call stack того места где это произошло. B>при этом это release версия приложения. (и мы не в debug'e сидим)
Там функция раскрутки стека по структуре CONTEXT, описывающей некоторую точку выполняемого кода. Когда вылетает SEH-исключения (типа того же access violation) можно получить CONTEXT точки, где оно вылетело (см. в MSDN GetExceptionЧегоТоТам), а потом натравить на эту структуру функцию ShowStack, которая выведет данные на стандартный выход. Не проверял, но полагаю, что даже в release для стабильной работы надо отключить оптимизацию стекового фрейма (/Oy-). Чтобы вместо адресов видны были символические имена функций, надо так же скомпилить release с дебажной базой данных (в отдельных pdb-файлах, конечно, они размер не увеличивают).
Hello betauser,
b> вот к примеру у меня упала прога. b> (ну допустим обратились вне адресного пространства) b> мне нужно получить call stack того места где это произошло. b> при этом это release версия приложения. (и мы не в debug'e сидим) b> как это реализовать?
Для релиза мало без опции stack frame?
hаскрутить стек будет затруднительно.
Учитывая, что алгоритм call stack такой.
a> Для релиза мало без опции stack frame?
Имелось в виду что при компиляции должна происходить
генерация кадра стэка.
По умолчанию для MSVC++ это не делается.
Сильно это прогу не замедлит.
Здравствуйте, betauser, Вы писали:
B>вот к примеру у меня упала прога. B>(ну допустим обратились вне адресного пространства) B>мне нужно получить call stack того места где это произошло. B>при этом это release версия приложения. (и мы не в debug'e сидим) B>как это реализовать?
B>вариант наставить в каждой функции exception не подходит.
Вот что мы в своих проектах используем.
// xxstackguard.h#ifndef XX_STACK_GUARD_H
#define XX_STACK_GUARD_H
#include <exception>
#ifdef STACKGUARG_EXPORTS
#define XX_PUBLIC XX_EXPORT
#else
#define XX_PUBLIC XX_IMPORT
#endif// forward declarationclass IxxStackGuardManager;
EXTERN_C XX_PUBLIC IxxStackGuardManager * xxGetStackGuardManager();
class IxxStackGuardManager
{
public:
virtual void Push(const char * lpszReport) = 0;
virtual void Clear() = 0;
virtual const char * Report() = 0;
};
class CxxStackGuard
{
const char* m_lpszFunction;
public:
inline CxxStackGuard(const char* lpszFunction)
: m_lpszFunction(lpszFunction)
{
}
inline ~CxxStackGuard()
{
#if (_MSC_VER >= 1300)
#ifdef _STLPORT_VERSION
if (__uncaught_exception())
#else
if (std::uncaught_exception())
#endif
#else
#error uncaught_exception is not supported
#endif
{
IxxStackGuardManager * pManager = xxGetStackGuardManager();
pManager->Push(m_lpszFunction);
}
}
};
////////////////////////////////////////////////////////////////////////////
// несколько вспомогательных макросов.#define XX_STACK_GUARD\
CxxStackGuard __xxStackGuard__FUNCTION__);\
#define XX_STACK_GUARD_BLOCK(x)\
CxxStackGuard __xxStackGuardBlock(#x);\
////////////////////////////////////////////////////////////////////////////
// пытались сформировать имя переменной на базе номера строки, но это работает
// только при определенных настройках проекта (VC7.x)#define XX_CONCATNAME__(a, b) a##b
#define XX_CONCATNAME_(a, b) XX_CONCATNAME__(a, b)
#define XX_CONCATNAME(a) XX_CONCATNAME_(a, __LINE__)
//#define XX_STACK_GUARD_BLOCK(x)\
// CxxStackGuard XX_CONCATNAME(x);#undef XX_PUBLIC
#endif
// xxstackguard.cpp#include"dbg/xxstackguard.h"#include <vector>
#include <string>
#define new XX_DEBUG_NEW
class CxxStackGuardManager : public IxxStackGuardManager
{
typedef std::vector <const char *> _Stack;
public:
CxxStackGuardManager()
{
}
virtual ~CxxStackGuardManager()
{
m_sReport.clear();
m_stack.clear();
}
virtual void Push(const char * lpszReport)
{
m_stack.push_back(lpszReport);
}
virtual void Clear()
{
m_stack.clear();
m_sReport.clear;
}
virtual const char * Report()
{
std::string space = "\r\n\t";
m_sReport = "Stack guard:";
for (_Stack::iterator iter = m_stack.begin();
iter != m_stack.end(); ++ iter)
m_sReport += space + std::string(*(iter));
return m_sReport.c_str();
}
protected:
_Stack m_stack;
std::string m_sReport;
};
EXTERN_C XX_EXPORT IxxStackGuardManager * xxGetStackGuardManager()
{
#ifndef _MT
static CxxStackGuardManager stackManager;
return &stackManager;
#else// Много поточный режим, пока, не реализован,
// но его легко реализовать либо через <mep> либо через Tls
// Эта функция вызывается в случае падения программы, поэтому
// скорость здесь не критична. #error !!!
#endif
}
У этого метода есть один недостаток: иногда в отчет не попадает функция, в которой произошел бабах (f3).
(в данном случае в дебаге отчет __должен__ быть такой — "f3 — f2 — f1 — main" а в релизе __может__
быть такой — "f2 — f1 — main").
как правило, это происходит, если функция f3 очень простая (до оптимизировались).
Но несмотря на это он в разы уменьшает время локализации и устранения ошибки (ошибок).
kmn wrote:
> Здравствуйте, betauser, Вы писали: > > B>вот к примеру у меня упала прога. > B>(ну допустим обратились вне адресного пространства) > B>мне нужно получить call stack того места где это произошло. > B>при этом это release версия приложения. (и мы не в debug'e сидим) > B>как это реализовать? > > B>вариант наставить в каждой функции exception не подходит. > > Вот что мы в своих проектах используем.
I've also provided a nifty class (WheatyExceptionReport) that can be easily dropped into C++-based projects to provide a detailed crash report, including the names and values of all local and global variables at the time of a program crash.
Здравствуйте, betauser, Вы писали:
B>вот к примеру у меня упала прога. B>(ну допустим обратились вне адресного пространства) B>мне нужно получить call stack того места где это произошло. B>при этом это release версия приложения. (и мы не в debug'e сидим) B>как это реализовать?
B>вариант наставить в каждой функции exception не подходит.
чтобы MiniDumpWriteDump использовать ставлю exception handler на App и когда падаем пишу дамп из хэндлера. Потом через WinDbg нахожу что упало. Значительно помогает генерация PDB файлов.
ME>I've also provided a nifty class (WheatyExceptionReport) that can be easily dropped into C++-based projects to provide a detailed crash report, including the names and values of all local and global variables at the time of a program crash.
Это все отлично, но есть ли какието тулзы для анализа полученных отчетов и просмотра в удобочитаемов виде?
Вот приходят к нам пачками эти отчеты у кого что где упало, а хотелось бы какоето средство, чтобы техсуппорт мог бы сразу видеть в чем ошибка, не листая кипу файлов.
Здравствуйте, pavel_turbin, Вы писали:
_>чтобы MiniDumpWriteDump использовать ставлю exception handler на App и когда падаем пишу дамп из хэндлера. Потом через WinDbg нахожу что упало.
_>Значительно помогает генерация PDB файлов.
Не могли бы Вы немного подробнее рассказать. Я сделал именно так, как Вы описали.
Тестеру передается релиз версия без дебаговой информации. С моей стороны у меня есть PDB-файл. Когда у тестера случается крэш, он шлет мне минидамп (DMP). Я открываю его в WinDbg.
Проблема: как сделать так, чтобы WinDbg воспринимал мой PDB файл и показывал его символы? Вообще, какая должна быть последовательность действий в WinDbg, чтобы узнать в какой из моих функций "упало"?
А есть ли такое решение, чтобы написать небольшой файл с содержимым стека и md5 exe-файла+указанных мною модулей (или даже адресами функций после разбора фреймов) *без* *помощи* DbgHelp.dll. А я уж имея PDB разберу этот файл, а по md5 определю версию модулей (это чтобы не связываться с версиями).
I>Не могли бы Вы немного подробнее рассказать. Я сделал именно так, как Вы описали.
1. имеем DMP file and PDB
2. запускаем windbg
3. сначала окрываем dmp: menu file -->Open Crash dump, выбираем нащ дамп.
3. устанавливаем символы: menu file -->"Symbols file path". Здесь в окне вводим путь в Вашему PDB и отмечаем reload checkbox. Жмем OK.
4. в коммандной строке главного окна вводим команду
!analyze -v
она выдаст стек и прочие радости.
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
..... сдесь будет стек. Если PDB не найден, то стек будеть корявый.
windbg имеет хорщий help. Можно начать с чтения "Analyzing a User-Mode Dump File"
_>1. имеем DMP file and PDB
_>2. запускаем windbg _>3. сначала окрываем dmp: menu file -->Open Crash dump, выбираем нащ дамп. _>3. устанавливаем символы: menu file -->"Symbols file path". Здесь в окне вводим путь в Вашему PDB и отмечаем reload checkbox. Жмем OK. _>4. в коммандной строке главного окна вводим команду _>!analyze -v _>она выдаст стек и прочие радости. _>******************************************************************************* _>* * _>* Exception Analysis * _>* * _>*******************************************************************************
_>..... сдесь будет стек. Если PDB не найден, то стек будеть корявый.
Пробую делать так как здесь сказанно, но не нахожу папки с MiniDump в папке Windows. Проверял в настройках винды — указано создавать малый дамп в %SystemRoot%\Minidump. Но папки такой здесь нет. Использую Windows XP sp1.