#include #include #include #include #include #include #include #include #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) 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 size; size = snprintf(dir, LT_MAXFILE, "%s/RUN-%s/", dir_base, run->name); if (size == LT_MAXFILE) return -1; if (dir_get(cfg, dir, 0)) return -1; return 0; } static int dir_round(struct lt_config_app *cfg, char *dir, char *dir_run, struct lt_error_app_run *run, unsigned long round) { int size; size = snprintf(dir, LT_MAXFILE, "%s/%lu/", dir_run, round); 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 get_symbols_all(struct lt_config_app *cfg, struct lt_error_app_run *run) { struct lt_list_head *sym_all = &cfg->error_symbols_all; struct lt_error_app_return *ret; int count = 0; lt_list_for_each_entry(ret, &run->head_return, list_run) { struct lt_error_app_return_sym *sym, *n; lt_list_for_each_entry_safe(sym, n, &ret->head_sym, list) { lt_list_move_tail(&sym->list, sym_all); count++; } } PRINT_VERBOSE(cfg, 1, "got %d symbols\n", count); return count; } long automated_get_index(struct lt_config_app *cfg, struct lt_fifo_mbase *mbase) { struct lt_fifo_msym *msym = (struct lt_fifo_msym*) mbase; return msym->info; } static char *automated_get_symname(struct lt_config_app *cfg, struct lt_fifo_mbase *mbase) { struct lt_fifo_msym *msym = (struct lt_fifo_msym*) mbase; return msym->data + msym->sym; } static struct lt_error_app_return_sym* automated_find_symbol_all(struct lt_config_app *cfg, char *symname) { struct lt_error_app_return_sym *sym; struct lt_list_head *sym_all = &cfg->error_symbols_all; /* TODO hash, hash, hash... and hash again .. pleaeease ;) hash! */ lt_list_for_each_entry(sym, sym_all, list) if (!strcmp(sym->name, symname)) return sym; return NULL; } static int automated_add_symbol_current(struct lt_config_app *cfg, struct lt_error_app_return_sym *sym_orig) { struct lt_error_app_return_sym *sym; sym = malloc(sizeof(*sym)); if (!sym) return -1; *sym = *sym_orig; sym->keep = 1; sym->val_idx = 0; lt_list_add_tail(&sym->list, &cfg->error_symbols_current); return 0; } static int prepare_config_symbols_automated(struct lt_config_app *cfg, struct lt_error_app_run *run, unsigned long round) { struct lt_list_head *sym_current = &cfg->error_symbols_current; cfg->error_automated_symbol_index = -1; if (!round) { int cnt; cnt = get_symbols_all(cfg, run); if (cnt <= 0) return -EINVAL; cfg->error_symbols_all_cnt = cnt; return 0; } while (!lt_list_empty(sym_current)) { struct lt_error_app_return_sym *last; last = lt_list_last_entry(sym_current, struct lt_error_app_return_sym, list); PRINT_VERBOSE(cfg, 1, "last %s\n", last->name); if (last->keep) { int idx = last->val_idx; last->keep = 0; PRINT_VERBOSE(cfg, 1, "from keep to vals[%d] = %llu\n", idx, last->vals[idx]); break; } else { int idx = last->val_idx + 1; if (idx < last->val_cnt) { last->val_idx = idx; PRINT_VERBOSE(cfg, 1, "from vals[%d] = %llu to vals[%d] = %llu\n", idx - 1, last->vals[idx - 1], idx, last->vals[idx]); break; } PRINT_VERBOSE(cfg, 1, "removing\n"); lt_list_del(&last->list); } } /* nothing more to try, we are done */ if (lt_list_empty(sym_current)) { PRINT_VERBOSE(cfg, 1, "FINISHED\n"); cfg->error_finished = 1; return 0; } return 0; } static int prepare_config_symbols_replay(struct lt_config_app *cfg) { return 0; } static int prepare_config_symbols_seq(struct lt_config_app *cfg, unsigned long round) { return 0; } static int prepare_config_symbols(struct lt_config_app *cfg, struct lt_error_app_run *run, unsigned long round) { switch(run->type) { case LT_ERROR_RUN_TYPE_SEQ: return prepare_config_symbols_seq(cfg, round); case LT_ERROR_RUN_TYPE_AUTO: return prepare_config_symbols_automated(cfg, run, round); case LT_ERROR_RUN_TYPE_REPLAY: return prepare_config_symbols_replay(cfg); } BUG(); return -EINVAL; } static int find_error_symbol(struct lt_config_app *cfg, char *name, int len) { char *names = cfg->error_symbols; int size_names = cfg->error_symbols_size_names; char *p; PRINT_VERBOSE(cfg, 1, "name %s\n", name); while(size_names) { if (!strncmp(names, name, len)) { int idx = names - cfg->error_symbols; PRINT_VERBOSE(cfg, 1, "found idx %d\n", idx); return names - cfg->error_symbols; } p = memchr(names, 0, size_names); size_names -= ++p - names; names = p; } PRINT_VERBOSE(cfg, 1, "not found\n"); return -1; } static void display_error_symbols(struct lt_config_app *cfg) { char *names = cfg->error_symbols; int size_names = cfg->error_symbols_size_names; char *p; PRINT_VERBOSE(cfg, 1, "START\n"); while(size_names) { PRINT_VERBOSE(cfg, 1, "name %s\n", names); p = memchr(names, 0, size_names); size_names -= ++p - names; names = p; } PRINT_VERBOSE(cfg, 1, "END\n"); } static int add_error_symbol(struct lt_config_app *cfg, char *name) { char *names = cfg->error_symbols; int len = strlen(name); int size_names = cfg->error_symbols_size_names; int size_total = cfg->error_symbols_size_total; int index; PRINT_VERBOSE(cfg, 1, "name %s\n", name); display_error_symbols(cfg); if (!names) { names = malloc(1000); if (!names) return -ENOMEM; PRINT_VERBOSE(cfg, 1, "initial allocation\n"); cfg->error_symbols = names; size_names = cfg->error_symbols_size_names = 0; size_total = cfg->error_symbols_size_total = 1000; } index = find_error_symbol(cfg, name, len); if (index >= 0) return index; /* +1 for ZERO byte */ if (len + size_names + 1 >= size_total) { size_total += 1000; names = realloc(names, size_total); if (!names) return -ENOMEM; PRINT_VERBOSE(cfg, 1, "reallocation size %d\n", size_total); cfg->error_symbols = names; cfg->error_symbols_size_total = size_total; } index = size_names; strncpy(names + size_names, name, len); size_names += len; names[size_names++] = 0; cfg->error_symbols_size_names = size_names; PRINT_VERBOSE(cfg, 1, "added %s, index %d\n", name, index); display_error_symbols(cfg); return index; } static int prepare_config_error(struct lt_config_app *cfg, struct lt_error_app_run *run) { struct lt_list_head *sym_current = &cfg->error_symbols_current; struct lt_error_app_return_sym *sym; struct lt_error_config *cfg_err; int sym_cnt = 0; lt_list_for_each_entry(sym, sym_current, list) sym_cnt++; PRINT_VERBOSE(cfg, 1, "number of symbols %d\n", sym_cnt); cfg_err = malloc(sizeof(*cfg_err) + sym_cnt * sizeof(struct lt_error_sym)); if (!cfg_err) return -ENOMEM; memset(cfg_err, 0, sizeof(*cfg_err)); cfg_err->sym_cnt = sym_cnt; cfg_err->type = run->type; sym_cnt = 0; lt_list_for_each_entry(sym, sym_current, list) { struct lt_error_sym *cfg_sym; int index; cfg_sym = &cfg_err->sym[sym_cnt++]; index = add_error_symbol(cfg, sym->name); if (index < 0) return -ENOMEM; cfg_sym->name.index = index; cfg_sym->ret = sym->vals[sym->val_idx]; cfg_sym->keep = sym->keep; cfg_sym->handle_sigsegv = sym->handle_sigsegv; PRINT_VERBOSE(cfg, 1, "symbol %s, keep %d, ret %llu\n", sym->name, cfg_sym->keep, cfg_sym->ret); } cfg_err->names_size = cfg->error_symbols_size_names; cfg->error_config = cfg_err; return 0; } static int prepare_config(struct lt_config_app *cfg, char *dir, struct lt_error_app *app, struct lt_error_app_run *run, unsigned long round) { if (!app->no_storage) { 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_symbols(cfg, run, round)) return -1; if (prepare_config_error(cfg, run)) return -1; return 0; } static void automated_dump_config(struct lt_config_app *cfg, struct lt_error_app *app, char *dir) { /* dump config to be replayed with REPLAY */ } static void post_config_automated(struct lt_config_app *cfg, struct lt_error_app *app, char *dir) { automated_dump_config(cfg, app, dir); } static void post_config(struct lt_config_app *cfg, struct lt_error_app *app, struct lt_error_app_run *run, char *dir) { switch(run->type) { case LT_ERROR_RUN_TYPE_AUTO: return post_config_automated(cfg, app, dir); } if (!app->no_storage) { if ((lt_sh(cfg, pipe)) && (*lt_sh(cfg, output))) fclose(lt_sh(cfg, fout)); fclose(cfg->fstat); } free(cfg->error_config); free(cfg->error_symbols); cfg->error_config = NULL; cfg->error_symbols = NULL; cfg->error_symbols_size_names = 0; cfg->error_symbols_size_total = 0; } static int process_run_automated_cb(struct lt_config_app *cfg, struct lt_thread *t, struct lt_fifo_mbase *mbase) { struct lt_error_app_return_sym *sym; struct lt_error_config *error_config; char *symname; long index; PRINT_VERBOSE(cfg, 1, "type %d\n", mbase->type); if (mbase->type != LT_FIFO_MTYPE_EXIT) return 0; index = automated_get_index(cfg, mbase); error_config = cfg->error_config; PRINT_VERBOSE(cfg, 1, "index %d, error_automated_symbol_index %d, sym_cnt %d\n", index, cfg->error_automated_symbol_index, error_config->sym_cnt); symname = automated_get_symname(cfg, mbase); PRINT_VERBOSE(cfg, 1, "symname %s\n", symname); if (cfg->error_automated_symbol_index < (error_config->sym_cnt - 1)) { if (index != -1) cfg->error_automated_symbol_index = index; return 0; } sym = automated_find_symbol_all(cfg, symname); if (!sym) return 0; PRINT_VERBOSE(cfg, 1, "added %s\n", symname); if (automated_add_symbol_current(cfg, sym)) return -1; return 0; } static int process_run_cb(struct lt_config_app *cfg, struct lt_thread *t, struct lt_fifo_mbase *mbase) { /* TODO need a way to distiguish error type inside tracer */ return process_run_automated_cb(cfg, t, mbase); } static int process_run(struct lt_config_app *cfg, char *dir_base, struct lt_error_app *app, struct lt_error_app_run *run) { static char dir[LT_MAXFILE]; static char round[LT_MAXFILE]; unsigned long r = 0; if (!app->no_storage && dir_run(cfg, dir, dir_base, run)) return -1; PRINT_VERBOSE(cfg, 1, "dir '%s'\n", dir); if (lt_process_register(cfg, process_run_cb)) return -1; while(1) { int status; if (!app->no_storage && dir_round(cfg, round, dir, run, r)) return -1; printf("."); if (prepare_config(cfg, round, app, run, r)) break; if (cfg->error_finished) break; status = lt_run(cfg); if (WIFSIGNALED(status)) printf("\nround %lu killed by signal %d\n", r, WTERMSIG(status)); fflush(NULL); post_config(cfg, app, run, round); r++; }; printf("\n"); BUG_ON(lt_process_unregister(cfg, process_run_cb)); return cfg->error_finished ? 0 : -1; } 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]; struct lt_error_app_run *run; if (!app->no_storage && dir_go(cfg, dir, dir_base, go)) return -1; lt_list_for_each_entry(run, &go->head_run, list_go) if (process_run(cfg, dir, app, run)) 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 (!app->no_storage && 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 RETURN assignment to RUN, * 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_type(struct lt_config_app *cfg, struct lt_error_app_run **run, int type) { struct lt_error_app_run *app_run = app_run_get(cfg, run); if (!app_run) return -ENOMEM; app_run->type = type; 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, 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->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, struct lt_list_head *list_vals, int handle_sigsegv, int keep) { struct lt_error_app_return *app_ret = app_return_get(cfg, ret); struct lt_error_app_return_sym *sym; struct lt_config_ln *ln, *h; int vals_cnt = 0; if (!app_ret) return -ENOMEM; sym = malloc(sizeof(*sym)); if (!sym) return -ENOMEM; sym->name = strdup(name); sym->handle_sigsegv = handle_sigsegv; sym->keep = keep; /* process return values name list */ lt_list_for_each_entry_safe(ln, h, list_vals, list) { if (vals_cnt == LT_MAX_SYM_RETURNS) { PRINT_VERBOSE(cfg, 1, "failed: too many return values for (%d allowed) for symbol %s\n", LT_MAX_SYM_RETURNS, name); return -1; } BUG_ON(ln->type != LT_CONFIG_LN_VALUE); sym->vals[vals_cnt++] = ln->val; lt_list_del(&ln->list); free(ln); } sym->val_cnt = vals_cnt; PRINT_VERBOSE(cfg, 1, "%s = %ld, handle_sigsegv = %d, keep %d\n", sym->name, sym->val, handle_sigsegv, keep); if (vals_cnt > 1) { int i; for(i = 0; i < vals_cnt; i++) PRINT_VERBOSE(cfg, 1, "\tret[%d] = %lu\n", i, sym->vals[i]); } /* It's essential we keep the user * defined sequenece of symbols. */ 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); app->no_storage = 1; return 0; } int lt_error_config_write(struct lt_config_app *cfg, int fd) { struct lt_error_config *cfg_err = cfg->error_config; int len, size = sizeof(struct lt_error_config); BUG_ON(!cfg_err); size += cfg_err->sym_cnt * sizeof(struct lt_error_sym); PRINT_VERBOSE(cfg, 1, "sym_cnt %d\n", cfg_err->sym_cnt); if (size != write(fd, cfg_err, size)) { perror("write failed"); return -1; } PRINT_VERBOSE(cfg, 1, "writing names size %d\n", cfg->error_symbols_size_names); display_error_symbols(cfg); len = cfg->error_symbols_size_names; size = write(fd, cfg->error_symbols, len); if (size != len) { perror("write failed"); return -1; } PRINT_VERBOSE(cfg, 1, "OK\n"); return 0; }