r15424: Implement a "stacktrace" smbcontrol option using libunwind's remote
authorJames Peach <jpeach@samba.org>
Thu, 4 May 2006 00:35:05 +0000 (00:35 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 16:16:42 +0000 (11:16 -0500)
stack tracing support. This provides an easy way for users to provide
stack traces (hopefully it will be implemented on something other than
ia64).
(This used to be commit 0b5e07e12daa98095dae27e0a6d53fe8ec3f3700)

source3/Makefile.in
source3/configure.in
source3/utils/smbcontrol.c

index 6d7919bfbf5cfabdff2ed98c8cf9cde62051c02e..773eb65016188ec8ab68ba6f5d822e99e0816886 100644 (file)
@@ -974,9 +974,9 @@ bin/smbstatus@EXEEXT@: $(STATUS_OBJ) @BUILD_POPT@ bin/.dummy
 
 bin/smbcontrol@EXEEXT@: $(SMBCONTROL_OBJ) @BUILD_POPT@ bin/.dummy
        @echo Linking $@
-       @$(CC) -DUSING_SMBCONTROL $(FLAGS) @PIE_LDFLAGS@ -o $@ $(SMBCONTROL_OBJ) $(DYNEXP) \
-       $(LDFLAGS) $(LIBS) \
-       @POPTLIBS@
+       @$(CC) -DUSING_SMBCONTROL $(FLAGS) @PIE_LDFLAGS@ -o $@ \
+       $(SMBCONTROL_OBJ) $(DYNEXP) $(LDFLAGS) \
+       $(LIBS) @LIBUNWIND_PTRACE@ @POPTLIBS@
 
 bin/smbtree@EXEEXT@: $(SMBTREE_OBJ) @BUILD_POPT@ bin/.dummy
        @echo Linking $@
index bb4cf82f3577cda22e6218a3084e15a61318a837..fc5bad6dab3c22a264d34885d2576c005b581038 100644 (file)
@@ -1280,12 +1280,61 @@ AC_TRY_LINK(
     [
        AC_MSG_RESULT(yes)
        AC_DEFINE(HAVE_LIBUNWIND, 1, [Whether libunwind is available])
+
+       # If we have libunwind, test whether we also have libunwind-ptrace
+       # which would let us unwind arbitrary processes.
+       save_LIBS=$LIBS
+       AC_CHECK_HEADERS(libunwind-ptrace.h)
+       AC_CHECK_LIB(unwind-ptrace, _UPT_create,
+           [
+               LIBUNWIND_PTRACE="-lunwind-ptrace";
+               AC_DEFINE(HAVE_LIBUNWIND_PTRACE, 1,
+                   [Whether libunwind-ptrace.a is available.])
+           ],
+           [ LIBUNWIND_PTRACE="" ])
+
+       LIBS=$save_LIBS
     ],
     [
        AC_MSG_RESULT(no)
        LIBS=$save_LIBS
     ])
 
+# To use libunwind-ptrace, we also need to make some ptrace system calls.
+if test x"$LIBUNWIND_PTRACE" != x"" ; then
+    AC_CHECK_HEADERS(sys/ptrace.h)
+    AC_MSG_CHECKING([for the Linux ptrace(2) interface])
+    AC_TRY_LINK(
+           [
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_PTRACE_H
+#include <sys/ptrace.h>
+#endif
+           ],
+           [
+               int main(int argc, const char ** argv)
+               {
+                       pid_t me = (pid_t)-1;
+                       ptrace(PTRACE_ATTACH, me, 0, 0);
+                       ptrace(PTRACE_DETACH, me, 0, 0);
+                       return 0;
+               }
+           ],
+           [
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_LINUX_PTRACE, 1,
+                   [Whether the Linux ptrace(2) interface is available.])
+           ],
+           [
+               AC_MSG_RESULT(no)
+               LIBUNWIND_PTRACE=""
+           ])
+fi
+
+AC_SUBST(LIBUNWIND_PTRACE)
+
 # syscall() is needed for smbwrapper.
 AC_CHECK_FUNCS(syscall)
 
index 5c798c79014b16c96a448795508d6c2b5f04bd5c..c368ee0ad4cb0428bb794c83fe2036c06fde48d9 100644 (file)
@@ -7,6 +7,7 @@
    Copyright (C) Andrew Tridgell 1994-1998
    Copyright (C) Martin Pool 2001-2002
    Copyright (C) Simo Sorce 2002
+   Copyright (C) James Peach 2006
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include "includes.h"
 
+#if HAVE_LIBUNWIND_H
+#include <libunwind.h>
+#endif
+
+#if HAVE_LIBUNWIND_PTRACE_H
+#include <libunwind-ptrace.h>
+#endif
+
+#if HAVE_SYS_PTRACE_H
+#include <sys/ptrace.h>
+#endif
+
 /* Default timeout value when waiting for replies (in seconds) */
 
 #define DEFAULT_TIMEOUT 10
@@ -131,7 +144,177 @@ static BOOL do_debug(const struct process_id pid,
                pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False);
 }
 
