[systemd-commits] 6 commits - Makefile.am TODO configure.ac man/journald.conf.xml src/journal

Lennart Poettering lennart at kemper.freedesktop.org
Thu Jun 19 03:38:54 PDT 2014


 Makefile.am                   |    9 +
 TODO                          |   11 --
 configure.ac                  |   39 ++++++++
 man/journald.conf.xml         |   39 ++++----
 src/journal/coredump.c        |   75 +++++++++------
 src/journal/coredumpctl.c     |   88 ++++++++++++------
 src/journal/journald-server.c |   11 +-
 src/journal/journald-server.h |    2 
 src/journal/journald.conf     |    2 
 src/journal/stacktrace.c      |  200 ++++++++++++++++++++++++++++++++++++++++++
 src/journal/stacktrace.h      |   24 +++++
 11 files changed, 400 insertions(+), 100 deletions(-)

New commits:
commit 92788e46d63e03c64146d90140599dbabf39627f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 19 12:38:22 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index e4724f9..17f0476 100644
--- a/TODO
+++ b/TODO
@@ -222,10 +222,6 @@ Features:
 
 * systemctl delete x.snapshot leaves no trace in logs (at least at default level).
 
-* make the coredump collector tool move itself into the user's cgroup
-  so that the coredump is properly written to the user's own journal
-  file.
-
 * seems that when we follow symlinks to units we prefer the symlink
   destination path over /etc and /usr. We should not do that. Instead
   /etc should always override /run+/usr and also any symlink

commit edc3797f7cd9e37c24e5241cac3263e7c918f732
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 19 12:36:35 2014 +0200

    journald: make SplitMode=uid the default
    
    Now that we actually can distuingish system and normal users there's no
    point in taking session information into account anymore when splitting
    up logs.
    
    This has the beenfit with that coredump information will actually end up
    in each user's own journal.

diff --git a/man/journald.conf.xml b/man/journald.conf.xml
index 5cd09a2..046609e 100644
--- a/man/journald.conf.xml
+++ b/man/journald.conf.xml
@@ -146,29 +146,30 @@
 
                                 <listitem><para>Controls whether to
                                 split up journal files per user. One
-                                of <literal>login</literal>,
-                                <literal>uid</literal> and
-                                <literal>none</literal>. If
-                                <literal>login</literal>, each logged-in
-                                user will get his own journal
-                                files, but systemd user IDs will log
-                                into the system journal. If
-                                <literal>uid</literal>, any user ID
-                                will get his own journal files
-                                regardless of whether it belongs to a
-                                system service or refers to a real
-                                logged in user. If
+                                of <literal>uid</literal>,
+                                <literal>login</literal> and
+                                <literal>none</literal>.  If
+                                <literal>uid</literal>, all users will
+                                get each their own journal files
+                                regardless of whether they possess a
+                                login session or not, however system
+                                users will log into the system
+                                journal. If <literal>login</literal>,
+                                actually logged-in users will get each
+                                their own journal files, but users
+                                without login session and system users
+                                will log into the system journal. If
                                 <literal>none</literal>, journal files
                                 are not split up by user and all
-                                messages are instead stored in the single
-                                system journal. Note that splitting
-                                up journal files by user is only
-                                available for journals stored
+                                messages are instead stored in the
+                                single system journal. Note that
+                                splitting up journal files by user is
+                                only available for journals stored
                                 persistently. If journals are stored
-                                on volatile storage (see above), only a
-                                single journal file for all user IDs
+                                on volatile storage (see above), only
+                                a single journal file for all user IDs
                                 is kept. Defaults to
-                                <literal>login</literal>.</para></listitem>
+                                <literal>uid</literal>.</para></listitem>
                         </varlistentry>
 
                         <varlistentry>
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 39a1a07..a4600e3 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -805,12 +805,11 @@ static void dispatch_message_real(
                 /* Split up strictly by any UID */
                 journal_uid = realuid;
         else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0)
