From: Martin Pitt Date: Tue, 22 Dec 2009 10:09:20 +0000 (+0100) Subject: Support storing assertion messages into core dump X-Git-Tag: 2.23.2~67 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=da66897950431870390f8dc3f798e24f23ffb8c8;p=platform%2Fupstream%2Fglib.git Support storing assertion messages into core dump Crash interception/debugging systems like Apport or ABRT capture core dumps for later crash analysis. However, if a program exits with an assertion failure, the core dump is not useful since the assertion message is only printed to stderr. glibc recently got a patch which stores the message of assert() into the __abort_msg global variable. (http://sourceware.org/git/?p=glibc.git;a=commitdiff;h=48dcd0ba) That works fine for programs which actually use the standard C assert() macro. This patch adds the same functionality for glib's assertion tests. If we are building against a glibc which already has __abort_msg (2.11 and later, or backported above git commit), use that, otherwise put it into our own field __glib_assert_msg. Usage: $ cat test.c #include int main() { g_assert(1 < 0); return 0; } $ ./test **ERROR:test.c:5:main: assertion failed: (1 < 0) Aborted (Core dumped) $ gdb --batch --ex 'print (char*) __abort_msg' ./test core [...] $1 = 0x93bf028 "ERROR:test.c:5:main: assertion failed: (1 < 0)" https://bugzilla.gnome.org/show_bug.cgi?id=594872 --- diff --git a/configure.in b/configure.in index faecaa6a8..edc4142d6 100644 --- a/configure.in +++ b/configure.in @@ -2676,6 +2676,20 @@ int error = EILSEQ; ], have_eilseq=yes, have_eilseq=no); AC_MSG_RESULT($have_eilseq) +dnl ********************************************************************** +dnl *** Check whether glibc has global variable for assertion messages *** +dnl ********************************************************************** + +AC_MSG_CHECKING(if libc has __abort_msg) +AC_LINK_IFELSE([ +extern char *__abort_msg; +int main() { return __abort_msg == (char*) 0; } +], [libc_has_abort_msg=yes], [libc_has_abort_msg=no]) +AC_MSG_RESULT($libc_has_abort_msg) +if test "$libc_has_abort_msg" = "yes"; then + AC_DEFINE(HAVE_LIBC_ABORT_MSG,1,[Whether libc defines __abort_msg]) +fi + dnl ****************************************************************** dnl *** Look for glib-genmarshal in PATH if we are cross-compiling *** dnl ****************************************************************** diff --git a/glib/gtestutils.c b/glib/gtestutils.c index fdb9494b8..36a0a8ec4 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -40,6 +40,19 @@ #ifdef HAVE_SYS_SELECT_H #include #endif /* HAVE_SYS_SELECT_H */ + +/* if we have a recent enough glibc, use its __abort_msg variable for storing + * assertion messages (just like assert()). If not, declare our own variable, + * so that platforms with older glibc or different libc implementations can use + * this feature for debugging as well. + */ +#ifdef HAVE_LIBC_ABORT_MSG +extern char *__abort_msg; +#define ASSERT_MESSAGE_STORE __abort_msg +#else +char *__glib_assert_msg = NULL; +#define ASSERT_MESSAGE_STORE __glib_assert_msg +#endif /* --- structures --- */ struct GTestCase @@ -1297,6 +1310,16 @@ g_assertion_message (const char *domain, func, func[0] ? ":" : "", " ", message, NULL); g_printerr ("**\n%s\n", s); + + /* store assertion message in global variable, so that it can be found in a + * core dump; also, use standard C allocation here for compatiblity with + * glibc's __abort_msg variable */ + if (ASSERT_MESSAGE_STORE != NULL) + /* free the old one */ + free (ASSERT_MESSAGE_STORE); + ASSERT_MESSAGE_STORE = (char*) malloc (strlen (s) + 1); + strcpy (ASSERT_MESSAGE_STORE, s); + g_test_log (G_TEST_LOG_ERROR, s, NULL, 0, NULL); g_free (s); abort(); diff --git a/tests/.gitignore b/tests/.gitignore index 0b37c5e85..0ab78eb23 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,4 @@ +assert-msg-test asyncqueue-test atomic-test base64-test diff --git a/tests/Makefile.am b/tests/Makefile.am index da1397643..0298f2a30 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -64,7 +64,8 @@ noinst_PROGRAMS = $(TEST_PROGS) \ unicode-normalize \ unicode-collate \ $(timeloop) \ - errorcheck-mutex-test + errorcheck-mutex-test \ + assert-msg-test TEST_PROGS += scannerapi scannerapi_SOURCES = scannerapi.c @@ -82,6 +83,7 @@ testgdate_LDADD = $(libglib) testgdateparser_LDADD = $(libglib) unicode_normalize_LDADD = $(libglib) errorcheck_mutex_test_LDADD = $(libglib) $(libgthread) $(G_THREAD_LIBS) +assert_msg_test_LDADD = $(libglib) if ENABLE_TIMELOOP timeloop_LDADD = $(libglib) timeloop_closure_LDADD = $(libglib) $(libgobject) @@ -136,7 +138,7 @@ test_programs = \ uri-test \ regex-test -test_scripts = run-markup-tests.sh run-collate-tests.sh run-bookmark-test.sh +test_scripts = run-markup-tests.sh run-collate-tests.sh run-bookmark-test.sh run-assert-msg-test.sh test_script_support_programs = markup-test unicode-collate bookmarkfile-test diff --git a/tests/assert-msg-test.c b/tests/assert-msg-test.c new file mode 100644 index 000000000..1dbe7bc15 --- /dev/null +++ b/tests/assert-msg-test.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + g_assert(42 < 0); + return 0; +} + diff --git a/tests/run-assert-msg-test.sh b/tests/run-assert-msg-test.sh new file mode 100755 index 000000000..0fd248222 --- /dev/null +++ b/tests/run-assert-msg-test.sh @@ -0,0 +1,48 @@ +#! /bin/sh + +fail () +{ + echo "Test failed: $*" + exit 1 +} + +echo_v () +{ + if [ "$verbose" = "1" ]; then + echo "$*" + fi +} + +error_out=/dev/null +if [ "$1" = "-v" ]; then + verbose=1 + error_out=/dev/stderr +fi + +echo_v "Running assert-msg-test" +OUT=$(./assert-msg-test 2>&1) && fail "assert-msg-test should abort" +echo "$OUT" | grep -q '^ERROR:assert-msg-test.c:.*:main: assertion failed: (42 < 0)' || \ + fail "does not print assertion message" + +if ! type gdb >/dev/null 2>&1; then + echo_v "Skipped (no gdb installed)" + exit 0 +fi + +# do we use libc's or our own variable? +if grep -q '^#define HAVE_LIBC_ABORT_MSG' $(dirname $0)/../config.h; then + VAR=__abort_msg +else + VAR=__glib_assert_msg +fi + +echo_v "Running gdb on assert-msg-test" +OUT=$(gdb --batch --ex run --ex "print (char*) $VAR" .libs/lt-assert-msg-test 2> $error_out) || \ + fail "failed to run gdb" + +echo_v "Checking if assert message is in $VAR" +if ! echo "$OUT" | grep -q '^$1.*"ERROR:assert-msg-test.c:.*:main: assertion failed: (42 < 0)"'; then + fail "$VAR does not have assertion message" +fi + +echo_v "All tests passed."