-/* Inject a fault (fata signal) into a running smbd */
+#if defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE)
+
+/* Return the name of a process given it's PID. This will only work on Linux,
+ * but that's probably moot since this whole stack tracing implementatino is
+ * Linux-specific anyway.
+ */
+static const char * procname(pid_t pid, char * buf, size_t bufsz)
+{
+       char path[64];
+       FILE * fp;
+
+       snprintf(path, sizeof(path), "/proc/%llu/cmdline",
+               (unsigned long long)pid);
+       if ((fp = fopen(path, "r")) == NULL) {
+               return NULL;
+       }
+
+       fgets(buf, bufsz, fp);
+
+       fclose(fp);
+       return buf;
+}
+
+static void print_stack_trace(pid_t pid, int * count)
+{
+       void *              pinfo = NULL;
+       unw_addr_space_t    aspace = NULL;
+       unw_cursor_t        cursor;
+       unw_word_t          ip, sp;
+
+       char                nbuf[256];
+       unw_word_t          off;
+
+       int ret;
+
+       if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
+               fprintf(stderr,
+                       "Failed to attach to process %llu: %s\n",
+                       (unsigned long long)pid, strerror(errno));
+               return;
+       }
+
+       /* Wait until the attach is complete. */
+       waitpid(pid, NULL, 0);
+
+       if (((pinfo = _UPT_create(pid)) == NULL) ||
+           ((aspace = unw_create_addr_space(&_UPT_accessors, 0)) == NULL)) {
+               /* Probably out of memory. */
+               fprintf(stderr,
+                       "Unable to initialize stack unwind for process %llu\n",
+                       (unsigned long long)pid);
+               goto cleanup;
+       }
+
+       if ((ret = unw_init_remote(&cursor, aspace, pinfo))) {
+               fprintf(stderr,
+                       "Unable to unwind stack for process %llu: %s\n",
+                       (unsigned long long)pid, unw_strerror(ret));
+               goto cleanup;
+       }
+
+       if (*count > 0) {
+               printf("\n");
+       }
+
+       if (procname(pid, nbuf, sizeof(nbuf))) {
+               printf("Stack trace for process %llu (%s):\n",
+                       (unsigned long long)pid, nbuf);
+       } else {
+               printf("Stack trace for process %llu:\n",
+                       (unsigned long long)pid);
+       }
+
+       while (unw_step(&cursor) > 0) {
+               ip = sp = off = 0;
+               unw_get_reg(&cursor, UNW_REG_IP, &ip);
+               unw_get_reg(&cursor, UNW_REG_SP, &sp);
+
+               ret = unw_get_proc_name(&cursor, nbuf, sizeof(nbuf), &off);
+               if (ret != 0 && ret != -UNW_ENOMEM) {
+                       snprintf(nbuf, sizeof(nbuf), "<unknown symbol>");
+               }
+               printf("    %s + %#llx [ip=%#llx] [sp=%#llx]\n",
+                       nbuf, (long long)off, (long long)ip,
+                       (long long)sp);
+       }
+
+       (*count)++;
+
+cleanup:
+       if (aspace) {
+               unw_destroy_addr_space(aspace);
+       }
+
+       if (pinfo) {
+               _UPT_destroy(pinfo);
+       }
+
+       ptrace(PTRACE_DETACH, pid, NULL, NULL);
+}
+
+static int stack_trace_connection(TDB_CONTEXT * tdb, TDB_DATA key,
+       TDB_DATA data, void * priv)
+{
+       struct connections_data conn;
+
+       if (data.dsize != sizeof(conn))
+               return 0;
+
+       memcpy(&conn, data.dptr, sizeof(conn));
+       print_stack_trace(procid_to_pid(&conn.pid), (int *)priv);
+
+       return 0;
+}
+
+static BOOL do_daemon_stack_trace(const struct process_id pid,
+                      const int argc, const char **argv)
+{
+       fprintf(stderr,
+               "Daemon stack tracing is not supported on this platform\n");
+       return False;
+
+       pid_t   dest;
+       int     count = 0;
+
+       if (argc != 1) {
+               fprintf(stderr, "Usage: smbcontrol <dest> stacktrace\n");
+               return False;
+       }
+
+       dest = procid_to_pid(&pid);
+
+       if (dest != 0) {
+               /* It would be nice to be able to make sure that this PID is
+                * the PID of a smbd/winbind/nmbd process, not some random PID
+                * the user liked the look of. It doesn't seem like it's worth
+                * the effort at the moment, however.
+                */
+               print_stack_trace(dest, &count);
+       } else {
+               TDB_CONTEXT * tdb;
+
+               tdb = tdb_open_log(lock_path("connections.tdb"), 0, 
+                                  TDB_DEFAULT, O_RDONLY, 0);
+               if (!tdb) {
+                       fprintf(stderr,
+                               "Failed to open connections database: %s\n",
+                               strerror(errno));
+                       return False;
+               }
+
+               tdb_traverse(tdb, stack_trace_connection, &count);
+               tdb_close(tdb);
+       }
+
+       return True;
+}
+
+#else /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */
+
+static BOOL do_daemon_stack_trace(const struct process_id pid,
+                      const int argc, const char **argv)
+{
+       fprintf(stderr,
+               "Daemon stack tracing is not supported on this platform\n");
+       return False;
+}
+
+#endif /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */
+
+/* Inject a fault (fatal signal) into a running smbd */
 
 static BOOL do_inject_fault(const struct process_id pid,
                       const int argc, const char **argv)
@@ -799,6 +982,8 @@ static const struct {
        { "profile", do_profile, "" },
        { "inject", do_inject_fault,
            "Inject a fatal signal into a running smbd"},
+       { "stacktrace", do_daemon_stack_trace,
+           "Display a stack trace of a daemon" },
        { "profilelevel", do_profilelevel, "" },
        { "debuglevel", do_debuglevel, "Display current debuglevels" },
        { "printnotify", do_printnotify, "Send a print notify message" },