diff options
author | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2011-10-18 10:00:38 +0200 |
---|---|---|
committer | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2011-11-24 21:20:27 +0100 |
commit | 2d6cc3704e7affeb56c798b39a942daca95385dc (patch) | |
tree | dd339c0bbc9305fa4352deff86da4d14f3a96c9d /src/error.c | |
parent | e2052e8c5f9fa4dc50bdcf33ff96c6bcc71ca59e (diff) | |
download | latrace-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.c | 660 |
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 = <_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; +} |