-                /* Split up by login UIDs, this avoids creation of
-                 * individual journals for system UIDs.  We do this
-                 * only if the realuid is not root, in order not to
-                 * accidentally leak privileged information to the
-                 * user that is logged by a privileged process that is
-                 * part of an unprivileged session.*/
+                /* Split up by login UIDs.  We do this only if the
+                 * realuid is not root, in order not to accidentally
+                 * leak privileged information to the user that is
+                 * logged by a privileged process that is part of an
+                 * unprivileged session.*/
                 journal_uid = owner;
         else
                 journal_uid = 0;
diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h
index e468b82..42a2235 100644
--- a/src/journal/journald-server.h
+++ b/src/journal/journald-server.h
@@ -45,8 +45,8 @@ typedef enum Storage {
 } Storage;
 
 typedef enum SplitMode {
-        SPLIT_LOGIN,
         SPLIT_UID,
+        SPLIT_LOGIN,
         SPLIT_NONE,
         _SPLIT_MAX,
         _SPLIT_INVALID = -1
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index d106d00..cded4a9 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -11,7 +11,7 @@
 #Storage=auto
 #Compress=yes
 #Seal=yes
-#SplitMode=login
+#SplitMode=uid
 #SyncIntervalSec=5m
 #RateLimitInterval=30s
 #RateLimitBurst=1000

commit 9eecdbc354c180885f10415b01fee126f3bb34aa
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 19 12:25:42 2014 +0200

    update TODO

diff --git a/TODO b/TODO
index 7568418..e4724f9 100644
--- a/TODO
+++ b/TODO
@@ -400,13 +400,6 @@ Features:
   mode, it will never touch the RTC if the no reliable time source is active or the
   user did not request anything like it.
 
-* libunwind support for coredump pattern hook, and includes this in
-  the message for coredumps. After all, libunwind is now capable to
-  unwind coredumps since a few weeks ago. This probably requires that
-  we have nice support for multi-line messages on display in
-  logs-show.c. Alternatively: use libelfutil, which seems to be the
-  better supported alternative.
-
 * add libsystemd-password or so to query passwords during boot using the password agent logic
 
 * If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle().

commit 0c51aada566403039544bc8ef094fadb4af29e23
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 19 12:24:00 2014 +0200

    coredumpctl: introduce new -1 switch for showing a single, most recent entry only
    
    "coredumpctl info -1" is now incredibly useful for showing the most recent
    stacktrace.

diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index 9eaa897..7314683 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -45,12 +45,12 @@ static enum {
         ACTION_DUMP,
         ACTION_GDB,
 } arg_action = ACTION_LIST;
-
-static FILE* output = NULL;
 static const char* arg_field = NULL;
-
 static int arg_no_pager = false;
 static int arg_no_legend = false;
+static int arg_one = false;
+
+static FILE* output = NULL;
 
 static Set *new_matches(void) {
         Set *set;
@@ -165,7 +165,7 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "ho:F:", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0)
                 switch(c) {
 
                 case 'h':
@@ -208,6 +208,10 @@ static int parse_argv(int argc, char *argv[], Set *matches) {
                         arg_field = optarg;
                         break;
 
+                case '1':
+                        arg_one = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -535,33 +539,6 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
         return 0;
 }
 
-static int dump_list(sd_journal *j) {
-        int found = 0;
-
-        assert(j);
-
-        /* The coredumps are likely to compressed, and for just
-         * listing them we don't need to decompress them, so let's
-         * pick a fairly low data threshold here */
-        sd_journal_set_data_threshold(j, 4096);
-
-        SD_JOURNAL_FOREACH(j) {
-                if (arg_action == ACTION_INFO)
-                        print_info(stdout, j, found++);
-                else if (arg_field)
-                        print_field(stdout, j);
-                else
-                        print_list(stdout, j, found++);
-        }
-
-        if (!arg_field && !found) {
-                log_notice("No coredumps found");
-                return -ESRCH;
-        }
-
-        return 0;
-}
-
 static int focus(sd_journal *j) {
         int r;
 
@@ -573,12 +550,53 @@ static int focus(sd_journal *j) {
                 return r;
         }
         if (r == 0) {
-                log_error("No match found");
+                log_error("No match found.");
                 return -ESRCH;
         }
         return r;
 }
 
+static void print_entry(sd_journal *j, unsigned n_found) {
+        assert(j);
+
+        if (arg_action == ACTION_INFO)
+                print_info(stdout, j, n_found);
+        else if (arg_field)
+                print_field(stdout, j);
+        else
+                print_list(stdout, j, n_found);
+}
+
+static int dump_list(sd_journal *j) {
+        unsigned n_found = 0;
+        int r;
+
+        assert(j);
+
+        /* The coredumps are likely to compressed, and for just
+         * listing them we don't need to decompress them, so let's
+         * pick a fairly low data threshold here */
+        sd_journal_set_data_threshold(j, 4096);
+
+        if (arg_one) {
+                r = focus(j);
+                if (r < 0)
+                        return r;
+
+                print_entry(j, 0);
+        } else {
+                SD_JOURNAL_FOREACH(j)
+                        print_entry(j, n_found++);
+
+                if (!arg_field && n_found <= 0) {
+                        log_notice("No coredumps found.");
+                        return -ESRCH;
+                }
+        }
+
+        return 0;
+}
+
 static int dump_core(sd_journal* j) {
         const void *data;
         size_t len, ret;

commit 0cd77f9783f8e64cd77cd2640f9ffa343cdc567e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 19 12:13:09 2014 +0200

    coredump: simplify how we apply extended attributes to coredumps

diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index f48f4e2..0a141c9 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -159,34 +159,29 @@ static int fix_acl(int fd, uid_t uid) {
 }
 
 static int fix_xattr(int fd, char *argv[]) {
+
+        static const char * const xattrs[_ARG_MAX] = {
+                [ARG_PID] = "user.coredump.pid",
+                [ARG_UID] = "user.coredump.uid",
+                [ARG_GID] = "user.coredump.gid",
+                [ARG_SIGNAL] = "user.coredump.signal",
+                [ARG_TIMESTAMP] = "user.coredump.timestamp",
+                [ARG_COMM] = "user.coredump.comm",
+        };
+
         int r = 0;
+        unsigned i;
 
         /* Attach some metadate to coredumps via extended
          * attributes. Just because we can. */
 
-        if (!isempty(argv[ARG_PID]))
-                if (fsetxattr(fd, "user.coredump.pid", argv[ARG_PID], strlen(argv[ARG_PID]), XATTR_CREATE) < 0)
-                        r = -errno;
+        for (i = 0; i < _ARG_MAX; i++) {
+                if (isempty(argv[i]))
+                        continue;
 
-        if (!isempty(argv[ARG_UID]))
-                if (fsetxattr(fd, "user.coredump.uid", argv[ARG_UID], strlen(argv[ARG_UID]), XATTR_CREATE) < 0)
-                        r = -errno;
-
-        if (!isempty(argv[ARG_GID]))
-                if (fsetxattr(fd, "user.coredump.gid", argv[ARG_GID], strlen(argv[ARG_GID]), XATTR_CREATE) < 0)
-                        r = -errno;
-
-        if (!isempty(argv[ARG_SIGNAL]))
-                if (fsetxattr(fd, "user.coredump.signal", argv[ARG_SIGNAL], strlen(argv[ARG_SIGNAL]), XATTR_CREATE) < 0)
-                        r = -errno;
-
-        if (!isempty(argv[ARG_TIMESTAMP]))
-                if (fsetxattr(fd, "user.coredump.timestamp", argv[ARG_TIMESTAMP], strlen(argv[ARG_TIMESTAMP]), XATTR_CREATE) < 0)
-                        r = -errno;
-
-        if (!isempty(argv[ARG_COMM]))
-                if (fsetxattr(fd, "user.coredump.comm", argv[ARG_COMM], strlen(argv[ARG_COMM]), XATTR_CREATE) < 0)
+                if (fsetxattr(fd, xattrs[i], argv[i], strlen(argv[i]), XATTR_CREATE) < 0)
                         r = -errno;
+        }
 
         return r;
 }

commit 8d4e028f1868c47864ec873d9f30c3ee961a8849
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jun 19 12:07:12 2014 +0200

    coredump: include stacktrace of coredumps in the log message
    
    elfutils' libdw is maintained, can read DWARF debug data and appears to
    be the library of choice for generating backtraces today.

diff --git a/Makefile.am b/Makefile.am
index 7c20e33..bd33138 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3686,6 +3686,15 @@ systemd_coredump_LDADD = \
 	libsystemd-internal.la \
 	libsystemd-shared.la
 
+if HAVE_ELFUTILS
+systemd_coredump_SOURCES += \
+	src/journal/stacktrace.c \
+	src/journal/stacktrace.h
+
+systemd_coredump_LDADD += \
+	$(ELFUTILS_LIBS)
+endif
+
 rootlibexec_PROGRAMS += \
 	systemd-coredump
 
diff --git a/configure.ac b/configure.ac
index e35d864..1391d03 100644
--- a/configure.ac
+++ b/configure.ac
@@ -627,6 +627,44 @@ fi
 AC_SUBST(AUDIT_LIBS)
 
 # ------------------------------------------------------------------------------
+AC_ARG_ENABLE([elfutils],
+        AS_HELP_STRING([--disable-elfutils],[Disable optional ELFUTILS support]),
+                [case "${enableval}" in
+                        yes) have_elfutils=yes ;;
+                        no) have_elfutils=no ;;
+                        *) AC_MSG_ERROR(bad value ${enableval} for --disable-elfutils) ;;
+                esac],
+                [have_elfutils=auto])
+
+if test "x${have_elfutils}" != xno ; then
+        AC_CHECK_HEADERS(
+                [elfutils/libdwfl.h],
+                [have_elfutils=yes],
+                [if test "x$have_elfutils" = xyes ; then
+                        AC_MSG_ERROR([*** ELFUTILS headers not found.])
+                fi])
+
+        AC_CHECK_LIB(
+                [dw],
+                [dwfl_begin],
+                [have_elfutils=yes],
+                [if test "x$have_elfutils" = xyes ; then
+                        AC_MSG_ERROR([*** ELFUTILS libs not found.])
+                fi])
+
+        if test "x$have_elfutils" = xyes ; then
+                ELFUTILS_LIBS="-lelf -ldw"
+                AC_DEFINE(HAVE_ELFUTILS, 1, [ELFUTILS available])
+        else
+                have_elfutils=no
+        fi
+else
+        ELFUTILS_LIBS=
+fi
+AC_SUBST(ELFUTILS_LIBS)
+AM_CONDITIONAL(HAVE_ELFUTILS, [test "$have_elfutils" = "yes"])
+
+# ------------------------------------------------------------------------------
 have_libcryptsetup=no
 AC_ARG_ENABLE(libcryptsetup, AS_HELP_STRING([--disable-libcryptsetup], [disable libcryptsetup tools]))
 if test "x$enable_libcryptsetup" != "xno"; then
