diff options
Diffstat (limited to 'runtime/staprun')
-rw-r--r-- | runtime/staprun/ChangeLog | 57 | ||||
-rw-r--r-- | runtime/staprun/cap.c | 78 | ||||
-rw-r--r-- | runtime/staprun/common.c | 46 | ||||
-rw-r--r-- | runtime/staprun/ctl.c | 35 | ||||
-rw-r--r-- | runtime/staprun/mainloop.c | 296 | ||||
-rw-r--r-- | runtime/staprun/stapio.c | 17 | ||||
-rw-r--r-- | runtime/staprun/staprun.c | 173 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 10 | ||||
-rw-r--r-- | runtime/staprun/staprun_funcs.c | 104 | ||||
-rw-r--r-- | runtime/staprun/symbols.c | 333 | ||||
-rw-r--r-- | runtime/staprun/unwind_data.c | 97 |
11 files changed, 505 insertions, 741 deletions
diff --git a/runtime/staprun/ChangeLog b/runtime/staprun/ChangeLog index e9ef2e2d..969c299d 100644 --- a/runtime/staprun/ChangeLog +++ b/runtime/staprun/ChangeLog @@ -1,3 +1,60 @@ +2008-05-05 Martin Hunt <hunt@redhat.com> + + * mainloop.c (child_proc): Handle sig_chld + in the proper thread. + (signal_thread): Don't call send_request() + because it isn't thread-safe. + +2008-05-05 Martin Hunt <hunt@redhat.com> + + * mainloop.c (signal_thread): New thread to handle signals + better. + (setup_main_signals): Create signal thread. + +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + PR 6008 + * common.c (parse_args): Increase the limitation of the buffer size + to 4095MB. + * common.c (usage): Ditto. + +2008-04-30 Masami Hiramatsu <mhiramat@redhat.com> + + * stapio.c (main): Fix a typo in a debug message. + * staprun.c (main): Ditto. + +2008-04-24 Frank Ch. Eigler <fche@elastic.org> + + PR 6451. + * common.c (control_channel): Initialize to -1. + * ctl.c (close_ctl_channel): Tolerate fd=0. + +2008-04-22 Martin Hunt <hunt@redhat.com> + + * cap.c (init_cap): Detect capabilities failure and + run with them disabled. + +2008-04-22 Martin Hunt <hunt@redhat.com> + + * mainloop.c (send_request): Move here from common.c + staprun no longer send any messages. + +2008-04-22 hunt <hunt@redhat.com> + + * common.c (usage): Add -d option. + +2008-04-21 Martin Hunt <hunt@redhat.com> + + * staprun.c, stapio.c, staprun.h, mainloop.c, staprun_funcs.c, + ctl.c, common.c: Add "-d" option to have staprun remove + modules. Have staprun exec stapio and then have stapio + exec "staprun -d" to remove the module when finished. + +2008-04-16 Martin Hunt <hunt@redhat.com> + + * ctl.c (init_ctl_channel): Remove unused parameter. + Just opens one channel now. + 2008-02-21 David Smith <dsmith@redhat.com> * staprun_funcs.c (check_path): Small security fix. diff --git a/runtime/staprun/cap.c b/runtime/staprun/cap.c index 6f22dfc9..6ac6701f 100644 --- a/runtime/staprun/cap.c +++ b/runtime/staprun/cap.c @@ -23,6 +23,8 @@ #include "staprun.h" #include <sys/prctl.h> +static int _stp_no_caps = 0; + /* like perror, but exits */ #define ferror(msg) { \ _perr(msg); \ @@ -54,10 +56,10 @@ * CAP_CHOWN - allows chown */ -int init_cap(void) +void init_cap(void) { cap_t caps = cap_init(); - cap_value_t capv[] = {CAP_SYS_MODULE, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SETUID, CAP_SETGID, CAP_DAC_OVERRIDE}; + cap_value_t capv[] = { CAP_SYS_MODULE, CAP_SYS_ADMIN, CAP_SYS_NICE, CAP_SETUID, CAP_SETGID, CAP_DAC_OVERRIDE }; const int numcaps = sizeof(capv) / sizeof(capv[0]); uid_t uid = getuid(); gid_t gid = getgid(); @@ -69,8 +71,11 @@ int init_cap(void) if (cap_set_flag(caps, CAP_PERMITTED, numcaps, capv, CAP_SET) < 0) ferror("cap_set_flag"); - if (cap_set_proc(caps) < 0) - ferror("cap_set_proc"); + if (cap_set_proc(caps) < 0) { + dbug(1, "Setting capabilities failed. Capabilities disabled.\n"); + _stp_no_caps = 1; + return; + } cap_free(caps); @@ -82,8 +87,6 @@ int init_cap(void) if (setresgid(gid, gid, gid) < 0) ferror("setresgid"); - - return 1; } void print_cap(char *text) @@ -97,19 +100,18 @@ void print_cap(char *text) perr("cap_get_proc"); return; } - + getresuid(&uid, &euid, &suid); getresgid(&gid, &egid, &sgid); printf("***** %s\n", text); - if ((p=prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0)) < 0) + if ((p = prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0)) < 0) perr("Couldn't get PR_SET_KEEPCAPS flag value"); - else + else printf("KEEPCAPS: %d\n", p); - printf("uid: %d, euid: %d, suid: %d\ngid: %d. egid: %d, sgid: %d\n", - uid, euid, suid, gid, egid, sgid ); + printf("uid: %d, euid: %d, suid: %d\ngid: %d. egid: %d, sgid: %d\n", uid, euid, suid, gid, egid, sgid); printf("Caps: %s\n", cap_to_text(caps, NULL)); cap_free(caps); printf("*****\n\n"); @@ -121,38 +123,44 @@ void print_cap(char *text) */ void drop_cap(cap_value_t cap) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_CLEAR) < 0) - ferror("Could not clear effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); + if (_stp_no_caps == 0) { + cap_t caps = cap_get_proc(); + if (caps == NULL) + ferror("cap_get_proc failed"); + if (cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_CLEAR) < 0) + ferror("Could not clear effective capabilities"); + if (cap_set_proc(caps) < 0) + ferror("Could not apply capability set"); + cap_free(caps); + } } /* add_cap() adds a permitted capability to the effective set. */ void add_cap(cap_value_t cap) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) < 0) - ferror("Could not set effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); + if (_stp_no_caps == 0) { + cap_t caps = cap_get_proc(); + if (caps == NULL) + ferror("cap_get_proc failed"); + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET) < 0) + ferror("Could not set effective capabilities"); + if (cap_set_proc(caps) < 0) + ferror("Could not apply capability set"); + cap_free(caps); + } } /* del_cap() deletes a permitted capability from the effective set. */ void del_cap(cap_value_t cap) { - cap_t caps = cap_get_proc(); - if (caps == NULL) - ferror("cap_get_proc failed"); - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR) < 0) - ferror("Could not clear effective capabilities"); - if (cap_set_proc(caps) < 0) - ferror("Could not apply capability set"); - cap_free(caps); + if (_stp_no_caps == 0) { + cap_t caps = cap_get_proc(); + if (caps == NULL) + ferror("cap_get_proc failed"); + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_CLEAR) < 0) + ferror("Could not clear effective capabilities"); + if (cap_set_proc(caps) < 0) + ferror("Could not apply capability set"); + cap_free(caps); + } } diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c index 47778efd..93da51d8 100644 --- a/runtime/staprun/common.c +++ b/runtime/staprun/common.c @@ -22,6 +22,7 @@ unsigned int buffer_size; char *target_cmd; char *outfile_name; int attach_mod; +int delete_mod; int load_only; int need_uprobes; @@ -30,8 +31,7 @@ char *modname = NULL; char *modpath = ""; char *modoptions[MAXMODOPTIONS]; -int initialized = 0; -int control_channel = 0; +int control_channel = -1; /* NB: fd==0 possible */ void parse_args(int argc, char **argv) { @@ -44,10 +44,11 @@ void parse_args(int argc, char **argv) target_cmd = NULL; outfile_name = NULL; attach_mod = 0; + delete_mod = 0; load_only = 0; need_uprobes = 0; - while ((c = getopt(argc, argv, "ALuvb:t:d:c:o:x:")) != EOF) { + while ((c = getopt(argc, argv, "ALuvb:t:dc:o:x:")) != EOF) { switch (c) { case 'u': need_uprobes = 1; @@ -57,8 +58,8 @@ void parse_args(int argc, char **argv) break; case 'b': buffer_size = (unsigned)atoi(optarg); - if (buffer_size < 1 || buffer_size > 64) { - err("Invalid buffer size '%d' (should be 1-64).\n", buffer_size); + if (buffer_size < 1 || buffer_size > 4095) { + err("Invalid buffer size '%d' (should be 1-4095).\n", buffer_size); usage(argv[0]); } break; @@ -67,7 +68,8 @@ void parse_args(int argc, char **argv) target_pid = atoi(optarg); break; case 'd': - /* obsolete internal option used by stap */ + /* delete module */ + delete_mod = 1; break; case 'c': target_cmd = optarg; @@ -128,11 +130,14 @@ void usage(char *prog) err("-o FILE Send output to FILE.\n"); err("-b buffer size The systemtap module specifies a buffer size.\n"); err(" Setting one here will override that value. The\n"); - err(" value should be an integer between 1 and 64\n"); + err(" value should be an integer between 1 and 4095 \n"); err(" which be assumed to be the buffer size in MB.\n"); err(" That value will be per-cpu in bulk mode.\n"); err("-L Load module and start probes, then detach.\n"); err("-A Attach to loaded systemtap module.\n"); + err("-d Delete a module. Only detached or unused modules\n"); + err(" the user has permission to access will be deleted. Use \"*\"\n"); + err(" (quoted) to delete all unused modules.\n"); err("MODULE can be either a module name or a module path. If a\n"); err("module name is used, it is looked for in the following\n"); err("directory: /lib/modules/`uname -r`/systemtap\n"); @@ -250,10 +255,7 @@ static void fatal_handler (int signum) rc = write (STDERR_FILENO, ERR_MSG, sizeof(ERR_MSG)); rc = write (STDERR_FILENO, str, strlen(str)); rc = write (STDERR_FILENO, "\n", 1); - if (initialized) - _exit(3); - else - _exit(1); + _exit(1); } void setup_signals(void) @@ -294,28 +296,6 @@ void setup_signals(void) #endif } -/** - * send_request - send request to kernel over control channel - * @type: the relay-app command id - * @data: pointer to the data to be sent - * @len: length of the data to be sent - * - * Returns 0 on success, negative otherwise. - */ -int send_request(int type, void *data, int len) -{ - char buf[1024]; - - /* Before doing memcpy, make sure 'buf' is big enough. */ - if ((len + 4) > (int)sizeof(buf)) { - _err("exceeded maximum send_request size.\n"); - return -1; - } - memcpy(buf, &type, 4); - memcpy(&buf[4], data, len); - return write(control_channel, buf, len+4); -} - /* * set FD_CLOEXEC for any file descriptor */ diff --git a/runtime/staprun/ctl.c b/runtime/staprun/ctl.c index af7e6c1a..4597bf72 100644 --- a/runtime/staprun/ctl.c +++ b/runtime/staprun/ctl.c @@ -12,45 +12,42 @@ #include "staprun.h" -int init_ctl_channel(int symbols) +int init_ctl_channel(const char *name, int verb) { - char *cname, buf[PATH_MAX]; + char buf[PATH_MAX]; struct statfs st; int old_transport = 0; - - if (symbols) - cname = ".symbols"; - else - cname = ".cmd"; - if (statfs("/sys/kernel/debug", &st) == 0 && (int) st.f_type == (int) DEBUGFS_MAGIC) { - if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s/%s", modname, cname)) + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) { + if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s/.cmd", name)) return -1; } else { old_transport = 1; - if (sprintf_chk(buf, "/proc/systemtap/%s/%s", modname, cname)) + if (sprintf_chk(buf, "/proc/systemtap/%s/.cmd", name)) return -1; } - - dbug(2, "Opening %s\n", buf); + + dbug(2, "Opening %s\n", buf); control_channel = open(buf, O_RDWR); if (control_channel < 0) { - if (attach_mod && errno == ENOENT) - err("ERROR: Can not attach. Module %s not running.\n", modname); - else - perr("Couldn't open control channel '%s'", buf); + if (verb) { + if (attach_mod && errno == ENOENT) + err("ERROR: Can not attach. Module %s not running.\n", name); + else + perr("Couldn't open control channel '%s'", buf); + } return -1; } if (set_clexec(control_channel) < 0) return -1; - + return old_transport; } void close_ctl_channel(void) { - if (control_channel > 0) { + if (control_channel >= 0) { close(control_channel); - control_channel = 0; + control_channel = -1; } } diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index 2e0c3c5c..61963743 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -15,39 +15,98 @@ /* globals */ int ncpus; -int use_old_transport = 0; +static int use_old_transport = 0; +//enum _stp_sig_type { sig_none, sig_done, sig_detach }; +//static enum _stp_sig_type got_signal = sig_none; -static void sigproc(int signum) +/** + * send_request - send request to kernel over control channel + * @type: the relay-app command id + * @data: pointer to the data to be sent + * @len: length of the data to be sent + * + * Returns 0 on success, negative otherwise. + */ +int send_request(int type, void *data, int len) { - dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); - - if (signum == SIGCHLD) { - pid_t pid = waitpid(-1, NULL, WNOHANG); - if (pid != target_pid) - return; - send_request(STP_EXIT, NULL, 0); - } else if (signum == SIGQUIT) - cleanup_and_exit(2); - else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) - send_request(STP_EXIT, NULL, 0); + char buf[1024]; + + /* Before doing memcpy, make sure 'buf' is big enough. */ + if ((len + 4) > (int)sizeof(buf)) { + _err("exceeded maximum send_request size.\n"); + return -1; + } + memcpy(buf, &type, 4); + memcpy(&buf[4], data, len); + return write(control_channel, buf, len + 4); } -static void setup_main_signals(int cleanup) +static void *signal_thread(void *arg) { - struct sigaction a; - memset(&a, 0, sizeof(a)); - sigfillset(&a.sa_mask); - if (cleanup == 0) { - a.sa_handler = sigproc; - sigaction(SIGCHLD, &a, NULL); - } else - a.sa_handler = SIG_IGN; - sigaction(SIGINT, &a, NULL); - sigaction(SIGTERM, &a, NULL); - sigaction(SIGHUP, &a, NULL); - sigaction(SIGQUIT, &a, NULL); + sigset_t *s = (sigset_t *) arg; + int signum, rc, btype = STP_EXIT; + + while (1) { + if (sigwait(s, &signum) < 0) { + _perr("sigwait"); + continue; + } + dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); + if (signum == SIGQUIT) + cleanup_and_exit(1); + else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) { + // send STP_EXIT + rc = write(control_channel, &btype, sizeof(btype)); + break; + } + } + return NULL; +} + +static void chld_proc(int signum) +{ + int32_t rc, btype = STP_EXIT; + dbug(2, "chld_proc %d (%s)\n", signum, strsignal(signum)); + pid_t pid = waitpid(-1, NULL, WNOHANG); + if (pid != target_pid) + return; + // send STP_EXIT + rc = write(control_channel, &btype, sizeof(btype)); } +static void setup_main_signals(void) +{ + pthread_t tid; + struct sigaction sa; + sigset_t *s = malloc(sizeof(*s)); + if (!s) { + _perr("malloc failed"); + exit(1); + } + sigfillset(s); + pthread_sigmask(SIG_SETMASK, s, NULL); + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + + sa.sa_handler = chld_proc; + sigaction(SIGCHLD, &sa, NULL); + + sigemptyset(s); + sigaddset(s, SIGINT); + sigaddset(s, SIGTERM); + sigaddset(s, SIGHUP); + sigaddset(s, SIGQUIT); + pthread_sigmask(SIG_SETMASK, s, NULL); + if (pthread_create(&tid, NULL, signal_thread, s) < 0) { + _perr("failed to create thread"); + exit(1); + } +} /* * start_cmd forks the command given on the command line @@ -75,7 +134,7 @@ void start_cmd(void) a.sa_handler = SIG_IGN; sigaction(SIGINT, &a, NULL); - dbug (1, "execing target_cmd %s\n", target_cmd); + dbug(1, "execing target_cmd %s\n", target_cmd); if ((pid = fork()) < 0) { _perr("fork"); exit(1); @@ -86,8 +145,8 @@ void start_cmd(void) sigaction(SIGINT, &a, NULL); /* commands we fork need to run at normal priority */ - setpriority (PRIO_PROCESS, 0, 0); - + setpriority(PRIO_PROCESS, 0, 0); + /* wait here until signaled */ sigwait(&usrset, &signum); @@ -107,11 +166,11 @@ void system_cmd(char *cmd) { pid_t pid; - dbug (2, "system %s\n", cmd); + dbug(2, "system %s\n", cmd); if ((pid = fork()) < 0) { _perr("fork"); } else if (pid == 0) { - setpriority (PRIO_PROCESS, 0, 0); + setpriority(PRIO_PROCESS, 0, 0); if (execl("/bin/sh", "sh", "-c", cmd, NULL) < 0) perr("%s", cmd); _exit(1); @@ -128,7 +187,7 @@ static void read_buffer_info(void) if (!use_old_transport) return; - if (statfs("/sys/kernel/debug", &st) == 0 && (int) st.f_type == (int) DEBUGFS_MAGIC) + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) return; if (sprintf_chk(buf, "/proc/systemtap/%s/bufsize", modname)) @@ -152,7 +211,6 @@ static void read_buffer_info(void) return; } - /** * init_stapio - initialize the app * @print_summary: boolean, print summary or not at end of run @@ -164,7 +222,7 @@ int init_stapio(void) dbug(2, "init_stapio\n"); /* create control channel */ - use_old_transport = init_ctl_channel(0); + use_old_transport = init_ctl_channel(modname, 1); if (use_old_transport < 0) { err("Failed to initialize control channel.\n"); return -1; @@ -177,7 +235,7 @@ int init_stapio(void) if (init_oldrelayfs() < 0) { close_ctl_channel(); return -1; - } + } } else { if (init_relayfs() < 0) { close_ctl_channel(); @@ -192,17 +250,12 @@ int init_stapio(void) if (target_cmd) start_cmd(); - return 0; } -/* cleanup_and_exit() closed channels and frees memory - * then exits with the following status codes: - * 1 - failed to initialize. - * 2 - disconnected - * 3 - initialized - */ -void cleanup_and_exit (int closed) +/* cleanup_and_exit() closed channels, frees memory, + * removes the module (if necessary) and exits. */ +void cleanup_and_exit(int detach) { pid_t err; static int exiting = 0; @@ -211,32 +264,34 @@ void cleanup_and_exit (int closed) return; exiting = 1; - setup_main_signals(1); + setup_main_signals(); - dbug(1, "CLEANUP AND EXIT closed=%d\n", closed); + dbug(1, "detach=%d\n", detach); /* what about child processes? we will wait for them here. */ err = waitpid(-1, NULL, WNOHANG); if (err >= 0) err("\nWaiting for processes to exit\n"); - while(wait(NULL) > 0) ; + while (wait(NULL) > 0) ; if (use_old_transport) - close_oldrelayfs(closed == 2); + close_oldrelayfs(detach); else close_relayfs(); dbug(1, "closing control channel\n"); close_ctl_channel(); - if (initialized == 2 && closed == 2) { - err("\nDisconnecting from systemtap module.\n" \ - "To reconnect, type \"staprun -A %s\"\n", modname); - } else if (initialized) - closed = 3; - else - closed = 1; - exit(closed); + if (detach) { + err("\nDisconnecting from systemtap module.\n" "To reconnect, type \"staprun -A %s\"\n", modname); + } else { + dbug(2, "removing %s\n", modname); + if (execl(BINDIR "/staprun", "staprun", "-d", modname, NULL) < 0) { + perror(modname); + _exit(1); + } + } + _exit(0); } /** @@ -247,90 +302,103 @@ int stp_main_loop(void) { ssize_t nb; void *data; - int type; + uint32_t type; FILE *ofp = stdout; char recvbuf[8196]; setvbuf(ofp, (char *)NULL, _IOLBF, 0); - setup_main_signals(0); + setup_main_signals(); dbug(2, "in main loop\n"); send_request(STP_READY, NULL, 0); - while (1) { /* handle messages from control channel */ + /* handle messages from control channel */ + while (1) { nb = read(control_channel, recvbuf, sizeof(recvbuf)); + dbug(2, "nb=%d\n", (int)nb); if (nb <= 0) { if (errno != EINTR) _perr("Unexpected EOF in read (nb=%ld)", (long)nb); continue; } - - type = *(int *)recvbuf; - data = (void *)(recvbuf + sizeof(int)); - switch (type) { + type = *(uint32_t *) recvbuf; + data = (void *)(recvbuf + sizeof(uint32_t)); + nb -= sizeof(uint32_t); + + switch (type) { #ifdef STP_OLD_TRANSPORT case STP_REALTIME_DATA: - { - ssize_t bw = write(out_fd[0], data, nb - sizeof(int)); - if (bw >= 0 && bw != (nb - (ssize_t)sizeof(int))) { - nb = nb - bw; - bw = write(out_fd[0], data, nb - sizeof(int)); + { + ssize_t bw = write(out_fd[0], data, nb); + if (bw >= 0 && bw != nb) { + nb = nb - bw; + bw = write(out_fd[0], data, nb); + } + if (bw != nb) { + _perr("write error (nb=%ld)", (long)nb); + cleanup_and_exit(0); + } + break; } - if (bw != (nb - (ssize_t)sizeof(int))) { - _perr("write error (nb=%ld)", (long)nb); - cleanup_and_exit(1); - } - break; - } #endif case STP_OOB_DATA: - fputs ((char *)data, stderr); - break; - case STP_EXIT: - { - /* module asks us to unload it and exit */ - int *closed = (int *)data; - dbug(2, "got STP_EXIT, closed=%d\n", *closed); - cleanup_and_exit(*closed); + fputs((char *)data, stderr); break; - } - case STP_START: - { - struct _stp_msg_start *t = (struct _stp_msg_start *)data; - dbug(2, "probe_start() returned %d\n", t->res); - if (t->res < 0) { - if (target_cmd) - kill (target_pid, SIGKILL); - cleanup_and_exit(1); - } else if (target_cmd) - kill (target_pid, SIGUSR1); - break; - } + case STP_EXIT: + { + /* module asks us to unload it and exit */ + dbug(2, "got STP_EXIT\n"); + cleanup_and_exit(0); + break; + } + case STP_START: + { + struct _stp_msg_start *t = (struct _stp_msg_start *)data; + dbug(2, "probe_start() returned %d\n", t->res); + if (t->res < 0) { + if (target_cmd) + kill(target_pid, SIGKILL); + cleanup_and_exit(0); + } else if (target_cmd) + kill(target_pid, SIGUSR1); + break; + } case STP_SYSTEM: - { - struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; - dbug(2, "STP_SYSTEM: %s\n", c->cmd); - system_cmd(c->cmd); - break; - } + { + struct _stp_msg_cmd *c = (struct _stp_msg_cmd *)data; + dbug(2, "STP_SYSTEM: %s\n", c->cmd); + system_cmd(c->cmd); + break; + } case STP_TRANSPORT: - { - struct _stp_msg_start ts; - if (use_old_transport) { - if (init_oldrelayfs() < 0) - cleanup_and_exit(1); - } else { - if (init_relayfs() < 0) + { + struct _stp_msg_start ts; + if (use_old_transport) { + if (init_oldrelayfs() < 0) + cleanup_and_exit(0); + } else { + if (init_relayfs() < 0) + cleanup_and_exit(0); + } + ts.target = target_pid; + send_request(STP_START, &ts, sizeof(ts)); + if (load_only) cleanup_and_exit(1); + break; + } + case STP_UNWIND: + { + int len; + char *ptr = (char *)data; + while (nb > 0) { + send_unwind_data(ptr); + len = strlen(ptr) + 1; + ptr += len; + nb -= len; + } + break; } - ts.target = target_pid; - initialized = 2; - send_request(STP_START, &ts, sizeof(ts)); - if (load_only) - cleanup_and_exit(2); - break; - } default: err("WARNING: ignored message of type %d\n", (type)); } diff --git a/runtime/staprun/stapio.c b/runtime/staprun/stapio.c index ee30a1a1..3c8c4f7f 100644 --- a/runtime/staprun/stapio.c +++ b/runtime/staprun/stapio.c @@ -27,24 +27,23 @@ char *__name__ = "stapio"; int main(int argc, char **argv) { setup_signals(); - parse_args(argc, argv); if (buffer_size) - dbug(1, "Using a buffer of %u bytes.\n", buffer_size); + dbug(1, "Using a buffer of %u MB.\n", buffer_size); if (optind < argc) { parse_modpath(argv[optind++]); dbug(2, "modpath=\"%s\", modname=\"%s\"\n", modpath, modname); } - if (optind < argc) { + if (optind < argc) { if (attach_mod) { err("ERROR: Cannot have module options with attach (-A).\n"); usage(argv[0]); } else { - unsigned start_idx = 3; /* reserve three slots in modoptions[] */ - while (optind < argc && start_idx+1 < MAXMODOPTIONS) + unsigned start_idx = 3; /* reserve three slots in modoptions[] */ + while (optind < argc && start_idx + 1 < MAXMODOPTIONS) modoptions[start_idx++] = argv[optind++]; modoptions[start_idx] = NULL; } @@ -57,13 +56,7 @@ int main(int argc, char **argv) if (init_stapio()) exit(1); - - initialized = 1; - if (attach_mod) { - /* already started */ - initialized++; - } - + if (stp_main_loop()) { err("ERROR: Couldn't enter main loop. Exiting.\n"); exit(1); diff --git a/runtime/staprun/staprun.c b/runtime/staprun/staprun.c index f4e67fdb..0291d01f 100644 --- a/runtime/staprun/staprun.c +++ b/runtime/staprun/staprun.c @@ -16,21 +16,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Copyright (C) 2005-2007 Red Hat, Inc. + * Copyright (C) 2005-2008 Red Hat, Inc. * */ #include "staprun.h" -int inserted_module = 0; - /* used in dbug, _err and _perr */ char *__name__ = "staprun"; extern long delete_module(const char *, unsigned int); -static int -run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) +static int run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) { pid_t pid; int rstatus; @@ -42,14 +39,13 @@ run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) err("%s ", argv[i]); i++; } - err("\n"); + err("\n"); } if ((pid = fork()) < 0) { _perr("fork"); return -1; - } - else if (pid == 0) { + } else if (pid == 0) { /* Make sure we run as the full user. If we're * switching to a non-root user, this won't allow * that process to switch back to root (since the @@ -79,17 +75,6 @@ run_as(uid_t uid, gid_t gid, const char *path, char *const argv[]) return -1; } -/* Keep the uid and gid settings because we will likely */ -/* conditionally restore "-u" */ -static int run_stapio(char **argv) -{ - uid_t uid = getuid(); - gid_t gid = getgid(); - argv[0] = PKGLIBDIR "/stapio"; - - return run_as(uid, gid, argv[0], argv); -} - /* * Module to be inserted has one or more user-space probes. Make sure * uprobes is enabled. @@ -132,8 +117,7 @@ static int enable_uprobes(void) dbug(2, "Inserting uprobes module from SystemTap runtime.\n"); argv[0] = NULL; - return insert_module(PKGDATADIR "/runtime/uprobes/uprobes.ko", - NULL, argv); + return insert_module(PKGDATADIR "/runtime/uprobes/uprobes.ko", NULL, argv); } static int insert_stap_module(void) @@ -144,6 +128,66 @@ static int insert_stap_module(void) return insert_module(modpath, bufsize_option, modoptions); } +static int remove_module(const char *name, int verb); + +static void remove_all_modules(void) +{ + char *base; + struct statfs st; + struct dirent *d; + DIR *moddir; + + if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) + base = "/sys/kernel/debug/systemtap"; + else + base = "/proc/systemtap"; + + moddir = opendir(base); + if (moddir) { + while ((d = readdir(moddir))) + if (remove_module(d->d_name, 0) == 0) + printf("Module %s removed.\n", d->d_name); + closedir(moddir); + } +} + +static int remove_module(const char *name, int verb) +{ + int ret; + dbug(2, "%s\n", name); + + if (strcmp(name, "*") == 0) { + remove_all_modules(); + return 0; + } + + /* Call init_ctl_channel() which actually attempts an open() + * of the control channel. This is better than using access() because + * an open on an already open channel will fail, preventing us from attempting + * to remove an in-use module. + */ + if (init_ctl_channel(name, 0) < 0) { + if (verb) + err("Error accessing systemtap module %s: %s\n", name, strerror(errno)); + return 1; + } + close_ctl_channel(); + + dbug(2, "removing module %s\n", name); + + /* Don't remove module when priority is elevated. */ + if (setpriority(PRIO_PROCESS, 0, 0) < 0) + _perr("setpriority"); + + ret = do_cap(CAP_SYS_MODULE, delete_module, name, 0); + if (ret != 0) { + err("Error removing module '%s': %s.\n", name, strerror(errno)); + return 1; + } + + dbug(1, "Module %s removed.\n", name); + return 0; +} int init_staprun(void) { @@ -154,71 +198,28 @@ int init_staprun(void) /* We're done with CAP_SYS_ADMIN. */ drop_cap(CAP_SYS_ADMIN); - - if (!attach_mod) { + + if (delete_mod) + exit(remove_module(modname, 1)); + else if (!attach_mod) { if (need_uprobes && enable_uprobes() != 0) return -1; if (insert_stap_module() < 0) return -1; - else - inserted_module = 1; } - return 0; } - -static void cleanup(int rc) -{ - /* Only cleanup once. */ - static int done = 0; - if (done == 0) - done = 1; - else - return; - - dbug(2, "rc=%d, inserted_module=%d\n", rc, inserted_module); - - if (setpriority (PRIO_PROCESS, 0, 0) < 0) - _perr("setpriority"); - - stop_symbol_thread(); - - /* rc == 2 means disconnected */ - if (rc == 2) - return; - - /* If we inserted the module and did not get rc==2, then */ - /* we really want to remove it. */ - if (inserted_module || rc == 3) { - long ret; - dbug(2, "removing module %s\n", modname); - ret = do_cap(CAP_SYS_MODULE, delete_module, modname, 0); - if (ret != 0) - err("Error removing module '%s': %s\n", modname, moderror(errno)); - } -} - -static void exit_cleanup(void) -{ - dbug(2, "something exited...\n"); - cleanup(1); -} int main(int argc, char **argv) { int rc; - if (atexit(exit_cleanup)) { - _perr("cannot set exit function"); - exit(1); - } - - /* NB: Don't do the geteuid()!=0 check here, since we want to - test command-line error-handling while running non-root. */ + /* NB: Don't do the geteuid()!=0 check here, since we want to + test command-line error-handling while running non-root. */ /* Get rid of a few standard environment variables (which */ /* might cause us to do unintended things). */ rc = unsetenv("IFS") || unsetenv("CDPATH") || unsetenv("ENV") - || unsetenv("BASH_ENV"); + || unsetenv("BASH_ENV"); if (rc) { _perr("unsetenv failed"); exit(-1); @@ -229,20 +230,20 @@ int main(int argc, char **argv) parse_args(argc, argv); if (buffer_size) - dbug(2, "Using a buffer of %u bytes.\n", buffer_size); + dbug(2, "Using a buffer of %u MB.\n", buffer_size); if (optind < argc) { parse_modpath(argv[optind++]); dbug(2, "modpath=\"%s\", modname=\"%s\"\n", modpath, modname); } - if (optind < argc) { + if (optind < argc) { if (attach_mod) { err("ERROR: Cannot have module options with attach (-A).\n"); usage(argv[0]); } else { unsigned start_idx = 0; - while (optind < argc && start_idx+1 < MAXMODOPTIONS) + while (optind < argc && start_idx + 1 < MAXMODOPTIONS) modoptions[start_idx++] = argv[optind++]; modoptions[start_idx] = NULL; } @@ -254,14 +255,13 @@ int main(int argc, char **argv) } if (geteuid() != 0) { - err("ERROR: The effective user ID of staprun must be set to the root user.\n" - " Check permissions on staprun and ensure it is a setuid root program.\n"); + err("ERROR: The effective user ID of staprun must be set to the root user.\n" + " Check permissions on staprun and ensure it is a setuid root program.\n"); exit(1); } - if (!init_cap()) - exit(1); - + init_cap(); + if (check_permissions() != 1) usage(argv[0]); @@ -277,11 +277,14 @@ int main(int argc, char **argv) if (init_staprun()) exit(1); - setup_staprun_signals(); - start_symbol_thread(); - - rc = run_stapio(argv); - cleanup(rc); - + argv[0] = PKGLIBDIR "/stapio"; + if (execv(argv[0], argv) < 0) { + perror(argv[0]); + goto err; + } return 0; + +err: + remove_module(modname, 1); + return 1; } diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 1128fb4c..60bab391 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -103,8 +103,6 @@ extern char *__name__; #define STP_OLD_TRANSPORT #include "../transport/transport_msgs.h" -extern int use_old_transport; - #define RELAYFS_MAGIC 0xF0B4A981 #define DEBUGFS_MAGIC 0x64626720 #define DEBUGFSDIR "/sys/kernel/debug" @@ -118,9 +116,8 @@ int init_stapio(void); int stp_main_loop(void); int send_request(int type, void *data, int len); void cleanup_and_exit (int); -int do_module(void *); -int do_kernel_symbols(void); -int init_ctl_channel(int); +void send_unwind_data(const char *name); +int init_ctl_channel(const char *name, int verb); void close_ctl_channel(void); int init_relayfs(void); void close_relayfs(void); @@ -129,7 +126,7 @@ void close_oldrelayfs(int); void setup_signals(void); /* cap.c */ void print_cap(char *text); -int init_cap(void); +void init_cap(void); void add_cap(cap_value_t cap); void del_cap(cap_value_t cap); void drop_cap(cap_value_t cap); @@ -169,6 +166,7 @@ extern int target_pid; extern char *target_cmd; extern char *outfile_name; extern int attach_mod; +extern int delete_mod; extern int load_only; extern int need_uprobes; diff --git a/runtime/staprun/staprun_funcs.c b/runtime/staprun/staprun_funcs.c index 34e12c25..c1cb92b7 100644 --- a/runtime/staprun/staprun_funcs.c +++ b/runtime/staprun/staprun_funcs.c @@ -16,18 +16,6 @@ #include <grp.h> #include <pwd.h> -void setup_staprun_signals(void) -{ - struct sigaction a; - memset(&a, 0, sizeof(a)); - sigfillset(&a.sa_mask); - a.sa_handler = SIG_IGN; - sigaction(SIGINT, &a, NULL); - sigaction(SIGTERM, &a, NULL); - sigaction(SIGHUP, &a, NULL); - sigaction(SIGQUIT, &a, NULL); -} - extern long init_module(void *, unsigned long, const char *); /* Module errors get translated. */ @@ -401,95 +389,3 @@ int check_permissions(void) * is in that directory. */ return check_path(); } - -pthread_t symbol_thread_id = (pthread_t)0; -int kernel_ptr_size = 0; - -/* Symbol handling thread */ -void *handle_symbols(void __attribute__((unused)) *arg) -{ - ssize_t nb; - void *data; - int32_t type; - char recvbuf[8192]; - - dbug(2, "waiting for symbol requests\n"); - - /* handle messages from control channel */ - while (1) { - nb = read(control_channel, recvbuf, sizeof(recvbuf)); - if (nb <= 0) { - if (errno != EINTR) - _perr("Unexpected EOF in read (nb=%ld)", (long)nb); - continue; - } - - type = *(int32_t *)recvbuf; - data = (void *)(recvbuf + sizeof(int32_t)); - - switch (type) { - case STP_MODULE: - { - dbug(2, "STP_MODULES request received\n"); - if (do_module(data) < 0) - goto done; - break; - } - case STP_SYMBOLS: - { - struct _stp_msg_symbol *req = (struct _stp_msg_symbol *)data; - dbug(2, "STP_SYMBOLS request received\n"); - if (req->endian != 0x1234) { - err("ERROR: staprun is compiled with different endianess than the kernel!\n"); - goto done; - } - kernel_ptr_size = req->ptr_size; - if (kernel_ptr_size != 4 && kernel_ptr_size != 8) { - err("ERROR: invalid kernel pointer size %d\n", kernel_ptr_size); - goto done; - } - if (do_kernel_symbols() < 0) - goto done; - break; - } - default: - err("WARNING: ignored message of type %d\n", (type)); - } - } - -done: - /* signal stapio we're done */ - kill(0, SIGINT); - - return NULL; -} - -void start_symbol_thread(void) -{ - int status; - - /* create symbol control channel */ - status = do_cap(CAP_DAC_OVERRIDE, init_ctl_channel, 1); - drop_cap(CAP_DAC_OVERRIDE); - if (status < 0) { - err("Failed to initialize control channel.\n"); - exit(1); - } - status = pthread_create(&symbol_thread_id, NULL, handle_symbols, NULL); - if (status) { - perr("Failed to create symbol thread.\n"); - exit(1); - } -} - -void stop_symbol_thread(void) -{ - - if (symbol_thread_id) { - dbug(2, "Stopping symbol thread.\n"); - pthread_cancel(symbol_thread_id); - pthread_join(symbol_thread_id, NULL); - } - close_ctl_channel(); -} - diff --git a/runtime/staprun/symbols.c b/runtime/staprun/symbols.c deleted file mode 100644 index c7362d9e..00000000 --- a/runtime/staprun/symbols.c +++ /dev/null @@ -1,333 +0,0 @@ -/* -*- linux-c -*- - * Symbols and modules functions for staprun. - * - * Copyright (C) 2006-2008 Red Hat Inc. - * - * This file is part of systemtap, and is free software. You can - * redistribute it and/or modify it under the terms of the GNU General - * Public License (GPL); either version 2, or (at your option) any - * later version. - */ - -#include "staprun.h" - -/* send symbol data */ -static int send_data(int32_t type, void *data, int len) -{ - if (write(control_channel, &type, 4) <= 0) - return -1; - return write(control_channel, data, len); -} - - -/* Get the sections for a module. Put them in the supplied buffer */ -/* in the following order: */ -/* [struct _stp_msg_module][struct _stp_symbol sections ...][string data][unwind data] */ -/* Return the total length of all the data. */ - -#define SECDIR "/sys/module/%s/sections" -static int get_sections(char *name, char *data_start, int datalen) -{ - char dir[STP_MODULE_NAME_LEN + sizeof(SECDIR)]; - char filename[STP_MODULE_NAME_LEN + 256]; - char buf[32], strdata_start[32768]; - char *strdata=strdata_start, *data=data_start; - int fd, len, res, unwind_data_len=0; - struct _stp_msg_module *mod = (struct _stp_msg_module *)data_start; - - struct dirent *d; - DIR *secdir; - void *sec; - int struct_symbol_size = kernel_ptr_size == 8 ? sizeof(struct _stp_symbol64) : sizeof(struct _stp_symbol32); - uint64_t sec_addr; - - /* start of data is a struct _stp_msg_module */ - data += sizeof(struct _stp_msg_module); - - res = snprintf(dir, sizeof(dir), SECDIR, name); - if (res >= (int)sizeof(dir)) { - _err("Couldn't fit module \"%s\" into dir buffer.\n" \ - "This should never happen. Please file a bug report.\n", name); - return -1; - } - - if ((secdir = opendir(dir)) == NULL) - return 0; - - /* Initialize mod. */ - memset(mod, 0, sizeof(struct _stp_msg_module)); - - /* Copy name in and check for overflow. */ - strncpy(mod->name, name, STP_MODULE_NAME_LEN); - if (mod->name[STP_MODULE_NAME_LEN - 1] != '\0') { - _err("Couldn't fit module \"%s\" into mod->name buffer.\n" \ - "This should never happen. Please file a bug report.\n", name); - return -1; - } - - /* FIXME: optionally fill in unwind data here */ - mod->unwind_len = unwind_data_len; - - while ((d = readdir(secdir))) { - char *secname = d->d_name; - - /* Copy filename in and check for overflow. */ - res = snprintf(filename, sizeof(filename), "/sys/module/%s/sections/%s", name, secname); - if (res >= (int)sizeof(filename)) { - _err("Couldn't fit secname \"%s\" into filename buffer.\n" \ - "This should never happen. Please file a bug report.\n", secname); - closedir(secdir); - return -1; - } - - /* filter out some non-useful stuff */ - if (!strncmp(secname,"__",2) - || !strcmp(secname,".") - || !strcmp(secname,"..") - || !strcmp(secname,".module_sig") - || !strcmp(secname,".modinfo") - || !strcmp(secname,".strtab") - || !strcmp(secname,".symtab") ) { - continue; - } - if (!strncmp(secname, ".gnu.linkonce", 13) - && strcmp(secname, ".gnu.linkonce.this_module")) - continue; - - if ((fd = open(filename,O_RDONLY)) >= 0) { - if (read(fd, buf, 32) > 0) { - /* create next section */ - sec = data; - if (data - data_start + struct_symbol_size > datalen) - goto err1; - data += struct_symbol_size; - - sec_addr = (uint64_t)strtoull(buf,NULL,16); - if (kernel_ptr_size == 8) { - ((struct _stp_symbol64 *)sec)->addr = sec_addr; - ((struct _stp_symbol64 *)sec)->symbol = (uint64_t)(strdata - strdata_start); - } else { - ((struct _stp_symbol32 *)sec)->addr = (uint32_t)sec_addr; - ((struct _stp_symbol32 *)sec)->symbol = (uint32_t)(strdata - strdata_start); - } - mod->num_sections++; - - /* now create string data for the - * section (checking for overflow) */ - if ((strdata - strdata_start + strlen(strdata)) - >= sizeof(strdata_start)) - goto err1; - strcpy(strdata, secname); - strdata += strlen(secname) + 1; - - /* These sections are used a lot so keep the values handy */ - if (!strcmp(secname, ".data") || !strncmp(secname, ".rodata", 7)) { - if (mod->data == 0 || sec_addr < mod->data) - mod->data = sec_addr; - } - if (!strcmp(secname, ".text")) - mod->text = sec_addr; - if (!strcmp(secname, ".gnu.linkonce.this_module")) - mod->module = sec_addr; - } - close(fd); - } - } - closedir(secdir); - - /* consolidate buffers */ - len = strdata - strdata_start; - if ((len + data - data_start) > datalen) - goto err0; - strdata = strdata_start; - while (len--) - *data++ = *strdata++; - -#if 0 - if (unwind_data_len) { - if ((unwind_data_len + data - data_start) > datalen) - goto err0; - memcpy(data, unwind_data, unwind_data_len); - data += unwind_data_len; - } -#endif - return data - data_start; - -err1: - close(fd); - closedir(secdir); -err0: - /* if this happens, something went seriously wrong. */ - _err("Unexpected error. Overflowed buffers.\n"); - return -1; -} -#undef SECDIR - -/* - * For modules, we send the name, section names, and offsets - */ -static int send_module (char *mname) -{ - char data[65536]; - int len; - len = get_sections(mname, data, sizeof(data)); - if (len > 0) { - if (send_data(STP_MODULE, data, len) < 0) { - _err("Loading of module %s failed. Exiting...\n", mname); - return -1; - } - } - return len; -} - -/* - * Send either all modules, or a specific one. - * Returns: - * >=0 : OK - * -1 : serious error (exit) - */ -int do_module (void *data) -{ - struct _stp_msg_module *mod = (struct _stp_msg_module *)data; - - if (mod->name[0] == 0) { - struct dirent *d; - DIR *moddir = opendir("/sys/module"); - if (moddir) { - while ((d = readdir(moddir))) - if (send_module(d->d_name) < 0) { - closedir(moddir); - return -1; - } - closedir(moddir); - } - send_request(STP_MODULE, data, 1); - return 0; - } - - return send_module(mod->name); -} - -#define MAX_SYMBOLS 32*1024 - -/* - * Read /proc/kallsyms and send all kernel symbols to the - * systemtap module. Ignore module symbols; the systemtap module - * can access them directly. - */ -int do_kernel_symbols(void) -{ - FILE *kallsyms=NULL; - char *name, *mod, *dataptr, *datamax, type, *data_base=NULL; - unsigned long long addr; - void *syms = NULL; - int ret, num_syms, i = 0, struct_symbol_size; - int max_syms= MAX_SYMBOLS, data_basesize = MAX_SYMBOLS*32; - - if (kernel_ptr_size == 8) - struct_symbol_size = sizeof(struct _stp_symbol64); - else - struct_symbol_size = sizeof(struct _stp_symbol32); - - syms = malloc(max_syms * struct_symbol_size); - data_base = malloc(data_basesize); - if (data_base == NULL || syms == NULL) { - _err("Failed to allocate memory for symbols\n"); - goto err; - } - dataptr = data_base; - datamax = data_base + data_basesize; - - kallsyms = fopen ("/proc/kallsyms", "r"); - if (!kallsyms) { - _perr("Fatal error: Unable to open /proc/kallsyms"); - goto err; - } - - /* put empty string in data */ - *dataptr++ = 0; - - while ((ret = fscanf(kallsyms, "%llx %c %as [%as", &addr, &type, &name, &mod))>0 - && dataptr < datamax) { - if (ret < 3) - continue; - if (ret > 3) { - /* ignore modules */ - free(name); - free(mod); - /* modules are loaded above the kernel, so if we */ - /* are getting modules, then we're done. */ - break; - } - - if (type == 't' || type == 'T' || type == 'A') { - if (kernel_ptr_size == 8) { - ((struct _stp_symbol64 *)syms)[i].addr = (uint64_t)addr; - ((struct _stp_symbol64 *)syms)[i].symbol = (uint64_t)(dataptr - data_base); - } else { - ((struct _stp_symbol32 *)syms)[i].addr = (uint32_t)addr; - ((struct _stp_symbol32 *)syms)[i].symbol = (uint32_t)(dataptr - data_base); - } - if (dataptr >= datamax - strlen(name)) { - char *db; - data_basesize *= 2; - db = realloc(data_base, data_basesize); - if (db == NULL) { - _err("Could not allocate enough space for symbols.\n"); - goto err; - } - dataptr = db + (dataptr - data_base); - datamax = db + data_basesize; - data_base = db; - } - strcpy(dataptr, name); - dataptr += strlen(name) + 1; - free(name); - i++; - if (i >= max_syms) { - max_syms *= 2; - syms = realloc(syms, max_syms*struct_symbol_size); - if (syms == NULL) { - _err("Could not allocate enough space for symbols.\n"); - goto err; - } - } - } - } - num_syms = i; - if (num_syms <= 0) - goto err; - - - /* send header */ - struct _stp_msg_symbol_hdr smsh; - smsh.num_syms = num_syms; - smsh.sym_size = (uint32_t)(dataptr - data_base); - smsh.unwind_size = (uint32_t)0; - if (send_request(STP_SYMBOLS, &smsh, sizeof(smsh)) <= 0) - goto err; - - /* send syms */ - if (send_data(STP_SYMBOLS, syms, num_syms*struct_symbol_size) < 0) - goto err; - - /* send data */ - if (send_data(STP_SYMBOLS, data_base, dataptr-data_base) < 0) - goto err; - - free(data_base); - free(syms); - fclose(kallsyms); - return 0; - -err: - if (data_base) - free(data_base); - if (syms) - free(syms); - if (kallsyms) - fclose(kallsyms); - - _err("Loading of symbols failed. Exiting...\n"); - return -1; -} diff --git a/runtime/staprun/unwind_data.c b/runtime/staprun/unwind_data.c new file mode 100644 index 00000000..ed27cc20 --- /dev/null +++ b/runtime/staprun/unwind_data.c @@ -0,0 +1,97 @@ +/* -*- linux-c -*- + * Unwind data functions for staprun. + * + * Copyright (C) 2008 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#include "staprun.h" +#include <elfutils/libdwfl.h> +#include <dwarf.h> + +static char debuginfo_path_arr[] = "-:.debug:/usr/lib/debug"; +static char *debuginfo_path = debuginfo_path_arr; +static const Dwfl_Callbacks kernel_callbacks = { + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + .find_elf = dwfl_linux_kernel_find_elf, + .section_address = dwfl_linux_kernel_module_section_address, +}; + +void *get_module_unwind_data(Dwfl * dwfl, const char *name, int *len) +{ + Dwarf_Addr bias = 0; + Dwarf *dw; + GElf_Ehdr *ehdr, ehdr_mem; + GElf_Shdr *shdr, shdr_mem; + Elf_Scn *scn = NULL; + Elf_Data *data = NULL; + + Dwfl_Module *mod = dwfl_report_module(dwfl, name, 0, 0); + dwfl_report_end(dwfl, NULL, NULL); + dw = dwfl_module_getdwarf(mod, &bias); + Elf *elf = dwarf_getelf(dw); + ehdr = gelf_getehdr(elf, &ehdr_mem); + while ((scn = elf_nextscn(elf, scn))) { + shdr = gelf_getshdr(scn, &shdr_mem); + if (strcmp(elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name), ".debug_frame") == 0) { + data = elf_rawdata(scn, NULL); + break; + } + } + + if (data == NULL) { + *len = 0; + dbug(2, "module %s returns NULL\n", name); + return NULL; + } + dbug(2, "module %s returns %d\n", name, (int)data->d_size); + *len = data->d_size; + return data->d_buf; +} + +void send_unwind_data(const char *name) +{ + struct _stp_msg_unwind *un; + int unwind_data_len = 0; + void *unwind_data = NULL; + char *buf; + + dbug(2, "module %s\n", name); + if (strcmp(name, "*")) { + Dwfl *dwfl = dwfl_begin(&kernel_callbacks); + + if (name[0] == 0) + unwind_data = get_module_unwind_data(dwfl, "kernel", &unwind_data_len); + else + unwind_data = get_module_unwind_data(dwfl, name, &unwind_data_len); + + /* yuck */ + buf = (char *)malloc(unwind_data_len + sizeof(*un) + sizeof(uint32_t)); + if (!buf) { + err("malloc failed\n"); + return; + } + memcpy(buf + sizeof(*un) + sizeof(uint32_t), unwind_data, unwind_data_len); + dwfl_end(dwfl); + } else { + buf = (char *)malloc(sizeof(*un) + sizeof(uint32_t)); + if (!buf) { + err("malloc failed\n"); + return; + } + } + + un = (struct _stp_msg_unwind *)(buf + sizeof(uint32_t)); + strncpy(un->name, name, sizeof(un->name)); + un->unwind_len = unwind_data_len; + *(uint32_t *) buf = STP_UNWIND; + + /* send unwind data */ + if (write(control_channel, buf, unwind_data_len + sizeof(*un) + sizeof(uint32_t)) <= 0) + err("write failed\n"); +} |