diff options
author | James Peach <jpeach@samba.org> | 2006-05-04 00:35:05 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:16:42 -0500 |
commit | 4ab90ea08c9337128a75a34662c27a3d6bf88d32 (patch) | |
tree | 2a9e008af2e82cb0106dfc4f92a67b97b5eccc34 /source3 | |
parent | 7fbc91995e2d3330daed6353ec417a0b08408ed4 (diff) | |
download | samba-4ab90ea08c9337128a75a34662c27a3d6bf88d32.tar.gz samba-4ab90ea08c9337128a75a34662c27a3d6bf88d32.tar.bz2 samba-4ab90ea08c9337128a75a34662c27a3d6bf88d32.zip |
r15424: Implement a "stacktrace" smbcontrol option using libunwind's remote
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)
Diffstat (limited to 'source3')
-rw-r--r-- | source3/Makefile.in | 6 | ||||
-rw-r--r-- | source3/configure.in | 49 | ||||
-rw-r--r-- | source3/utils/smbcontrol.c | 187 |
3 files changed, 238 insertions, 4 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 6d7919bfbf..773eb65016 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 $@ diff --git a/source3/configure.in b/source3/configure.in index bb4cf82f35..fc5bad6dab 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -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) diff --git a/source3/utils/smbcontrol.c b/source3/utils/smbcontrol.c index 5c798c7901..c368ee0ad4 100644 --- a/source3/utils/smbcontrol.c +++ b/source3/utils/smbcontrol.c @@ -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 @@ -25,6 +26,18 @@ #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" }, |