summaryrefslogtreecommitdiffstats
path: root/src/run.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/run.c')
-rw-r--r--src/run.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/src/run.c b/src/run.c
new file mode 100644
index 0000000..23eb983
--- /dev/null
+++ b/src/run.c
@@ -0,0 +1,319 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+
+#include "config.h"
+
+extern char **environ;
+extern struct timeval tv_program_start;
+extern struct timeval tv_program_stop;
+
+
+static int store_config(struct lt_config_app *cfg, char *file)
+{
+ int fd;
+
+ if (-1 == (fd = open(file, O_CREAT | O_TRUNC | O_RDWR,
+ S_IRUSR))) {
+ perror("open failed");
+ return -1;
+ }
+
+ if (-1 == write(fd, &cfg->sh, sizeof(cfg->sh))) {
+ perror("read failed");
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int set_dir_notify(char *dir)
+{
+ int fd;
+
+ if (-1 == (fd = inotify_init())) {
+ perror("inotify_init failed");
+ return -1;
+ }
+
+ if (-1 == inotify_add_watch(fd, dir, IN_CREATE)) {
+ perror("inotify_add_watch failed");
+ return -1;
+ }
+
+ return fd;
+}
+
+static int get_fifo_dir(char *buf, int len)
+{
+ snprintf(buf, len , "%s-XXXXXX", CONFIG_LT_CONFIG);
+ if (NULL == mkdtemp(buf)) {
+ perror("mkdtemp failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_fifo(struct lt_config_app *cfg, int notify_fd,
+ char *dir, pid_t *pid)
+{
+ char str_fifo[LT_MAXFILE];
+ unsigned char buf[1000];
+ struct inotify_event *event = (struct inotify_event*) buf;
+
+ if (-1 == read(notify_fd, event, 1000)) {
+ perror("read notify failed");
+ return -1;
+ }
+
+ sscanf(event->name, "fifo-%d", pid);
+ sprintf(str_fifo, "%s/%s", dir, event->name);
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "thread id %d, got fifo: %s\n",
+ *pid, str_fifo);
+
+ return lt_fifo_open(cfg, str_fifo);
+}
+
+static int process_fifo(struct lt_config_app *cfg, struct lt_thread *t)
+{
+ static char buf[FIFO_MSG_MAXLEN];
+
+ struct lt_fifo_mbase *mbase = (struct lt_fifo_mbase*) buf;
+
+ if (-1 == lt_fifo_recv(cfg, t, mbase, FIFO_MSG_MAXLEN))
+ return -1;
+
+ if ((FIFO_MSG_TYPE_ENTRY != mbase->type) &&
+ (FIFO_MSG_TYPE_EXIT != mbase->type)) {
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "unexpected message type %d\n",
+ mbase->type);
+ return -1;
+ }
+
+ struct lt_fifo_msym *msym = (struct lt_fifo_msym*) buf;
+
+ if (cfg->sh.counts)
+ return lt_stats_sym(cfg, t, msym);
+
+ if (FIFO_MSG_TYPE_ENTRY == msym->h.type) {
+
+ cfg->sh.indent_depth++;
+ lt_out_entry(&cfg->sh,
+ msym->data + msym->sym,
+ msym->data + msym->lib,
+ msym->data + msym->arg,
+ msym->data + msym->argd);
+
+ } else if (FIFO_MSG_TYPE_EXIT == msym->h.type) {
+
+ lt_out_exit(&cfg->sh,
+ msym->data + msym->sym,
+ msym->data + msym->lib,
+ msym->data + msym->arg,
+ msym->data + msym->argd);
+
+ cfg->sh.indent_depth--;
+ }
+
+ return 0;
+}
+
+static int process(struct lt_config_app *cfg, pid_t pid, char *dir, int notify_fd)
+{
+ int finish = 0, getin = 1;
+ int max_fd = notify_fd;
+ fd_set cfg_set, wrk_set;
+ int status;
+
+ FD_ZERO(&cfg_set);
+ FD_SET(notify_fd, &cfg_set);
+
+ while(!waitpid(pid, &status, WNOHANG) ||
+ /* let all the thread fifo close */
+ (finish) ||
+ /* Get inside at least once, in case the traced program
+ finished before we got here. Another case is if there's
+ an input on notify descriptor, we want to try another
+ select to be sure we dont miss any other event. */
+ (getin))
+ {
+
+ struct timeval tv = { 0, 100 };
+ struct lt_thread *t;
+ int ret;
+
+ getin = 0;
+
+ wrk_set = cfg_set;
+ if (-1 == (ret = select(max_fd + 1, &wrk_set, NULL, NULL, &tv))) {
+ perror("select failed");
+ return -1;
+ }
+
+ if (!ret)
+ continue;
+
+ /* process notify */
+ if (FD_ISSET(notify_fd, &wrk_set)) {
+ int fd;
+ pid_t pid;
+
+ /* try to get any event at least once again */
+ getin = 1;
+
+ if (-1 == (fd = get_fifo(cfg, notify_fd, dir, &pid)))
+ continue;
+
+ if (NULL == (t = lt_thread_add(cfg, fd, pid))) {
+ close(fd);
+ continue;
+ }
+
+ finish++;
+
+ FD_SET(fd, &cfg_set);
+ max_fd = (max_fd < fd ? fd : max_fd);
+ ret--;
+ }
+
+ /* process fifo */
+ for(t = cfg->threads; t ; t = t->next) {
+ if (FD_ISSET(t->fifo_fd, &wrk_set)) {
+ if (-1 == process_fifo(cfg, t)) {
+ FD_CLR(t->fifo_fd, &cfg_set);
+ gettimeofday(&t->tv_stop, NULL);
+ finish--;
+ }
+ ret--;
+ }
+ }
+ }
+
+ return status;
+}
+
+static int remove_dir(struct lt_config_app *cfg, char *name)
+{
+ DIR *dir;
+ struct dirent *d;
+
+ if (NULL == (dir = opendir(name))) {
+ perror("opendir failed");
+ return -1;
+ }
+
+ while((d = readdir(dir))) {
+ char file[LT_MAXFILE];
+
+ if (!strcmp(".", d->d_name) ||
+ (!strcmp("..", d->d_name)))
+ continue;
+
+ sprintf(file, "%s/%s", name, d->d_name);
+ if (-1 == (unlink(file)))
+ perror("unlink failed");
+ }
+
+ closedir(dir);
+
+ if (-1 == remove(name)) {
+ perror("remove failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+int lt_run(struct lt_config_app *cfg)
+{
+ pid_t child_pid;
+ char str_dir[LT_MAXFILE];
+ char str_cfg[LT_MAXFILE];
+ int status;
+ int notify_fd = -1;
+
+ if (-1 == get_fifo_dir(str_dir, LT_MAXFILE))
+ return -1;
+
+ sprintf(str_cfg, "%s/config", str_dir);
+ if (-1 == store_config(cfg, str_cfg))
+ return -1;
+
+ if (cfg->sh.pipe &&
+ (-1 == (notify_fd = set_dir_notify(str_dir))))
+ return -1;
+
+ gettimeofday(&tv_program_start, NULL);
+
+ if (0 == (child_pid = fork())) {
+ char str_audit[100];
+
+ sprintf(str_audit, "%s/libltaudit.so.%s", CONFIG_LIBDIR, LT_VER);
+ setenv("LD_AUDIT", str_audit, 1);
+ setenv("LT_DIR", str_dir, 1);
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "executing %s\n", cfg->prog);
+
+ if (-1 == execvp(cfg->prog, cfg->arg)) {
+ printf("execve failed for \"%s\" : %s\n",
+ cfg->prog, strerror(errno));
+ return -1;
+ }
+ } else if (child_pid < 0) {
+ perror("fork failed");
+ return -1;
+ }
+
+ if (cfg->sh.pipe)
+ status = process(cfg, child_pid, str_dir, notify_fd);
+ else
+ waitpid(child_pid, &status, 0);
+
+ gettimeofday(&tv_program_stop, NULL);
+
+ printf("\n%s finished - ", cfg->prog);
+
+ if (WIFEXITED(status)) {
+ printf("exited, status=%d\n", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ printf("killed by signal %d\n", WTERMSIG(status));
+ }
+
+ remove_dir(cfg, str_dir);
+ return 0;
+}