summaryrefslogtreecommitdiffstats
path: root/src/error.c
diff options
context:
space:
mode:
authorJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-10-18 10:00:38 +0200
committerJiri Olsa <Jiri Olsa jolsa@redhat.com>2011-11-24 21:20:27 +0100
commit2d6cc3704e7affeb56c798b39a942daca95385dc (patch)
treedd339c0bbc9305fa4352deff86da4d14f3a96c9d /src/error.c
parente2052e8c5f9fa4dc50bdcf33ff96c6bcc71ca59e (diff)
downloadlatrace-2d6cc3704e7affeb56c798b39a942daca95385dc.tar.gz
latrace-2d6cc3704e7affeb56c798b39a942daca95385dc.tar.xz
latrace-2d6cc3704e7affeb56c798b39a942daca95385dc.zip
error simulation: application part
Diffstat (limited to 'src/error.c')
-rw-r--r--src/error.c660
1 files changed, 660 insertions, 0 deletions
diff --git a/src/error.c b/src/error.c
new file mode 100644
index 0000000..68cf302
--- /dev/null
+++ b/src/error.c
@@ -0,0 +1,660 @@
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+int lt_error_app(struct lt_config_app *cfg, struct lt_error_app *error_app)
+{
+ struct lt_error_app *app;
+ struct lt_error_app_go *go, *g;
+ struct lt_error_app_run *run, *r;
+
+ if (!error_app->prog ||
+ !error_app->dir)
+ return -EINVAL;
+
+ app = malloc(sizeof(*app));
+ if (!app)
+ return -ENOMEM;
+
+ /* XXX Just copy whatever we got from the bison app object,
+ * but afterwards we need to reinit all the lists, since
+ * it's based on the bison object.
+ * There's probably more sane way to have the app object
+ * dynamically allocated from the start.. let's wait for
+ * someone smart to do that ;) */
+
+ *app = *error_app;
+
+ lt_init_list_head(&app->head_run);
+ lt_init_list_head(&app->head_go);
+ lt_init_list_head(&app->list);
+
+ lt_list_for_each_entry_safe(run, r, &error_app->head_run, list_app) {
+ lt_list_del(&run->list_app);
+ lt_list_add_tail(&run->list_app, &app->head_run);
+ }
+
+ lt_list_for_each_entry_safe(go, g, &error_app->head_go, list) {
+ lt_list_del(&go->list);
+ lt_list_add_tail(&go->list, &app->head_go);
+ }
+
+ lt_list_add_tail(&app->list, &cfg->error_apps);
+
+ return 0;
+}
+
+int lt_error_set(struct lt_config_app *cfg, char *error)
+{
+ struct lt_error_app *app;
+ int found = 0;
+
+ lt_list_for_each_entry(app, &cfg->error_apps, list) {
+ if (!strcmp(error, app->name)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ lt_sh(cfg, error_sim) = 1;
+ cfg->error_app = app;
+ return 0;
+}
+
+#define LT_DIR_PERM (S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
+
+static int dir_get(struct lt_config_app *cfg, char *dir, int exist_check)
+{
+ struct stat st;
+
+ PRINT_VERBOSE(cfg, 1, "exist_check %d, dir '%s'\n", exist_check, dir);
+
+ if (!stat(dir, &st)) {
+ if (exist_check) {
+ printf("failed: directory already exists '%s'\n", dir);
+ return -EEXIST;
+ }
+
+ /* TODO permission check */
+ return 0;
+ }
+
+ if (mkdir(dir, LT_DIR_PERM)) {
+ PRINT_VERBOSE(cfg, 1, "failed to create directory '%s'\n",
+ dir);
+ /* XXX some sensiblle return value ;) */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dir_base_name(struct lt_config_app *cfg,
+ char *buf, int len,
+ char *dir, char *prog)
+{
+ char file[LT_MAXFILE];
+ char *last_bs;
+ int size;
+
+ PRINT_VERBOSE(cfg, 1, "dir '%s', prog '%s'\n", dir, prog);
+
+ last_bs = strrchr(prog, '/');
+ if (last_bs) {
+ /* move behind '/' */
+ last_bs++;
+
+ /* endup with '/' isnt nice */
+ if (!strlen(last_bs))
+ return 0;
+
+ size = snprintf(file, LT_MAXFILE, "%s", last_bs);
+ } else
+ size = snprintf(file, LT_MAXFILE, "%s", prog);
+
+ PRINT_VERBOSE(cfg, 1, "file '%s'\n", file);
+
+ if (size == LT_MAXFILE)
+ return 0;
+
+ size = snprintf(buf, len, "%s/latrace-%s/", dir, file);
+ if (size == len)
+ return 0;
+
+ PRINT_VERBOSE(cfg, 1, "main dir '%s'\n", buf);
+ return size;
+}
+
+static int dir_base(struct lt_config_app *cfg, struct lt_error_app *app, char *dir)
+{
+ int size;
+ int ret;
+
+ /* We dont create parents, just one level..
+ * whatever pass mkdir ;) */
+ if (dir_get(cfg, app->dir, 0))
+ return -1;
+
+ size = dir_base_name(cfg, dir, LT_MAXFILE, app->dir, app->prog);
+ if (!size)
+ return -1;
+
+ if (dir_get(cfg, dir, 0))
+ return -1;
+
+ size = snprintf(dir + size , LT_MAXFILE, "latest");
+ if (size == LT_MAXFILE)
+ return -1;
+
+ ret = dir_get(cfg, dir, 1);
+ if (ret == -EEXIST) {
+ /* TODO move latest */
+ return -1;
+ } else if (ret != 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dir_go(struct lt_config_app *cfg,
+ char *dir, char *dir_base,
+ struct lt_error_app_go *go)
+{
+ int size;
+
+ size = snprintf(dir, LT_MAXFILE, "%s/GO-%s/", dir_base, go->name);
+ if (size == LT_MAXFILE)
+ return -1;
+
+ if (dir_get(cfg, dir, 0))
+ return -1;
+
+ return 0;
+}
+
+static int dir_run(struct lt_config_app *cfg,
+ char *dir, char *dir_base,
+ struct lt_error_app_run *run, int n)
+{
+ int size;
+
+ size = snprintf(dir, LT_MAXFILE, "%s/RUN-%s-%05d/", dir_base, run->name, n);
+ if (size == LT_MAXFILE)
+ return -1;
+
+ if (dir_get(cfg, dir, 0))
+ return -1;
+
+ return 0;
+}
+
+static int prepare_config_trace(struct lt_config_app *cfg, char *dir)
+{
+ FILE *f;
+
+ snprintf(lt_sh(cfg, output), LT_MAXFILE, "%s/trace", dir);
+
+ PRINT_VERBOSE(cfg, 1, "trace file %s\n", lt_sh(cfg, output));
+
+ /* If the pipe mode is on, the latrace itself is doing the
+ * the trace output to the file, we need to open the output
+ * file each run */
+ if (lt_sh(cfg, pipe)) {
+
+ f = fopen(lt_sh(cfg, output), "w");
+ if (!f) {
+ perror("pipe mode: failed to open output file");
+ return -1;
+ }
+
+ lt_sh(cfg, fout) = f;
+ PRINT_VERBOSE(cfg, 1, "trace file pipe enabled\n");
+ }
+
+ return 0;
+}
+
+static int prepare_config_stat(struct lt_config_app *cfg, char *dir)
+{
+ char stat_file[LT_MAXFILE];
+ FILE *f;
+
+ snprintf(stat_file, LT_MAXFILE, "%s/stat", dir);
+ f = fopen(stat_file, "w");
+ if (!f) {
+ perror("failed to open stat file");
+ return -1;
+ }
+
+ cfg->fstat = f;
+
+ PRINT_VERBOSE(cfg, 1, "stat file %s\n", stat_file);
+ return 0;
+}
+
+static int prepare_config_tty(struct lt_config_app *cfg, char *dir)
+{
+ cfg->output_tty = 1;
+ cfg->output_tty_fd = -1;
+ snprintf(cfg->output_tty_file, LT_MAXFILE, "%s/tty-output", dir);
+
+ PRINT_VERBOSE(cfg, 1, "tty output file %s\n", cfg->output_tty_file);
+ return 0;
+}
+
+static int prepare_config_args(struct lt_config_app *cfg,
+ struct lt_error_app *app,
+ struct lt_error_app_run *run)
+{
+ int i = 1, size;
+ char **arg = run->args;
+#define BUFSIZE 4096
+ char buf[BUFSIZE];
+
+ cfg->prog = app->prog;
+ cfg->arg[0] = app->prog;
+
+ size = snprintf(buf, BUFSIZE, "%s", app->prog);
+
+ while(arg && *arg && (i < LT_NUM_ARG)) {
+ PRINT_VERBOSE(cfg, 1, "arg: %s\n", *arg);
+ size += snprintf(buf + size, BUFSIZE, " %s", *arg);
+ cfg->arg[i++] = *arg++;
+ }
+
+ PRINT_VERBOSE(cfg, 1, "args: %s\n", buf);
+ return 0;
+}
+
+static int prepare_config_error(struct lt_config_app *cfg,
+ struct lt_error_app_run *run,
+ int n)
+{
+ struct lt_error_app_return *ret;
+ struct lt_error_def *error_def = &lt_sh(cfg, error_def);
+ int i = 0;
+
+ bzero(error_def, sizeof(struct lt_error_def));
+
+ lt_list_for_each_entry(ret, &run->head_return, list_run) {
+ struct lt_error_app_return_sym *sym;
+
+ lt_list_for_each_entry(sym, &ret->head_sym, list) {
+ struct lt_error_def_sym *sym_def;
+
+ sym_def = &error_def->sym[i];
+ strncpy(sym_def->symbol, sym->name, LT_MAXNAME);
+ sym_def->ret = sym->val;
+ sym_def->filter.type = ret->filter.type;
+
+ PRINT_VERBOSE(cfg, 1, "symbol %s, ret %s\n",
+ sym->name, sym->val);
+
+ if (i++ >= LT_ERROR_MAXSYM)
+ return -1;
+ }
+ }
+
+ lt_sh(cfg, error_def.n) = n;
+ PRINT_VERBOSE(cfg, 1, "N = %d\n", n);
+ return 0;
+}
+
+static int prepare_config(struct lt_config_app *cfg, char *dir,
+ struct lt_error_app *app,
+ struct lt_error_app_run *run, int n)
+{
+ PRINT_VERBOSE(cfg, 1, "dir '%s'\n", dir);
+
+ if (prepare_config_trace(cfg, dir))
+ return -1;
+
+ if (prepare_config_stat(cfg, dir))
+ return -1;
+
+ if (prepare_config_tty(cfg, dir))
+ return -1;
+
+ if (prepare_config_args(cfg, app, run))
+ return -1;
+
+ if (prepare_config_error(cfg, run, n))
+ return -1;
+
+ return 0;
+}
+
+static void post_config(struct lt_config_app *cfg)
+{
+ if ((lt_sh(cfg, pipe)) && (*lt_sh(cfg, output)))
+ fclose(lt_sh(cfg, fout));
+
+ fclose(cfg->fstat);
+}
+
+static int process_run(struct lt_config_app *cfg,
+ char *dir_base,
+ struct lt_error_app *app,
+ struct lt_error_app_go *go,
+ struct lt_error_app_run *run,
+ int n)
+{
+ static char dir[LT_MAXFILE];
+
+ if (dir_run(cfg, dir, dir_base, run, n))
+ return -1;
+
+ PRINT_VERBOSE(cfg, 1, "dir '%s'\n", dir);
+
+ if (prepare_config(cfg, dir, app, run, n))
+ return -1;
+
+ lt_run(cfg);
+
+ post_config(cfg);
+
+ return 0;
+}
+
+static int process_go(struct lt_config_app *cfg, char *dir_base,
+ struct lt_error_app *app,
+ struct lt_error_app_go *go)
+{
+ static char dir[LT_MAXFILE];
+ int i;
+
+ if (dir_go(cfg, dir, dir_base, go))
+ return -1;
+
+ PRINT_VERBOSE(cfg, 1, "dir '%s'\n", dir);
+
+ for(i = 0; i < go->n; i++) {
+ struct lt_error_app_run *run;
+
+ lt_list_for_each_entry(run, &go->head_run, list_go)
+ if (process_run(cfg, dir, app, go, run, i))
+ return -1;
+ }
+
+ return 0;
+}
+
+int lt_error_run(struct lt_config_app *cfg)
+{
+ struct lt_error_app *app = cfg->error_app;
+ struct lt_error_app_go *go;
+ static char dir[LT_MAXFILE];
+
+ printf("latrace error simulation [%s]\n", app->name);
+
+ if (dir_base(cfg, app, dir))
+ return -1;
+
+ PRINT_VERBOSE(cfg, 1, "dir '%s'\n", dir);
+
+ lt_list_for_each_entry(go, &app->head_go, list) {
+ if (process_go(cfg, dir, app, go))
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct lt_error_app_run *app_run_get(struct lt_config_app *cfg,
+ struct lt_error_app_run **run)
+{
+
+ PRINT_VERBOSE(cfg, 1, "run %p\n", *run);
+
+ if (!*run) {
+ struct lt_error_app_run *app_run;
+
+ app_run = malloc(sizeof(*app_run));
+ if (!app_run)
+ return NULL;
+
+ bzero(app_run, sizeof(*app_run));
+ lt_init_list_head(&app_run->head_return);
+ lt_init_list_head(&app_run->list_go);
+ lt_init_list_head(&app_run->list_app);
+
+ *run = app_run;
+ PRINT_VERBOSE(cfg, 1, "new %p\n", *run);
+ }
+
+ return *run;
+}
+
+static struct lt_error_app_return*
+find_return(struct lt_config_app *cfg,
+ struct lt_error_app *error_app,
+ char *name)
+{
+ struct lt_error_app_return *ret;
+
+ PRINT_VERBOSE(cfg, 1, "looking for %s\n", name);
+
+ lt_list_for_each_entry(ret, &error_app->head_return, list_app) {
+
+ PRINT_VERBOSE(cfg, 2, "got %s\n", ret->name);
+
+ if (!strcmp(name, ret->name)) {
+ PRINT_VERBOSE(cfg, 2, "FOUND\n");
+ return ret;
+ }
+ }
+
+ PRINT_VERBOSE(cfg, 1, "NOT FOUND\n");
+ return NULL;
+}
+
+int lt_error_run_return(struct lt_config_app *cfg,
+ struct lt_error_app_run **run,
+ struct lt_error_app *error_app,
+ struct lt_list_head *rets)
+{
+ struct lt_error_app_run *app_run = app_run_get(cfg, run);
+ struct lt_config_ln *ln, *h;
+
+ if (!app_run)
+ return -ENOMEM;
+
+ PRINT_VERBOSE(cfg, 1, "run %s\n", app_run->name);
+
+ lt_list_for_each_entry_safe(ln, h, rets, list) {
+ struct lt_error_app_return *ret;
+
+ ret = find_return(cfg, error_app, ln->name);
+ if (!ret)
+ return -EINVAL;
+
+ PRINT_VERBOSE(cfg, 1, "return %s, empty %d\n",
+ ret->name, lt_list_empty(&ret->list_run));
+
+ /* TODO Allow more than 1 RUN assignment to GO,
+ * so far only one assignment is allowed. */
+ if (!lt_list_empty(&ret->list_run))
+ return -EINVAL;
+
+ lt_list_add_tail(&ret->list_run, &app_run->head_return);
+
+ lt_list_del(&ln->list);
+ free(ln->name);
+ free(ln);
+ }
+
+ return 0;
+}
+
+int lt_error_run_args(struct lt_config_app *cfg,
+ struct lt_error_app_run **run,
+ struct lt_list_head *head)
+{
+ struct lt_error_app_run *app_run = app_run_get(cfg, run);
+ char **args = NULL;
+
+ if (!app_run)
+ return -ENOMEM;
+
+ /* only one ARGS definition perf RUN */
+ if (app_run->args)
+ return -EINVAL;
+
+ args = lt_config_ln_fill_array(head);
+ if (!args)
+ return -ENOMEM;
+
+ PRINT_VERBOSE(cfg, 1, "args %p\n", args);
+
+ app_run->args = args;
+ return 0;
+}
+
+static struct lt_error_app_run *find_run(struct lt_config_app *cfg,
+ struct lt_error_app *error_app,
+ char *name)
+{
+ struct lt_error_app_run *run;
+
+ PRINT_VERBOSE(cfg, 1, "looking for %s\n", name);
+
+ lt_list_for_each_entry(run, &error_app->head_run, list_app) {
+
+ PRINT_VERBOSE(cfg, 2, "got %s\n", run->name);
+
+ if (!strcmp(name, run->name)) {
+ PRINT_VERBOSE(cfg, 2, "FOUND\n");
+ return run;
+ }
+ }
+
+ PRINT_VERBOSE(cfg, 1, "NOT FOUND\n");
+ return NULL;
+}
+
+int lt_error_go(struct lt_config_app *cfg,
+ struct lt_error_app_go **go,
+ struct lt_error_app *error_app,
+ char *name, int n,
+ struct lt_list_head *runs)
+{
+ struct lt_error_app_go *app_go = *go;
+ struct lt_config_ln *ln, *h;
+
+ if (app_go)
+ return -EINVAL;
+
+ app_go = malloc(sizeof(*app_go));
+ bzero(app_go, sizeof(*app_go));
+ lt_init_list_head(&app_go->head_run);
+ lt_init_list_head(&app_go->list);
+
+ app_go->n = n;
+ app_go->name = name;
+
+ lt_list_for_each_entry_safe(ln, h, runs, list) {
+ struct lt_error_app_run *run;
+
+ run = find_run(cfg, error_app, ln->name);
+ if (!run)
+ return -EINVAL;
+
+ PRINT_VERBOSE(cfg, 1, "run %s, empty %d\n",
+ run->name, lt_list_empty(&run->list_go));
+
+ /* TODO Allow more than 1 RUN assignment to GO,
+ * so far only one assignment is allowed. */
+ if (!lt_list_empty(&run->list_go))
+ return -EINVAL;
+
+ lt_list_add_tail(&run->list_go, &app_go->head_run);
+
+ lt_list_del(&ln->list);
+ free(ln->name);
+ free(ln);
+ }
+
+ *go = app_go;
+ return 0;
+}
+
+static struct lt_error_app_return*
+app_return_get(struct lt_config_app *cfg, struct lt_error_app_return **ret)
+{
+ if (!*ret) {
+ struct lt_error_app_return *app_return;
+
+ app_return = malloc(sizeof(*app_return));
+ if (!app_return)
+ return NULL;
+
+ bzero(app_return, sizeof(*app_return));
+ lt_init_list_head(&app_return->head_sym);
+ lt_init_list_head(&app_return->list_app);
+ lt_init_list_head(&app_return->list_run);
+
+ *ret = app_return;
+ PRINT_VERBOSE(cfg, 1, "new %p\n", *ret);
+ }
+
+ return *ret;
+}
+
+int lt_error_return_ass(struct lt_config_app *cfg,
+ struct lt_error_app_return **ret,
+ char *name, unsigned long val)
+{
+ struct lt_error_app_return *app_ret = app_return_get(cfg, ret);
+ struct lt_error_app_return_sym *sym;
+
+ if (!app_ret)
+ return -ENOMEM;
+
+ sym = malloc(sizeof(*sym));
+ if (!sym)
+ return -ENOMEM;
+
+ sym->name = strdup(name);
+ sym->val = val;
+
+ PRINT_VERBOSE(cfg, 1, "%s = %ld\n", sym->name, val);
+
+ lt_list_add_tail(&sym->list, &app_ret->head_sym);
+ return 0;
+}
+
+int lt_error_return_filter(struct lt_config_app *cfg,
+ struct lt_error_app_return **ret,
+ int type, void *data)
+{
+ struct lt_error_app_return *app_ret = app_return_get(cfg, ret);
+
+ if (!app_ret)
+ return -ENOMEM;
+
+ app_ret->filter.type = type;
+ PRINT_VERBOSE(cfg, 1, "type %d\n", type);
+ return 0;
+}
+
+
+int lt_error_app_init(struct lt_error_app *app)
+{
+ bzero(app, sizeof(*app));
+ lt_init_list_head(&app->head_run);
+ lt_init_list_head(&app->head_go);
+ lt_init_list_head(&app->head_return);
+ lt_init_list_head(&app->list);
+ return 0;
+}