Может, кому пригодится - немножко опыта про google breakpad. Не удивляйтесь, если покажется, что что-то слишком усложнено, неправильно, через одно место или вообще вредно, все нижеследующее, включая, но не ограничиваясь, примеры кода - привожу не как инструмент, а как путь движения к оному. Пердупердил
0. Окружение:
lin, mac - gcc
win - vc (nmake, cl)
Про мак отмечу, что не особо вникал, да и давно это было - могу чего приврать, так что ориентируемся на lin - с большой вероятностью то же самое.
Сборка самого брейкпада под все платформы тема отдельная, но достаточно разжевана, проблем быть не должно. Из нюансов (не "вообще", а в контексте данного поста) - с целевым приложением под лин линкуется статически (libbreakpad_client.a), под вин - исходники включались в проект.
1. Собираем нужные нашему приложению фреймфорки, цель - релиз с дебаг инфой; (-g для gcc, про студию не скажу; должно быть достаточно в прошнике добавить в QMAKE_LFLAGS/QMAKE_CXXFLAGS QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO/QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO, но, возможно, придется поправить свой мейкспек - $qtdir$/mkspecs).
2. Добавляем в наше приложение брейкпад (BREAKPAD_SRC_DIR - кастомная переменная окружения):
2.1 В файле проекта указываем необходимые пути/флаги:
Bash
#ourapp.pro
...
CONFIG(debug, debug|release) {
...
} else {
...
QMAKE_LFLAGS += $$QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO
...
}
INCLUDEPATH += $$(BREAKPAD_SRC_DIR)
unix:LIBS += $$(BREAKPAD_SRC_DIR)/client/linux/libbreakpad_client.a
win32{
INCLUDEPATH += $$(BREAKPAD_SRC_DIR)/client/windows
SOURCES += \
$$(BREAKPAD_SRC_DIR)/common/windows/guid_string.cc \
$$(BREAKPAD_SRC_DIR)/client/windows/handler/exception_handler.cc \
$$(BREAKPAD_SRC_DIR)/client/windows/crash_generation/client_info.cc \
$$(BREAKPAD_SRC_DIR)/client/windows/crash_generation/crash_generation_client.cc \
$$(BREAKPAD_SRC_DIR)/client/windows/crash_generation/crash_generation_server.cc \
$$(BREAKPAD_SRC_DIR)/client/windows/crash_generation/minidump_generator.cc \
}
...
2.2 Добавляем брейкпадовский обработчик крешей. Обычно это делается в main:
C++ (Qt)
//-- ourapp/main.cpp
...
#define HAS_CRASHHANDLER
#if defined(Q_OS_LINUX)
#include "client/linux/handler/exception_handler.h"
#elif defined(Q_OS_WIN32)
#include "client/windows/handler/exception_handler.h"
#elif defined(Q_OS_MAC)
#include "client/mac/handler/exception_handler.h"
#endif
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
#ifdef HAS_CRASHHANDLER
const QString absPathForDump = ...
#if defined(Q_OS_LINUX)
google_breakpad::ExceptionHandler eh
(
absPathForDump.toLocal8Bit().data(),
NULL,
NULL,
NULL,
true
);
#elif defined(Q_OS_WIN32)
using namespace google_breakpad;
ExceptionHandler *handler = new ExceptionHandler
(
absPathForDump.toStdWString(),
NULL,
NULL,
NULL,
ExceptionHandler::HANDLER_ALL
);
Q_UNUSED(handler);
#elif defined(Q_OS_MAC)
#warning think, блядь, different!
#endif
#endif
return a.exec();
}
Примечание-1: Приведенный пример основан на довольно древней ревизии брейкпада; если я ничего не путаю, в свежих они унифицировали интерфейс и хендлер теперь ставится одинаково для всех платформ - не надо возится #elif defined(Q_OS_*).
Примечание-2: Вроде очевидно, но на всяк случай - путь сохранения дампов выбирайте таким, чтобы у пользователя был доступ на запись. Если складывать возле бинарника - могут возникнуть проблемы (c:\Program files, etc).
3. Собираем само приложение (релиз с дебаг инфой).
4. На получившиеся бинарники (*.pdb под вин, *.so и *.executable) натравливаем dump_syms из брейкпада. Для примера - имя файла libQtGui.so.4.8.2. На выходе получаем libQtGui.so.4.8.2.sym;
5. Теперь нужно сформировать дерево каталогов с файлами символов в соответствии с ожиданиями брейкпада. В полученном файле .sym первая строка примерно такая:
MODULE Linux x86_64 842F9DC91DD2ECBAD5B1867842FF52F30 libQtGui.so.4.8.2
"842F9DC91DD2ECBAD5B1867842FF52F30" - это хеш модуля, по которому брейкпад будет искать доп.инфу при разворачивании дампа; получившийся libQtGui.so.4.8.2.sym должен лежать в директории с этим именем. Т.е. паттерн именования - .../
binary_name/
binary_sym_hash/
binary.sym:
/path/2/symbols/storage/
libQtGui.so.4.8.2/
842F9DC91DD2ECBAD5B1867842FF52F30/
libQtGui.so.4.8.2.sym(В необязательную часть можно добавить версию приложения, целевую платформу и пр. вкусности, которые помогут организовать удобное хранение на серваке, но не будем об этом).
6. strip'аем бинарники, если все проделывалось под не-виндовз (под виндовз дебаг инфа в пдб, они не деплоятся); Собираем инсталяционный пакет и пр., чего там нам надо.
На этом, в общем-то, все - теперь если приложение упадет, будет сгенерирован файл дампа (GUID.dmp), который раскручивается так:
Bash
$BREAKPAD_SRC_DIR/src/processor/minidump_stackwalk -m /path/2/GUID.dmp /path/2/symbols/storage > /path/2/readable.log
Бесплатный бонус - зоопарк операционок нужен только на этапе сборке, после этого все можно делать на одной - файл дампа, сгенерированный под вин, можно раскрутить под лин, если на нем доступны виндовые *.sym; соответсвенно и в обратную сторону, и мак.
7. Уже не про сам брейкпад, а про организацию процесса.
7.1 Генерацию файлов символов можно автоматизировать:
Bash
#!/bin/bash
WIN_32=win_x32
WIN_64=win_x64
LIN_32=lin_x32
LIN_64=lin_x64
MAC_64=mac_x64
export DUMP_SYMS_EXE=dump_syms
PATH_ON_SERV=/var/www/dbg_symbs_storage/on/our/http
#for paths corrections:
doubleSlash=//
singleSlash=/
if [ x"$1" == x"" ]; then
echo "You must specify directory with build"
exit 1
fi
BUILD_DIR=$1
if [ ! -d $BUILD_DIR ]; then
echo "$BUILD_DIR is not a directory"
exit 2
fi
PLATFORM=$2
PDB_SUFFIX=""
case "$PLATFORM" in
$WIN_32)
echo one
FILES=`find $BUILD_DIR -name "*.pdb"`
PDB_SUFFIX=".pdb"
;;
$WIN_64)
FILES=`find $BUILD_DIR -name "*.pdb"`
PDB_SUFFIX=".pdb"
;;
*)
FILES=$(find $BUILD_DIR -type f)
;;
esac
function generateDbgSymbols {
IFS=$'\n'
for f in $FILES
do
if [ $PLATFORM == $WIN_32 ] || [ $PLATFORM == $WIN_64 ] || file "$f"|grep -Eq 'Mach-O|ELF' ; then
OUTNAME="";
OUTNAME=`dirname $f`/`basename $f "$PDB_SUFFIX" `.sym
OUTNAME=${OUTNAME/$doubleSlash/$singleSlash}
if echo $PLATFORM | grep -i "win" > /dev/null; then
f=`cygpath -w $f`
fi
if [ -f $f ] && [ -n OUTNAME ] ; then
echo generating sym file $OUTNAME from $f
$DUMP_SYMS_EXE $f > $OUTNAME
fi
OUTNAME="";
fi
done
unset IFS
}
# Clear old syms dir if exists
SYMBOLS_DIR=$BUILD_DIR/symbols
SYMBOLS_DIR=${SYMBOLS_DIR/$doubleSlash/$singleSlash}
test -d "$SYMBOLS_DIR" && rm -rf $SYMBOLS_DIR
mkdir -p $SYMBOLS_DIR
# Generating sym
generateDbgSymbols
echo builddir-2 is $BUILD_DIR
OIFS="$IFS"
IFS=$'\n'
for SYM_FILE in $(find $BUILD_DIR -name "*.sym" ); do
SYM_FILE=${SYM_FILE/$doubleSlash/$singleSlash}
echo "Processing $SYM_FILE"
MODULE_NAME=`basename $SYM_FILE`$PDB_SUFFIX
MODULE_NAME=${MODULE_NAME/.sym/}
MODULE_CHECKSUM=$(head -n1 $SYM_FILE | awk '{print $4}')
echo "Module name: $MODULE_NAME"
echo "Module checksum: $MODULE_CHECKSUM"
MODULE_SYMBOLS_DIR=$SYMBOLS_DIR/1/$MODULE_NAME/$MODULE_CHECKSUM
mkdir -p $MODULE_SYMBOLS_DIR
echo "module symbols dir:" $MODULE_SYMBOLS_DIR
cp $SYM_FILE $MODULE_SYMBOLS_DIR
done
unset IFS
echo "Symbol directory $SYMBOLS_DIR ready. Copying it to server"
convertedPath=$SYMBOLS_DIR/1
if echo $PLATFORM | grep -i "win" > /dev/null; then
convertedPath=`cygpath -u $SYMBOLS_DIR/1`
fi
scp -r $convertedPath user@our/http:$PATH_ON_SERV/$PLATFORM
if [ "${?}" == "0" ] ; then
echo Symbols copyed successfuly.
else
echo Symbols copying failed.
fi
rm -rf $SYMBOLS_DIR
exit 0
7.2 Чтобы пользователей не просить "А гляньте плиз, в кишках хомятника, в папочке с названием приложения, нет ли труднопроизносимого файла с расширением дээмпэ?" и не объяснять, что такое хомятник и расширение, дампы нужно загружать автоматически. Для этого при каждом старте проверяется наличие, если есть - загружаем к себе на сервак, у пользователя стираем. На серваке раскручиваем, заводим тикет в системе багтрекинга, если можем - определяем пользователя и шлем ему письмо про "не расстраивайтесь, сбой - это, конечно, плохо, но мы уже работаем над этим, подпишитесь на наш спам", рассылаем нотификации заинтересованным и вообще делаем, что там нам может понадобиться. Ну а реализация всего этого - к брейкпаду имеет мало отношения, так что на свой вкус.
Вроде есть интерфейс для серверного обработчика, но сам с этим не работал, так что - ищем самостоятельно.
При необходимости примеров толкового использования в билдрутине можно погуглить, как это сделано в файрфоксе.