1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-12 09:18:10 +03:00

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 commit is contained in:
James Peach 2006-05-04 00:35:05 +00:00 committed by Gerald (Jerry) Carter
parent 918f56b6de
commit 0b5e07e12d
3 changed files with 238 additions and 4 deletions

View 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 $@

View 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)

View 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
@ -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" },