Comments 42
Спасибо — в закладки! Интересно, а в mingw есть какие-нибудь механизмы для поддержания syslog || dtrace(systemtap)?
в reportTrouble надо i объявить.
Большое спасибо, честно говоря. Среди себя поднимал вопрос давным давно, сравнивая отлаживаемость Java с голыми С. И к тому же backtrace приходил, но сильно далеко не убежал.
А ваш код прямо сейчас скопипастил, спасибо :-)
Большое спасибо, честно говоря. Среди себя поднимал вопрос давным давно, сравнивая отлаживаемость Java с голыми С. И к тому же backtrace приходил, но сильно далеко не убежал.
А ваш код прямо сейчас скопипастил, спасибо :-)
А откуда backtrace возьмет имена функций на стрипнутом экзешнеке?
Как минимум, по классам он всё показывает:
Чаще всего в плюсовой программе именно это и требуется.
0 segfault 0x0000000102b1a8c8 segfault + 2248
1 segfault 0x0000000102b1a9d0 segfault + 2512
2 libsystem_c.dylib 0x00007fff99b5dcfa _sigtramp + 26
3 ??? 0x0000040000000000 0x0 + 4398046511104
4 segfault 0x0000000102b1ac67 _ZN7Crasher11doSomethingEv + 71
5 segfault 0x0000000102b1ac1a _ZN7Crasher18doSomethingPrivateEv + 208
6 segfault 0x0000000102b1ac67 _ZN7Crasher11doSomethingEv + 71
7 segfault 0x0000000102b1a880 segfault + 2176
8 segfault 0x0000000102b1aac4 segfault + 2756
9 segfault 0x0000000102b1ab33 segfault + 2867
10 segfault 0x0000000102b1a854 segfault + 2132
Чаще всего в плюсовой программе именно это и требуется.
А после того, как над экзешником поработал strip?
Это и есть после стрипа:
$ strip segfault
$ ./segfault
That's a function!
That's a private function! myPrivateInteger == 1752392050
myPrivateDoubles[1] == 60993401604041306737928347282702617388988841504491171140800281285302442927306116721201046092641903128620672849302937378251940003901836219046866981678295779355600933772275817062376375849852470059862498765690530537583237171035779906888043337758015488.000000
myPrivateString == 0x63202c6f6c6c6548
That's a function!
0 segfault 0x0000000102b1a8c8 segfault + 2248
1 segfault 0x0000000102b1a9d0 segfault + 2512
2 libsystem_c.dylib 0x00007fff99b5dcfa _sigtramp + 26
3 ??? 0x0000040000000000 0x0 + 4398046511104
4 segfault 0x0000000102b1ac67 _ZN7Crasher11doSomethingEv + 71
5 segfault 0x0000000102b1ac1a _ZN7Crasher18doSomethingPrivateEv + 208
6 segfault 0x0000000102b1ac67 _ZN7Crasher11doSomethingEv + 71
7 segfault 0x0000000102b1a880 segfault + 2176
8 segfault 0x0000000102b1aac4 segfault + 2756
9 segfault 0x0000000102b1ab33 segfault + 2867
10 segfault 0x0000000102b1a854 segfault + 2132
Впишите в начало main.cpp
и повторите эксперимент.
У вас по-умолчанию все символы доступны для динамической загрузки с помощью dladdr.
pragma GCC visibility push(hidden)
и повторите эксперимент.
У вас по-умолчанию все символы доступны для динамической загрузки с помощью dladdr.
Результат аналогичный указанному в комменте выше: все символы скрыты, кроме методов класса.
diff --git a/main.cpp b/main.cpp
index 9f89031..1e8ba47 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3,6 +3,8 @@
#include <signal.h>
#include <execinfo.h>
+#pragma GCC visibility push(hidden)
+
#define P_DOUBLE_COUNT 10000
// if set to 1, cat is used instead of curl'ing file to server
--
Результат (после стрипа):
0 segfault 0x0000000100000a7c 0x0 + 4294969980
1 segfault 0x0000000100000b19 0x0 + 4294970137
2 libSystem.B.dylib 0x00007fff831fd1ba _sigtramp + 26
3 ??? 0x3838383630393937 0x0 + 4051049670208207159
4 segfault 0x0000000100000c9b 0x0 + 4294970523
5 segfault 0x0000000100000c61 0x0 + 4294970465
6 segfault 0x0000000100000c9b 0x0 + 4294970523
7 segfault 0x0000000100000a59 0x0 + 4294969945
8 segfault 0x0000000100000b99 0x0 + 4294970265
9 segfault 0x0000000100000bd5 0x0 + 4294970325
10 segfault 0x0000000100000a34 0x0 + 4294969908
11 ??? 0x0000000000000001 0x0 + 1
Да, действительно, прошу прощения за допущенную ошибку — забыл собрать проект после вставки прагмы.
Кстати, не знал о такой. Часто ли используется в реальных проектах?
Кстати, не знал о такой. Часто ли используется в реальных проектах?
Обычно используется linker script чтобы что попало не экспортировалось.
Что становится нетривиальной задачей в случае C++.
Есть проблемы с explicitly instantiated templates, но такие вещи почти никогда не экспортируются. Всё остальное нормально работает.
Ну вот хотим мы класс экспортировать, не дай бог в неймспейсе, с кучей операторов и перегруженных функций. А еще хорошо бы экспортировать vtable и typeinfo от этого класса. Руками я это прописывать в линкер-скрипте не решусь.
Может мы о разных вещах говорим. Я имею ввиду ld --version-script=version-scriptfile который вполне успешно понимает C++ неймспейсы и классы.
Например экспорт всего класса Bar из namespace Foo, кроме символов начинающихся с private_stuff.
Например экспорт всего класса Bar из namespace Foo, кроме символов начинающихся с private_stuff.
LIBNAME_123 {
global:
# C++ functions
extern "C++" {
Foo::Bar::*;
};
# C functions
cfoo; cbar;
local: *;
extern "C++" {
Foo::Bar::private_stuff*;
};
};
Не далее чем на прошедшей неделе обнаружил в составе Mac OS X 10.7.4 модуль на C++ с той же самой проблемой — экспортируются абсолютно все C++ символы. При чем речь идет о модуле, исходники которого не доступны.
С символами код элементарно реверсится по дизасму (например всякие STL-ные вещи сразу же отбрасываются и время на анализ их кода не тратится).
Если вы комерческий вендор, и ваши исходные тексты закрыты, врядли вы захотите настолько облегчить реверсерам жизнь.
С символами код элементарно реверсится по дизасму (например всякие STL-ные вещи сразу же отбрасываются и время на анализ их кода не тратится).
Если вы комерческий вендор, и ваши исходные тексты закрыты, врядли вы захотите настолько облегчить реверсерам жизнь.
fopen, насколько я помню, работает с динамической памятью. Я бы не стал такое использовать в обработчике SIGSEGV — ведь сам краш мог произойти из-за расстрела или нехватки памяти.
А что тогда использовать?
Интересуюсь с целью улучшить свою реализацию.
Интересуюсь с целью улучшить свою реализацию.
в своем коде — переменные на стеке и статически аллоцированные. для общения с системой — open()/read()/write() и другие API которые заведомо не используют динамическую память
Спасибо, поэкспериментирую!
Поглядите в статью Практика работы с сигналами, в особенности в комментарии. На обработчики сигнала наложены жесточайшие ограничения :)
НО! В случае полного краха приложения, уж лучше что то (шанс что весь ваш обработчик отработаете и вы получите репорт) чем ничего (вы даже не пытаетесь его отправит, потому что следуете всем правилам написания обработчика сигналов)
НО! В случае полного краха приложения, уж лучше что то (шанс что весь ваш обработчик отработаете и вы получите репорт) чем ничего (вы даже не пытаетесь его отправит, потому что следуете всем правилам написания обработчика сигналов)
А еще backtrace_symbols_fd, чтобы писать сразу в файл.
ЕМНИП, stdio работает поверх выделенных заранее буферов. Но вопрос юзания сложных функций из обработчиков сигнала — конечно, тоже занятен :-)
Для создания краш-репортов можно использовать google-breakpad, разработанный специально для этих целей. К тому же он кросс-платформенный.
Еще бы деманглинг добавить.
Я иногда делаю так
Где функция
Этот код я честно слямзил не помню где много лет назад, ну и обрезал по максимуму под свои нужды.
В демоне — очень даже помогает, тут и деманглинг и прочее, и 3rd party код показывает с именами и номерами строк. cmdFile содержит список нужных мне команд, в общем случае там bt + quit.
Главное — strip не увлекаться ;-)
static int inSigSegvHandler = 0;
static void sigsegvHandler(int sig) {
++inSigSegvHandler;
if(inSigSegvHandler > 1) { // защита от двойного попадания, сигнал ставлю sigaction + SA_RESTART
fprintf(stderr, "*** SIG %d TWICE ***\n", sig);
_exit(2);
}
invokeDebuggerToSelf();
_exit(1);
}
Где функция
void invokeDebuggerToSelf() {
int pid = getpid();
char link[MAX_PATH];
char buf[MAX_PATH];
char bufexe[MAX_PATH];
char pidbuf[16];
char cmdline[MAX_PATH];
memset(bufexe, 0, sizeof(bufexe));
memset(link, 0, sizeof(link));
sprintf(link, "/proc/%d/exe", pid);
if(readlink(link, bufexe, MAX_PATH) <=0)
strcpy(bufexe, "где-там-оно-лежит"); // вдруг звезды сойдутся неудачно
sprintf(pidbuf, "%d", pid);
int wpid;
if((wpid=fork()) == 0) {
execlp("gdb", "gdb", "-batch", "-q", "-x", gdbFile, bufexe, pidbuf, NULL);
}
else if(wpid > 0) {
int status;
waitpid(wpid, &status, WUNTRACED);
_exit(0);
}
}
Этот код я честно слямзил не помню где много лет назад, ну и обрезал по максимуму под свои нужды.
В демоне — очень даже помогает, тут и деманглинг и прочее, и 3rd party код показывает с именами и номерами строк. cmdFile содержит список нужных мне команд, в общем случае там bt + quit.
Главное — strip не увлекаться ;-)
UFO just landed and posted this here
О как, спасибо, буду знать! И попробую в проекте заимплементить.
В винде можно не просто подойти к этой проблеме, а заюзать DbgHelp API. Если собирать проект студией, то она создает приличный по объему файл с символами *.pdb, который содержит символы и их привязки к релизному коду. Сам релизный код символов не содержит (кроме экспортных). При краше мы используем функцию MiniDumpWriteDump. Имея *.pdb файл символов, исходники и релизный образ у себя на машине открываем в студии дамп и получаем интерфейс отладчика, с состоянием, как будто бы крэш случился только что на нашей машине под отладчиком. Помимо стек-трейса, можно просмотреть значения локальных переменных, потоки и пр. инфу. Если использовать флаг MiniDumpWithFullMemory, то (судя по справке) вообще всю user-mode память процесса можно получить.
Все уже придумано:
code.google.com/p/google-breakpad/
Причем если все аккуратно настроить — буде отлично работать на релизной версии без символов
code.google.com/p/google-breakpad/
Причем если все аккуратно настроить — буде отлично работать на релизной версии без символов
Крэша в случае вызова метода через nullptr не будет только если метод невиртуальный, если виртуальный — тоже будет крэш. Ну это так, дополнение.
Sign up to leave a comment.
Краш-репорты в *nix: backtrace, SEGFAULT (и reinterpret_cast)