diff options
| author | jolsa@redhat.com <jolsa@redhat.com> | 2011-11-01 16:37:03 +0100 |
|---|---|---|
| committer | Jiri Olsa <Jiri Olsa jolsa@redhat.com> | 2011-11-13 11:06:37 +0100 |
| commit | 78a2fe366cf23632dd561e34d849e7fceaefd017 (patch) | |
| tree | 2e7d692053cb9305262d9058b029aff6547e502b /src | |
| parent | bd92a1f2212b5f5cfbc69971067e8d1116e59add (diff) | |
| download | latrace-78a2fe366cf23632dd561e34d849e7fceaefd017.tar.gz latrace-78a2fe366cf23632dd561e34d849e7fceaefd017.tar.xz latrace-78a2fe366cf23632dd561e34d849e7fceaefd017.zip | |
error simulation: automated changes
Diffstat (limited to 'src')
| -rw-r--r-- | src/audit-error.c | 63 | ||||
| -rw-r--r-- | src/audit.c | 7 | ||||
| -rw-r--r-- | src/config.c | 2 | ||||
| -rw-r--r-- | src/config.h | 8 | ||||
| -rw-r--r-- | src/error.c | 289 | ||||
| -rw-r--r-- | src/error.h | 4 | ||||
| -rw-r--r-- | src/fifo.c | 4 | ||||
| -rw-r--r-- | src/list.h | 3 |
8 files changed, 334 insertions, 46 deletions
diff --git a/src/audit-error.c b/src/audit-error.c index 3824d18..2ece447 100644 --- a/src/audit-error.c +++ b/src/audit-error.c @@ -46,7 +46,7 @@ struct lt_error_sym* lt_error_sym_get(struct lt_config_shared *cfg, extern __thread int pipe_fd; static void share_info(struct lt_config_audit *cfg, - struct lt_error_sym *esym, + long ret, int keep, struct timeval *tv, struct link_map *lr, const La_regs *inregs) @@ -58,8 +58,12 @@ static void share_info(struct lt_config_audit *cfg, lt_error_get_retaddr(cfg, &ret_addr, inregs); ret_addr -= lr->l_addr; - snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld, ret %p", - esym->ret, ret_addr); + if (keep) + snprintf(text, LT_MAXMSG, "ERROR SIMULATION inserted %ld, ret %p", + ret, ret_addr); + else + snprintf(text, LT_MAXMSG, "ERROR SIMULATION no change, ret %p", + ret_addr); if (lt_sh(cfg, pipe)) { char buf[LT_FIFO_MSG_MAXLEN]; @@ -111,15 +115,20 @@ static int install_sigsegv(struct lt_config_shared *cfg) return sigaction(SIGSEGV, &act, NULL); } +static __thread unsigned long automated_sym_cnt = 0; + int lt_error_sym_exit(struct lt_config_audit *cfg, struct lt_symbol *sym, struct timeval *tv, struct link_map *lr, const La_regs *inregs, - La_retval *outregs) + La_retval *outregs, + long *info) { struct lt_error_sym *esym; struct lt_error_config *cfg_err = cfg->error_config; + int keep = 0; + long ret; /* we're not interested in this symbol */ if (!sym || !sym->error) @@ -130,19 +139,39 @@ int lt_error_sym_exit(struct lt_config_audit *cfg, PRINT_VERBOSE(cfg, 1, "symbol %s, call %lu, n %ld\n", sym->name, esym->call, cfg_err->n); - /* we are not interested in this call number */ - if (esym->call++ != cfg_err->n) - return -1; + if (cfg_err->automated) { + struct lt_error_config_sym *cfg_sym; + + if (automated_sym_cnt >= cfg_err->sym_cnt) + return -1; + + cfg_sym = &cfg_err->sym[automated_sym_cnt]; + ret = cfg_sym->ret; + keep = cfg_sym->keep; - PRINT_VERBOSE(cfg, 1, "changing retval to %lu\n", - esym->ret); + *info = automated_sym_cnt++; + } else { + /* we are not interested in this call number */ + if (esym->call++ != cfg_err->n) + return -1; + + ret = esym->ret; + } + + if (keep) + PRINT_VERBOSE(cfg, 1, "keeping the return value\n", + ret); + else + PRINT_VERBOSE(cfg, 1, "changing retval to %lu\n", + ret); /* The fun begins right now.. let's change the return * value for the symbol ... */ - lt_error_set_retval(cfg, esym->ret, outregs); + if (!keep) + lt_error_set_retval(cfg, ret, outregs); /* and share some info about the sinner */ - share_info(cfg, esym, tv, lr, inregs); + share_info(cfg, ret, keep, tv, lr, inregs); if (esym->handle_sigsegv && install_sigsegv(cfg->sh)) PRINT_VERBOSE(cfg, 1, "failed to install SIGSEGV handler\n"); @@ -151,6 +180,7 @@ int lt_error_sym_exit(struct lt_config_audit *cfg, } static int symbol_add(struct lt_config_audit *cfg, + struct lt_error_config *cfg_err, struct lt_error_config_sym *cfg_sym) { ENTRY e, *ep; @@ -170,6 +200,15 @@ static int symbol_add(struct lt_config_audit *cfg, PRINT_VERBOSE(cfg, 1, "symbol table initialized\n"); } + e.key = cfg_sym->symbol; + if (!hsearch_r(e, FIND, &ep, &symbol_tab)) { + PRINT_VERBOSE(cfg, 1, "symbol %s already in the table\n", + cfg_sym->symbol); + /* This is a bug in normal processing, + * but completelly ok in automated mode. */ + return cfg_err->automated ? 0 : -1; + } + sym = malloc(sizeof(*sym)); if (!sym) { PRINT_VERBOSE(cfg, 1, "failed to allocate error symbol\n"); @@ -209,7 +248,7 @@ int lt_error_init(struct lt_config_audit *cfg) cfg_sym = &cfg_err->sym[i]; - if (symbol_add(cfg, cfg_sym)) { + if (symbol_add(cfg, cfg_err, cfg_sym)) { ok = 0; break; } diff --git a/src/audit.c b/src/audit.c index 7a83b56..4488d24 100644 --- a/src/audit.c +++ b/src/audit.c @@ -114,7 +114,7 @@ static int sym_entry(const char *symname, void *ptr, pipe_fd = lt_fifo_create(cfg.sh, cfg.dir); len = lt_fifo_msym_get(cfg.sh, buf, LT_FIFO_MTYPE_ENTRY, &tv, - (char*) symname, lib_to, argbuf, argdbuf); + (char*) symname, lib_to, argbuf, argdbuf, 0); free_argbuf(argret, argbuf, argdbuf); return lt_fifo_send(cfg.sh, pipe_fd, buf, len); @@ -141,6 +141,7 @@ static int sym_exit(const char *symname, void *ptr, char *argbuf = "", *argdbuf = ""; struct timeval tv; struct lt_symbol *sym = NULL; + long info = -1; PRINT_VERBOSE(&cfg, 2, "%s@%s\n", symname, lib_to); @@ -155,7 +156,7 @@ static int sym_exit(const char *symname, void *ptr, #ifdef CONFIG_ARCH_HAVE_ERROR_SIM if (lt_sh(&cfg, error_sim)) - lt_error_sym_exit(&cfg, sym, &tv, lr, inregs, outregs); + lt_error_sym_exit(&cfg, sym, &tv, lr, inregs, outregs, &info); #endif #ifdef CONFIG_ARCH_HAVE_ARGS @@ -169,7 +170,7 @@ static int sym_exit(const char *symname, void *ptr, int len; len = lt_fifo_msym_get(cfg.sh, buf, LT_FIFO_MTYPE_EXIT, &tv, - (char*) symname, lib_to, argbuf, argdbuf); + (char*) symname, lib_to, argbuf, argdbuf, info); free_argbuf(argret, argbuf, argdbuf); return lt_fifo_send(cfg.sh, pipe_fd, buf, len); diff --git a/src/config.c b/src/config.c index 9bde8a4..868f260 100644 --- a/src/config.c +++ b/src/config.c @@ -478,9 +478,11 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) cfg->output_tty_fd = -1; lt_init_list_head(&cfg->error_apps); +#ifdef CONFIG_ARCH_HAVE_ERROR_SIM lt_init_list_head(&cfg->process_funcs); lt_init_list_head(&cfg->error_symbols_all); lt_init_list_head(&cfg->error_symbols_current); +#endif /* read the default config file first */ if (read_config(cfg, LT_CONF_DIR "/latrace.conf")) { diff --git a/src/config.h b/src/config.h index 32d6511..ab71324 100644 --- a/src/config.h +++ b/src/config.h @@ -197,6 +197,8 @@ struct lt_config_app { struct lt_list_head error_symbols_all; struct lt_list_head error_symbols_current; + int error_automated_finished; + int error_automated_symbol_index; }; struct lt_config_ctl { @@ -297,6 +299,7 @@ struct lt_fifo_mbase { struct lt_fifo_msym { struct lt_fifo_mbase h; + long info; int sym; int lib; int arg; @@ -381,7 +384,7 @@ int lt_fifo_recv(struct lt_config_shared *cfg, struct lt_thread *t, void *buf, int bufsize); int lt_fifo_msym_get(struct lt_config_shared *cfg, char *buf, int type, struct timeval *tv, char *symname, char *libto, - char *arg, char *argd); + char *arg, char *argd, long info); int lt_fifo_mtext_get(struct lt_config_shared *cfg, char *buf, struct timeval *tv, char *text); @@ -456,7 +459,8 @@ int lt_error_sym_exit(struct lt_config_audit *cfg, struct timeval *tv, struct link_map *lr, const La_regs *inregs, - La_retval *outregs); + La_retval *outregs, + long *info); int lt_error_set_retval(struct lt_config_audit *cfg, unsigned long ret, La_retval *outregs); void lt_error_get_retaddr(struct lt_config_audit *cfg, diff --git a/src/error.c b/src/error.c index 6698ff8..ec457be 100644 --- a/src/error.c +++ b/src/error.c @@ -390,23 +390,22 @@ static int process_run(struct lt_config_app *cfg, return 0; } -static int process_automated_cb(struct lt_config_app *cfg, - struct lt_thread *t, - struct lt_fifo_mbase *mbase) -{ - return 0; -} - static int automated_get_all_syms(struct lt_config_app *cfg, struct lt_error_app_go *go) { struct lt_list_head *sym_all = &cfg->error_symbols_all; struct lt_error_app_run *run; - int count = 0; + int count = 0, runs = 1; lt_list_for_each_entry(run, &go->head_run, list_go) { struct lt_error_app_return *ret; + if (!runs--) { + PRINT_VERBOSE(cfg, 1, + "failed: supporting only single RUN within AUTOMATED go\n"); + return -1; + } + lt_list_for_each_entry(ret, &run->head_return, list_run) { struct lt_error_app_return_sym *sym; @@ -417,38 +416,270 @@ static int automated_get_all_syms(struct lt_config_app *cfg, } } + PRINT_VERBOSE(cfg, 1, "got %d symbols\n", count); return count; } -static int process_go_automated(struct lt_config_app *cfg, char *dir, - struct lt_error_app *app, - struct lt_error_app_go *go) +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 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; + + if (mbase->type != LT_FIFO_MTYPE_EXIT) + return 0; + + index = automated_get_index(cfg, mbase); + error_config = cfg->error_config; + + if (cfg->error_automated_symbol_index < error_config->sym_cnt) { + if (index != -1) + BUG_ON((cfg->error_automated_symbol_index + 1) != index); + cfg->error_automated_symbol_index = index; + return 0; + } + + symname = automated_get_symname(cfg, mbase); + sym = automated_find_symbol_all(cfg, symname); + if (!sym) + return 0; + + if (automated_add_symbol_current(cfg, sym)) + return -1; + + return 0; +} + +static int prepare_config_symbols_automated(struct lt_config_app *cfg) +{ + struct lt_list_head *sym_current = &cfg->error_symbols_current; + + 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_automated_finished = 1; + return 0; + } + + return 0; +} + +static int prepare_config_error_automated(struct lt_config_app *cfg, + struct lt_error_app_run *run) { - int ret = 0; + 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++; + + BUG_ON(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_config_sym)); + if (!cfg_err) + return -ENOMEM; + + cfg_err->sym_cnt = sym_cnt; + cfg_err->automated = 1; + + sym_cnt = 0; + + lt_list_for_each_entry(sym, sym_current, list) { + struct lt_error_config_sym *cfg_sym; + + cfg_sym = &cfg_err->sym[sym_cnt++]; + strncpy(cfg_sym->symbol, sym->name, LT_MAXNAME); + cfg_sym->ret = sym->vals[sym->val_idx]; + cfg_sym->keep = sym->keep; + cfg_sym->handle_sigsegv = sym->handle_sigsegv; - if (lt_process_register(cfg, process_automated_cb)) + PRINT_VERBOSE(cfg, 1, "symbol %s, ret %s\n", + sym->name, cfg_sym->ret); + } + + cfg->error_config = cfg_err; + return 0; +} + +static int prepare_config_automated(struct lt_config_app *cfg, + char *dir, + struct lt_error_app *app, + struct lt_error_app_run *run, + int n) +{ + if (prepare_config_trace(cfg, dir)) + return -1; + + if (prepare_config_stat(cfg, dir)) return -1; - if (!automated_get_all_syms(cfg, go)) { - PRINT_VERBOSE(cfg, 1, "no symbols defined\n"); + if (prepare_config_tty(cfg, dir)) return -1; + + if (prepare_config_args(cfg, app, run)) + return -1; + + /* omit the first run */ + if (n && prepare_config_symbols_automated(cfg)) + return -1; + + if (prepare_config_error_automated(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) +{ + post_config(cfg, app); + automated_dump_config(cfg, app, dir); + free(cfg->error_config); +} + +static int process_run_automated(struct lt_config_app *cfg, + char *dir_base, + struct lt_error_app *app, + struct lt_error_app_run *run) +{ + static char dir[LT_MAXFILE]; + int n = 0; + + if (lt_process_register(cfg, process_run_automated_cb)) + return -1; + + while(1) { + if (dir_run(cfg, dir, dir_base, run, n)) + break; + + if (prepare_config_automated(cfg, dir, app, run, n)) + break; + + if (cfg->error_automated_finished) + break; + + lt_run(cfg); + + post_config_automated(cfg, app, dir); + + n++; } - /* TODO - * 1) first run with no symbols - * 2) after each run add new symbol to the 'cur' list from global 'all' list - * 3) 'cur' list has fresh reallocated symbols giving the position and return - * for each symbol occurence - * 4) each 'cur' list symbol moves to new return value if all symbols 'below' it - * has changed all their return values - * 5) finish when first symbol cycles its all return values - * 6) repeating till there are new symbols (from 'all' list) visible - * - */ + BUG_ON(lt_process_unregister(cfg, process_run_automated_cb)); + + return cfg->error_automated_finished ? 0 : -1; +} + +static int process_go_automated(struct lt_config_app *cfg, char *dir, + struct lt_error_app *app, + struct lt_error_app_go *go) +{ + struct lt_error_app_run *run; + + if (!automated_get_all_syms(cfg, go) <= 0) + return -1; + + run = lt_list_first_entry(&go->head_run, struct lt_error_app_run, list_go); - BUG_ON(lt_process_unregister(cfg, process_automated_cb)); + if (process_run_automated(cfg, dir, app, run)) + PRINT_VERBOSE(cfg, 1, "failed: AUTOMATED run %s\n", run->name); - return ret; + return 0; } static int process_go_replay(struct lt_config_app *cfg, char *dir, @@ -765,6 +996,8 @@ int lt_error_return_ass(struct lt_config_app *cfg, 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); diff --git a/src/error.h b/src/error.h index d22ba4f..e60ef5b 100644 --- a/src/error.h +++ b/src/error.h @@ -25,6 +25,7 @@ struct lt_error_config_sym { struct lt_error_config { unsigned long n; + int automated; int sym_cnt; struct lt_error_config_sym sym[0]; }; @@ -55,6 +56,9 @@ struct lt_error_app_return_sym { long vals[LT_MAX_SYM_RETURNS]; }; + int val_idx; + int val_cnt; + /* When the symbol return is changed, * set SIGSEGV handlers. */ int handle_sigsegv; @@ -172,7 +172,7 @@ int lt_fifo_recv(struct lt_config_shared *cfg, struct lt_thread *t, void *buf, int lt_fifo_msym_get(struct lt_config_shared *cfg, char *buf, int type, struct timeval *tv, char *symname, char *libto, - char *arg, char *argd) + char *arg, char *argd, long info) { struct lt_fifo_msym *m = (struct lt_fifo_msym*) buf; int len_data, len = sizeof(struct lt_fifo_msym); @@ -182,6 +182,8 @@ int lt_fifo_msym_get(struct lt_config_shared *cfg, char *buf, int type, m->h.tid = (pid_t) syscall(SYS_gettid); m->h.tv = *tv; + m->info = info; + m->sym = 0; m->lib = strlen(symname); m->arg = m->lib + strlen(libto) + 1; @@ -93,6 +93,9 @@ static inline int lt_list_empty(struct lt_list_head *head) #define lt_list_first_entry(head, type, member) \ lt_list_entry((head)->next, type, member) +#define lt_list_last_entry(head, type, member) \ + lt_list_entry((head)->prev, type, member) + #define lt_list_for_each_entry(pos, head, member) \ for (pos = lt_list_entry((head)->next, typeof(*pos), member); \ &(pos)->member != (head); \ |