@@ -1171,6 +1209,7 @@ AC_MSG_RESULT([
         MICROHTTPD:              ${have_microhttpd}
         CHKCONFIG:               ${have_chkconfig}
         GNUTLS:                  ${have_gnutls}
+        ELFUTILS:                ${have_elfutils}
         binfmt:                  ${have_binfmt}
         vconsole:                ${have_vconsole}
         readahead:               ${have_readahead}
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index 3365f9f..f48f4e2 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -38,6 +38,7 @@
 #include "journald-native.h"
 #include "conf-parser.h"
 #include "copy.h"
+#include "stacktrace.h"
 
 #ifdef HAVE_ACL
 #include <sys/acl.h>
@@ -290,6 +291,7 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s
         _cleanup_free_ char *field = NULL;
         ssize_t n;
 
+        assert(fd >= 0);
         assert(ret);
         assert(ret_size);
 
@@ -346,7 +348,8 @@ int main(int argc, char* argv[]) {
         _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL,
                 *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL,
                 *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL,
-                *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL;
+                *coredump_filename = NULL, *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL,
+                *exe = NULL;
 
         _cleanup_close_ int coredump_fd = -1;
 
@@ -365,7 +368,6 @@ int main(int argc, char* argv[]) {
          * crashed and it might be journald which we'd rather not log
          * to then. */
         log_set_target(LOG_TARGET_KMSG);
-        log_set_max_level(LOG_DEBUG);
         log_open();
 
         if (argc != _ARG_MAX) {
@@ -474,10 +476,8 @@ int main(int argc, char* argv[]) {
                         IOVEC_SET_STRING(iovec[j++], core_slice);
         }
 
-        if (get_process_exe(pid, &t) >= 0) {
-                core_exe = strappend("COREDUMP_EXE=", t);
-                free(t);
-
+        if (get_process_exe(pid, &exe) >= 0) {
+                core_exe = strappend("COREDUMP_EXE=", exe);
                 if (core_exe)
                         IOVEC_SET_STRING(iovec[j++], core_exe);
         }
@@ -505,17 +505,15 @@ int main(int argc, char* argv[]) {
         IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
         IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
 
-        core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") dumped core.", NULL);
-        if (core_message)
-                IOVEC_SET_STRING(iovec[j++], core_message);
-
         /* Always stream the coredump to disk, if that's possible */
         r = save_external_coredump(argv, uid, &coredump_filename, &coredump_fd, &coredump_size);
         if (r < 0)
                 goto finish;
 
         /* If we don't want to keep the coredump on disk, remove it
-         * now, as later on we will lack the privileges for it. */
+         * now, as later on we will lack the privileges for
+         * it. However, we keep the fd to it, so that we can still
+         * process it and log it. */
         r = maybe_remove_external_coredump(coredump_filename, coredump_size);
         if (r < 0)
                 goto finish;
@@ -532,6 +530,24 @@ int main(int argc, char* argv[]) {
                 goto finish;
         }
 
+#ifdef HAVE_ELFUTILS
+        /* Try to get a strack trace if we can */
+        if (coredump_size <= arg_process_size_max) {
+                _cleanup_free_ char *stacktrace = NULL;
+
+                r = coredump_make_stack_trace(coredump_fd, exe, &stacktrace);
+                if (r >= 0)
+                        core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.\n\n", stacktrace, NULL);
+                else
+                        log_warning("Failed to generate stack trace: %s", strerror(-r));
+        }
+
+        if (!core_message)
+#endif
+        core_message = strjoin("MESSAGE=Process ", argv[ARG_PID], " (", argv[ARG_COMM], ") of user ", argv[ARG_UID], " dumped core.", NULL);
+        if (core_message)
+                IOVEC_SET_STRING(iovec[j++], core_message);
+
         /* Optionally store the entire coredump in the journal */
         if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) &&
             coredump_size <= (off_t) arg_journal_size_max) {
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index ea45946..9eaa897 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -403,7 +403,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
                 *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
                 *unit = NULL, *user_unit = NULL, *session = NULL,
                 *boot_id = NULL, *machine_id = NULL, *hostname = NULL,
-                *coredump = NULL, *slice = NULL, *cgroup = NULL, *owner_uid = NULL;
+                *coredump = NULL, *slice = NULL, *cgroup = NULL,
+                *owner_uid = NULL, *message = NULL;
         const void *d;
         size_t l;
 
@@ -427,6 +428,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
                 retrieve(d, l, "_BOOT_ID", &boot_id);
                 retrieve(d, l, "_MACHINE_ID", &machine_id);
                 retrieve(d, l, "_HOSTNAME", &hostname);
+                retrieve(d, l, "MESSAGE", &message);
         }
 
         if (need_space)
@@ -522,6 +524,14 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
                 if (access(coredump, F_OK) >= 0)
                         fprintf(file, "      Coredump: %s\n", coredump);
 
+        if (message) {
+                _cleanup_free_ char *m = NULL;
+
+                m = strreplace(message, "\n", "\n                ");
+
+                fprintf(file, "       Message: %s\n", strstrip(m ?: message));
+        }
+
         return 0;
 }
 
@@ -696,7 +706,7 @@ static int run_gdb(sd_journal *j) {
                         if (errno == ENOENT)
                                 log_error("Coredump neither in journal file nor stored externally on disk.");
                         else
-                                log_error("Failed to access coredump fiile: %s", strerror(-r));
+                                log_error("Failed to access coredump file: %m");
 
                         return -errno;
                 }
diff --git a/src/journal/stacktrace.c b/src/journal/stacktrace.c
new file mode 100644
index 0000000..6b9d272
--- /dev/null
+++ b/src/journal/stacktrace.c
@@ -0,0 +1,200 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dwarf.h>
+#include <elfutils/libdwfl.h>
+
+#include "util.h"
+#include "macro.h"
+#include "stacktrace.h"
+
+#define FRAMES_MAX 64
+#define THREADS_MAX 64
+
+struct stack_context {
+        FILE *f;
+        Dwfl *dwfl;
+        Elf *elf;
+        unsigned n_thread;
+        unsigned n_frame;
+};
+
+static int frame_callback(Dwfl_Frame *frame, void *userdata) {
+        struct stack_context *c = userdata;
+        Dwarf_Addr pc, pc_adjusted, bias = 0;
+        _cleanup_free_ Dwarf_Die *scopes = NULL;
+        const char *fname = NULL, *symbol = NULL;
+        Dwfl_Module *module;
+        bool is_activation;
+
+        assert(frame);
+        assert(c);
+
+        if (c->n_frame >= FRAMES_MAX)
+                return DWARF_CB_ABORT;
+
+        if (!dwfl_frame_pc(frame, &pc, &is_activation))
+                return DWARF_CB_ABORT;
+
+        pc_adjusted = pc - (is_activation ? 0 : 1);
+
+        module = dwfl_addrmodule(c->dwfl, pc_adjusted);
+        if (module) {
+                Dwarf_Die *s, *cudie;
+                int n;
+
+                cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
+                if (cudie) {
+                        n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
+                        for (s = scopes; s < scopes + n; s++) {
+                                if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
+                                        Dwarf_Attribute *a, space;
+
+                                        a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
+                                        if (!a)
+                                                a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
+                                        if (a)
+                                                symbol = dwarf_formstring(a);
+                                        if (!symbol)
+                                                symbol = dwarf_diename(s);
+
+                                        if (symbol)
+                                                break;
+                                }
+                        }
+                }
+
+                if (!symbol)
+                        symbol = dwfl_module_addrname(module, pc_adjusted);
+
+                fname = dwfl_module_info(module, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+        }
+
+        fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname));
+        c->n_frame ++;
+
+        return DWARF_CB_OK;
+}
+
+static int thread_callback(Dwfl_Thread *thread, void *userdata) {
+        struct stack_context *c = userdata;
+        pid_t tid;
+
+        assert(thread);
+        assert(c);
+
+        if (c->n_thread >= THREADS_MAX)
+                return DWARF_CB_ABORT;
+
+        if (c->n_thread != 0)
+                fputc('\n', c->f);
+
+        c->n_frame = 0;
+
+        tid = dwfl_thread_tid(thread);
+        fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
+
+        if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
+                return DWARF_CB_ABORT;
+
+        c->n_thread ++;
+
+        return DWARF_CB_OK;
+}
+
+int coredump_make_stack_trace(int fd, const char *executable, char **ret) {
+
+        static const Dwfl_Callbacks callbacks = {
+                .find_elf = dwfl_build_id_find_elf,
+                .find_debuginfo = dwfl_standard_find_debuginfo,
+        };
+
+        struct stack_context c = {};
+        char *buf = NULL;
+        size_t sz = 0;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+                return -errno;
+
+        c.f = open_memstream(&buf, &sz);
+        if (!c.f)
+                return -ENOMEM;
+
+        elf_version(EV_CURRENT);
+
+        c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+        if (!c.elf) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        c.dwfl = dwfl_begin(&callbacks);
+        if (!c.dwfl) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
+                r = -EINVAL;
+                goto finish;
+        }
+
+        fclose(c.f);
+        c.f = NULL;
+
+        *ret = buf;
+        buf = NULL;
+
+        r = 0;
+
+finish:
+        if (c.dwfl)
+                dwfl_end(c.dwfl);
+
+        if (c.elf)
+                elf_end(c.elf);
+
+        if (c.f)
+                fclose(c.f);
+
+        free(buf);
+
+        return r;
+}
diff --git a/src/journal/stacktrace.h b/src/journal/stacktrace.h
new file mode 100644
index 0000000..189e5c4
--- /dev/null
+++ b/src/journal/stacktrace.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2014 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int coredump_make_stack_trace(int fd, const char *executable, char **ret);



More information about the systemd-commits mailing list