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-13 15:49:02 +0100 |
commit | bf4e90d73c73d645e705d5fca68f9e8a41493784 (patch) | |
tree | 3f2db5ee4c50ca615f20e32b74d11d9b877c0b3b | |
parent | 0717d984e1c3596a82dcf302a9fd9c74993ee647 (diff) | |
download | latrace-bf4e90d73c73d645e705d5fca68f9e8a41493784.tar.gz latrace-bf4e90d73c73d645e705d5fca68f9e8a41493784.tar.xz latrace-bf4e90d73c73d645e705d5fca68f9e8a41493784.zip |
error simulation: application part
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | etc/latrace.d/latrace.conf.in | 69 | ||||
-rw-r--r-- | src/Makefile | 10 | ||||
-rw-r--r-- | src/audit-init.c | 1 | ||||
-rw-r--r-- | src/audit.c | 2 | ||||
-rw-r--r-- | src/autoconf.h.in | 3 | ||||
-rw-r--r-- | src/autoconf.make.in | 2 | ||||
-rw-r--r-- | src/config-bison.y | 170 | ||||
-rw-r--r-- | src/config-flex.l | 36 | ||||
-rw-r--r-- | src/config.c | 44 | ||||
-rw-r--r-- | src/config.h | 21 | ||||
-rw-r--r-- | src/error.c | 660 | ||||
-rw-r--r-- | src/error.h | 171 | ||||
-rw-r--r-- | src/fifo.c | 8 | ||||
-rw-r--r-- | src/latrace.c | 5 | ||||
-rw-r--r-- | src/symbol.c | 2 |
16 files changed, 1170 insertions, 43 deletions
diff --git a/configure.ac b/configure.ac index aeb951e..aa80b3a 100644 --- a/configure.ac +++ b/configure.ac @@ -103,6 +103,15 @@ else AC_MSG_WARN([No automated test support]) fi +# for following architectures we have error simulation support +if test "$unamem" = "x86_64" -o\ + "$unamem" = "i686"; then + AC_DEFINE(CONFIG_ARCH_HAVE_ERROR_SIM) + AC_SUBST(CONFIG_ARCH_HAVE_ERROR_SIM, "y") +else + AC_MSG_WARN([Error simulation support disabled]) +fi + AC_SEARCH_LIBS([cplus_demangle], [iberty_pic iberty], [AC_DEFINE(CONFIG_LIBERTY, 1, "Liberty found.")], [AC_MSG_WARN([libiberty not found, no demangle support (install binutils-dev)])]) diff --git a/etc/latrace.d/latrace.conf.in b/etc/latrace.d/latrace.conf.in index 2788b00..80c6247 100644 --- a/etc/latrace.d/latrace.conf.in +++ b/etc/latrace.d/latrace.conf.in @@ -96,3 +96,72 @@ OPTIONS { # function arguments - display string length and pointer value ARGS_STRING_POINTER_LENGTH = NO } + +# error simulation +# +#ERROR krava { +# DIR /krava/ +# PROG krava +# +# RETURN R1 { +# malloc = 100 +# krava = 1 +# FILTER NTH 10,12,14 +# } +# +# RETURN R2 { +# malloc = 100 FILTER SEQ +# krava = 1 FILTER NTH 10,1-5 +# } +# +# RETURN R3 { +# malloc = 100 +# krava = 1 +# FILTER STACK stack1 +# } +# +# RETURN R3 { +# malloc = 100 +# krava = 1 +# FILTER ARGS krava ARG a = 10 +# } +# +# RETURN R4 { +# malloc = 100 +# krava = 1 +# FILTER INTERACTIVE +# } +# +# RUN run1 { +# RETURN R1 +# ARGS +# } +# +# RUN run2 { +# RETURN R1 +# RETURN R2 +# ARGS +# } +# +# RUN run3 { +# RETURN R1 +# RETURN R2 +# ARGS a a a +# } +# +# GO go1 N=100 run1,run2 +# GO go2 run3 +#} +# +# directory structure +# ------------------- +# DIR/latrace-PROG +# - created based on DIR/PROG settings +# DIR/latrace-PROG/latest +# - created by latest run instance +# DIR/latrace-PROG/latest-TIME +# - moved from current latest dir +# DIR/latrace-PROG/latest/GO/ +# - created by processed GO +# DIR/latrace-PROG/latest/GO/RUN-N +# - created by processed RUN plus N value diff --git a/src/Makefile b/src/Makefile index 169a5f3..6b849d3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -72,13 +72,19 @@ LATRACE_OBJS=\ src/lib-include.o \ src/tty.o +ifeq ($(CONFIG_ARCH_HAVE_ERROR_SIM),y) +LATRACE_OBJS+=\ + src/error.o +endif + OBJS+=$(LATRACE_OBJS) PROGRAMS+=$(LATRACE_BIN) CPPFLAGS+=-DCONFIG_LIBDIR=\"$(libdir)\" CPPFLAGS+=-DLT_CONF_DIR=\"$(sysconfdir)/latrace.d\" CPPFLAGS+=-DLT_CONF_HEADERS_DIR=\"$(sysconfdir)/latrace.d/headers\" CPPFLAGS+=-DLT_CONF_HEADERS_FILE=\"$(sysconfdir)/latrace.d/headers/latrace.h\" -# no dependency for flex and bison definitions +CPPFLAGS+=-DLT_CONF_ERRORS_DIR=\"$(sysconfdir)/latrace.d/errors\" + OBJS_DEPS_OMIT+=\ src/config-bison.o \ src/config-flex.o @@ -93,4 +99,6 @@ install:: clean:: $(call remove, src/config-bison.[ch] src/config-flex.c) $(call remove, src/args-bison.[ch] src/args-flex.c) + $(call remove, src/audit-bison.[ch] src/audit-flex.c) $(call remove, src/config-bison.output src/args-bison.output) + $(call remove, src/audit-bison.output) diff --git a/src/audit-init.c b/src/audit-init.c index f36fee3..c3f96a4 100644 --- a/src/audit-init.c +++ b/src/audit-init.c @@ -255,6 +255,7 @@ int audit_init(int argc, char **argv, char **env) /* enable global symbols if needed */ lt_sh(&cfg, global_symbols) = lt_sh(&cfg, args_enabled); + PRINT_VERBOSE(&cfg, 1, "global_symbols %d\n", lt_sh(&cfg, global_symbols)); cfg.init_ok = 1; diff --git a/src/audit.c b/src/audit.c index 6f46e22..40e67ee 100644 --- a/src/audit.c +++ b/src/audit.c @@ -37,7 +37,7 @@ extern struct lt_config_audit cfg; -static __thread int pipe_fd = 0; +__thread int pipe_fd = 0; static __thread int flow_below_stack = 0; static __thread int indent_depth = 0; diff --git a/src/autoconf.h.in b/src/autoconf.h.in index 6d698a7..d2f53fb 100644 --- a/src/autoconf.h.in +++ b/src/autoconf.h.in @@ -30,6 +30,9 @@ /* Argument display support. */ #undef CONFIG_ARCH_HAVE_ARGS +/* Error simulation support. */ +#undef CONFIG_ARCH_HAVE_ERROR_SIM + /* liberty */ #undef CONFIG_LIBERTY diff --git a/src/autoconf.make.in b/src/autoconf.make.in index a49c37e..9655f4a 100644 --- a/src/autoconf.make.in +++ b/src/autoconf.make.in @@ -47,3 +47,5 @@ CONFIG_SYSDEP_DIR = @CONFIG_SYSDEP_DIR@ CONFIG_VERSION = @CONFIG_VERSION@ CONFIG_ARCH_HAVE_ARGS = @CONFIG_ARCH_HAVE_ARGS@ CONFIG_ARCH_HAVE_TEST = @CONFIG_ARCH_HAVE_TEST@ + +CONFIG_ARCH_HAVE_ERROR_SIM = @CONFIG_ARCH_HAVE_ERROR_SIM@ diff --git a/src/config-bison.y b/src/config-bison.y index 944f59b..14ee5a3 100644 --- a/src/config-bison.y +++ b/src/config-bison.y @@ -21,8 +21,9 @@ %name-prefix "lt_config_" %{ -#include <stdlib.h> +#include <strings.h> #include <string.h> +#include <stdlib.h> #include "config.h" #include "lib-include.h" @@ -33,7 +34,7 @@ static struct lt_config_app *scfg; int lt_config_lex(void); void lt_config_error(const char *m); -#define ERROR(fmt, args...) \ +#define ABORT(fmt, args...) \ do { \ char ebuf[1024]; \ sprintf(ebuf, fmt, ## args); \ @@ -42,13 +43,17 @@ do { \ } while(0) static LT_LIST_HEAD(opt_list); +static struct lt_error_app error_app; +static struct lt_error_app_run *error_app_run; +static struct lt_error_app_go *error_app_go; +static struct lt_error_app_return *error_app_return; #define OPTION_ADD(idx, sval, nval) \ do { \ struct lt_config_opt *opt; \ opt = lt_config_opt_new(scfg, idx, sval, nval); \ if (!opt) \ - ERROR("failed to process option\n"); \ + ABORT("failed to process option\n"); \ lt_list_add_tail(&opt->list, &opt_list); \ } while(0) @@ -66,6 +71,9 @@ static struct lt_list_head ln_names; %token OPT_LIBS OPT_LIBS_TO OPT_LIBS_FROM %token OPT_SYM OPT_SYM_OMIT OPT_SYM_BELOW OPT_SYM_NOEXIT %token OPT_ARGS_STRING_POINTER_LENGTH +%token ERROR +%token ERR_DO ERR_DIR ERR_RUN ERR_GO ERR_RETURN ERR_N +%token ERR_PROG ERR_ARGS ERR_FILTER ERR_SEQ %union { @@ -79,9 +87,11 @@ static struct lt_list_head ln_names; %% entry: -entry include_def +entry include | -entry options_def +entry options +| +entry errors | entry END { @@ -91,18 +101,18 @@ entry END | /* left blank intentionally */ -include_def: INCLUDE '"' NAME '"' +include: INCLUDE '"' NAME '"' { if (lt_inc_open(scfg->sh, lt_config_sinc, $3)) - ERROR("failed to process include"); + ABORT("failed to process include"); } -options_def: OPTIONS '{' OPTIONS_DEF '}' +options: OPTIONS '{' OPTIONS_DEF '}' { struct lt_config_opt *opt, *opth; if (lt_config_opt_process(scfg, &opt_list)) - ERROR("failed to process options"); + ABORT("failed to process options"); lt_list_for_each_entry_safe(opt, opth, &opt_list, list) { lt_list_del(&opt->list); @@ -185,8 +195,8 @@ OPTIONS_DEF OPT_LIBS '=' list_names_comma { char libs[LT_LIBS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, libs, LT_LIBS_MAXSIZE)) - ERROR("failed to process libs option"); + if (lt_config_ln_fill_buf(&ln_names, libs, LT_LIBS_MAXSIZE)) + ABORT("failed to process libs option"); OPTION_ADD(LT_OPT_LIBS, libs, -1); } @@ -195,8 +205,8 @@ OPTIONS_DEF OPT_LIBS_TO '=' list_names_comma { char libs_to[LT_LIBS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, libs_to, LT_LIBS_MAXSIZE)) - ERROR("failed to process libs_to option"); + if (lt_config_ln_fill_buf(&ln_names, libs_to, LT_LIBS_MAXSIZE)) + ABORT("failed to process libs_to option"); OPTION_ADD(LT_OPT_LIBS_TO, libs_to, -1); } @@ -205,8 +215,8 @@ OPTIONS_DEF OPT_LIBS_FROM '=' list_names_comma { char libs_from[LT_LIBS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, libs_from, LT_LIBS_MAXSIZE)) - ERROR("failed to process libs_from option"); + if (lt_config_ln_fill_buf(&ln_names, libs_from, LT_LIBS_MAXSIZE)) + ABORT("failed to process libs_from option"); OPTION_ADD(LT_OPT_LIBS_FROM, libs_from, -1); } @@ -215,8 +225,8 @@ OPTIONS_DEF OPT_SYM '=' list_names_comma { char sym[LT_LIBS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, sym, LT_LIBS_MAXSIZE)) - ERROR("failed to process sym option"); + if (lt_config_ln_fill_buf(&ln_names, sym, LT_LIBS_MAXSIZE)) + ABORT("failed to process sym option"); OPTION_ADD(LT_OPT_SYM, sym, -1); } @@ -225,8 +235,8 @@ OPTIONS_DEF OPT_SYM_OMIT '=' list_names_comma { char sym_omit[LT_SYMBOLS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, sym_omit, LT_SYMBOLS_MAXSIZE)) - ERROR("failed to process sym_omit option"); + if (lt_config_ln_fill_buf(&ln_names, sym_omit, LT_SYMBOLS_MAXSIZE)) + ABORT("failed to process sym_omit option"); OPTION_ADD(LT_OPT_SYM_OMIT, sym_omit, -1); } @@ -235,8 +245,8 @@ OPTIONS_DEF OPT_SYM_BELOW '=' list_names_comma { char sym_below[LT_SYMBOLS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, sym_below, LT_SYMBOLS_MAXSIZE)) - ERROR("failed to process sym_below option"); + if (lt_config_ln_fill_buf(&ln_names, sym_below, LT_SYMBOLS_MAXSIZE)) + ABORT("failed to process sym_below option"); OPTION_ADD(LT_OPT_SYM_BELOW, sym_below, -1); } @@ -245,8 +255,8 @@ OPTIONS_DEF OPT_SYM_NOEXIT '=' list_names_comma { char sym_noexit[LT_SYMBOLS_MAXSIZE]; - if (lt_config_ln_fill(&ln_names, sym_noexit, LT_SYMBOLS_MAXSIZE)) - ERROR("failed to process sym_below option"); + if (lt_config_ln_fill_buf(&ln_names, sym_noexit, LT_SYMBOLS_MAXSIZE)) + ABORT("failed to process sym_below option"); OPTION_ADD(LT_OPT_SYM_NOEXIT, sym_noexit, -1); } @@ -262,21 +272,125 @@ list_names_comma: list_names_comma ',' NAME { if (lt_config_ln_add(&ln_names, $3)) - ERROR("failed to add list name"); + ABORT("failed to add list name"); } | NAME { if (lt_config_ln_add(&ln_names, $1)) - ERROR("failed to add list name"); + ABORT("failed to add list name"); +} + +errors: ERROR NAME '{' error_def '}' +{ + error_app.name = strdup($2); + + if (lt_error_app(scfg, &error_app)) + ABORT("failed to add error"); + + lt_error_app_init(&error_app); +} + +error_def: +error_def error_run +{ + lt_list_add_tail(&error_app_run->list_app, &error_app.head_run); + error_app_run = NULL; +} +| +error_def error_go +{ + lt_list_add_tail(&error_app_go->list, &error_app.head_go); + error_app_go = NULL; +} +| +error_def error_return +{ + lt_list_add_tail(&error_app_return->list_app, &error_app.head_return); + error_app_return = NULL; +} +| +error_def ERR_DIR NAME +{ + error_app.dir = strdup($3); +} +| +error_def ERR_PROG NAME +{ + if (error_app.prog) + ABORT("only one PROG clause allowed"); + + error_app.prog = strdup($3); +} +| +/* left blank intentionally */ + + +error_return: ERR_RETURN NAME '{' error_return_def '}' +{ + error_app_return->name = strdup($2); +} + +error_return_def: +error_return_def NAME '=' VALUE +{ + if (lt_error_return_ass(scfg, &error_app_return, $2, $4)) + ABORT("failed to add symbol to return definition"); +} +| +error_return_def ERR_FILTER ERR_SEQ +{ + if (lt_error_return_filter(scfg, &error_app_return, + LT_ERROR_FILTER_TYPE_SEQ, NULL)) + ABORT("failed to add filter to return definition"); +} +| +/* left blank intentionally */ + + +error_run: ERR_RUN NAME '{' error_run_def '}' +{ + error_app_run->name = strdup($2); +} + +error_run_def: +error_run_def ERR_ARGS list_names_comma +{ + if (lt_error_run_args(scfg, &error_app_run, &ln_names)) + ABORT("failed to add run"); +} +| +error_run_def ERR_RETURN list_names_comma +{ + if (lt_error_run_return(scfg, &error_app_run, &error_app, &ln_names)) + ABORT("failed to add run"); +} +| + +error_go: +ERR_GO NAME list_names_comma +{ + if (lt_error_go(scfg, &error_app_go, &error_app, + strdup($2), 1, &ln_names)) + ABORT("failed to add go"); +} +| +ERR_GO NAME ERR_N '=' VALUE list_names_comma +{ + if (lt_error_go(scfg, &error_app_go, &error_app, + strdup($2), $5, &ln_names)) + ABORT("failed to add go"); } %% int lt_config_parse_init(struct lt_config_app *cfg, struct lt_include *inc) { - scfg = cfg; - lt_config_sinc = inc; + scfg = cfg; + lt_config_sinc = inc; lt_init_list_head(&ln_names); - return 0; + lt_error_app_init(&error_app); + error_app_run = NULL; + error_app_go = NULL; + return 0; } diff --git a/src/config-flex.l b/src/config-flex.l index 433992f..3b761d7 100644 --- a/src/config-flex.l +++ b/src/config-flex.l @@ -30,6 +30,7 @@ extern struct lt_include *lt_config_sinc; +static int block_cnt; #define NEW_LINE() \ do { \ @@ -46,6 +47,17 @@ do { \ lt_config_lval.l = atol(lt_config_text); return token; \ } while(0) +#define BLOCK_BEGIN() \ +do { \ + block_cnt++; \ +} while(0) + +#define BLOCK_END() \ +do { \ + if (!--block_cnt) \ + BEGIN(INITIAL); \ +} while(0) + %} num [-0-9] @@ -54,7 +66,7 @@ name ([-0-9a-zA-Z\./_\-\*])+ bool YES|NO comment ^([\s\t])*#.* -%x comment include options +%x comment include options error %% @@ -106,6 +118,28 @@ OPTIONS { BEGIN(options); return OPTIONS; } <options>"\n" { NEW_LINE(); } <options>. { ; } +ERROR { BEGIN(error); return ERROR; } +<error>DIR { return ERR_DIR; } +<error>RUN { return ERR_RUN; } +<error>GO { return ERR_GO; } +<error>RETURN { return ERR_RETURN; } +<error>PROG { return ERR_PROG; } +<error>ARGS { return ERR_ARGS; } +<error>N { return ERR_N; } +<error>FILTER { return ERR_FILTER; } +<error>SEQ { return ERR_SEQ; } + +<error>{value} { RETURN_LONG(VALUE); } +<error>{name} { RETURN_STR(NAME); } +<error>{comment} { ; } +<error>"}" { BLOCK_END(); return '}'; } +<error>"{" { BLOCK_BEGIN(); return '{'; } +<error>"=" { return '='; } +<error>"\"" { return '"'; } +<error>"\\" { ; } +<error>"," { return ','; } +<error>"\n" { NEW_LINE(); } +<error>. { ; } %% #ifndef yywrap diff --git a/src/config.c b/src/config.c index 9fea08b..b04ec2c 100644 --- a/src/config.c +++ b/src/config.c @@ -87,6 +87,8 @@ static void usage() printf(" -R, --ctl-config controled config\n"); printf(" -q, --disable disable auditing\n"); printf("\n"); + printf(" -e, --error error set error simulation\n"); + printf("\n"); printf(" -v, --verbose verbose output\n"); printf(" -V, --version display version\n"); printf(" -h, --help display help\n"); @@ -392,7 +394,7 @@ int lt_config_ln_add(struct lt_list_head *head, char *name) return 0; } -int lt_config_ln_fill(struct lt_list_head *head, char *buf, int size) +int lt_config_ln_fill_buf(struct lt_list_head *head, char *buf, int size) { struct lt_config_ln *ln, *n; int first = 1; @@ -422,6 +424,30 @@ int lt_config_ln_fill(struct lt_list_head *head, char *buf, int size) return 0; } +char **lt_config_ln_fill_array(struct lt_list_head *head) +{ + struct lt_config_ln *ln, *n; + char **array = NULL; + int i = 0; + + lt_list_for_each_entry_safe(ln, n, head, list) { + + /* starting from zero index and keeping NULL + * at the end of the args array*/ + array = realloc(array, sizeof(*array) * (i + 2)); + if (!array) + return NULL; + + array[i++] = ln->name; + array[i] = NULL; + + lt_list_del(&ln->list); + free(ln); + } + + return array; +} + int lt_config(struct lt_config_app *cfg, int argc, char **argv) { memset(cfg, 0, sizeof(*cfg)); @@ -439,6 +465,7 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) cfg->csort = LT_CSORT_CALL; cfg->fstat = stdout; cfg->output_tty_fd = -1; + lt_init_list_head(&cfg->error_apps); lt_init_list_head(&cfg->process_funcs); @@ -480,12 +507,13 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) {"no-follow-exec", no_argument, 0, 'E'}, {"disable", no_argument, 0, 'q'}, {"ctl-config", no_argument, 0, 'R'}, + {"error", required_argument, 0, 'e'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "+s:n:l:t:f:vhi:BdISb:cC:y:YL:po:a:N:ADVTFERq", + c = getopt_long(argc, argv, "+s:n:l:t:f:vhi:BdISb:cC:y:YL:po:a:N:ADVTFERe:q", long_options, &option_index); if (c == -1) @@ -643,6 +671,16 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) lt_sh(cfg, ctl_config) = 1; break; + #ifndef CONFIG_ARCH_HAVE_ERROR_SIM + case 'e': + printf("Error simulation support not compiled in"); + #else + case 'e': + if (lt_error_set(cfg, optarg)) + usage(); + #endif /* CONFIG_ARCH_HAVE_ERROR_SIM */ + break; + case 'V': version(); break; @@ -667,7 +705,7 @@ int lt_config(struct lt_config_app *cfg, int argc, char **argv) cfg->arg_num = i_arg; } - if (!cfg->prog) { + if ((!cfg->prog) && (!lt_sh(cfg, error_sim))) { printf("failed: no program specified\n"); usage(); } diff --git a/src/config.h b/src/config.h index 4ab0886..a47a4da 100644 --- a/src/config.h +++ b/src/config.h @@ -31,6 +31,7 @@ #include "audit.h" #include "list.h" +#include "error.h" #ifdef CONFIG_ARCH_HAVE_ARGS #include "args.h" @@ -152,10 +153,14 @@ struct lt_config_shared { int framesize_check; unsigned int framesize; int global_symbols; + int error_sim; /* for 'not_follow_fork' */ pid_t pid; + /* error definition (error_sim = 1) */ + struct lt_error_def error_def; + /* XXX feel like an idiot.. find another way!!! */ struct lt_config_shared *sh; }; @@ -187,6 +192,11 @@ struct lt_config_app { int notify_fd_watch; struct lt_list_head process_funcs; + + /* current error */ + struct lt_error_app *error_app; + /* list of defined errors */ + struct lt_list_head error_apps; }; struct lt_config_ctl { @@ -325,7 +335,8 @@ struct lt_thread { }; struct lt_symbol { - struct lt_args_sym *args; + struct lt_args_sym *args; + struct lt_error_sym *error; /* symbol name */ const char *name; @@ -406,7 +417,8 @@ struct lt_config_opt *lt_config_opt_new(struct lt_config_app *cfg, int idx, char *sval, long nval); int lt_config_opt_process(struct lt_config_app *cfg, struct lt_list_head *list); int lt_config_ln_add(struct lt_list_head *head, char *name); -int lt_config_ln_fill(struct lt_list_head *head, char *buf, int size); +int lt_config_ln_fill_buf(struct lt_list_head *head, char *buf, int size); +char **lt_config_ln_fill_array(struct lt_list_head *head); /* tty */ int tty_master(struct lt_config_app *cfg); @@ -418,6 +430,11 @@ void tty_close(struct lt_config_app *cfg); /* process functions registration */ int lt_process_register(struct lt_config_app *cfg, lt_process_cb cb); +/* error simulation app */ +int lt_error_app(struct lt_config_app *cfg, struct lt_error_app *error_app); +int lt_error_set(struct lt_config_app *cfg, char *error); +int lt_error_run(struct lt_config_app *cfg); + #define PRINT(fmt, args...) \ do { \ char lpbuf[1024]; \ 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; +} diff --git a/src/error.h b/src/error.h new file mode 100644 index 0000000..5c44633 --- /dev/null +++ b/src/error.h @@ -0,0 +1,171 @@ +#ifndef ERROR_H +#define ERROR_H + +enum { + LT_ERROR_FILTER_TYPE_NTH, + LT_ERROR_FILTER_TYPE_SEQ, + LT_ERROR_FILTER_TYPE_STACK, + LT_ERROR_FILTER_TYPE_INTERACTIVE, +}; + +struct lt_error_def_filter { + int type; +}; + +struct lt_error_def_sym { +#define LT_MAXNAME 20 + char symbol[LT_MAXNAME]; + long ret; + struct lt_error_def_filter filter; +}; + +struct lt_error_def { +#define LT_ERROR_MAXSYM 50 + struct lt_error_def_sym sym[LT_ERROR_MAXSYM]; + + /* call number */ + unsigned long n; +}; + +struct lt_error_audit_filter { + int type; + union { + int **nth; + }; +}; + +struct lt_error_sym { + char *name; + unsigned long ret; + unsigned long call; + struct lt_error_audit_filter filter; + + struct lt_list_head list; +}; + +struct lt_error_app_return_sym { + char *name; + long val; + + struct lt_list_head list; +}; + +struct lt_error_app_filter { + int type; +}; + +struct lt_error_app_return { + char *name; + struct lt_error_app_filter filter; + + struct lt_list_head head_sym; + struct lt_list_head list_app; + struct lt_list_head list_run; +}; + +struct lt_error_app_run { + char *name; + char **args; + + struct lt_list_head head_return; + struct lt_list_head list_go; + struct lt_list_head list_app; +}; + +struct lt_error_app_go { + int n; + char *name; + + struct lt_list_head head_run; + struct lt_list_head list; +}; + +struct lt_error_app { + char *name; + char *dir; + char *prog; + + struct lt_list_head head_run; + struct lt_list_head head_go; + struct lt_list_head head_return; + struct lt_list_head list; +}; + +struct lt_config_app; + +#ifdef CONFIG_ARCH_HAVE_ERROR_SIM +int lt_error_app(struct lt_config_app *cfg, + struct lt_error_app *error_app); +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); +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 *ret); +int lt_error_run_args(struct lt_config_app *cfg, + struct lt_error_app_run **run, + struct lt_list_head *args); +int lt_error_return_ass(struct lt_config_app *cfg, + struct lt_error_app_return **ret, + char *name, unsigned long val); +int lt_error_return_filter(struct lt_config_app *cfg, + struct lt_error_app_return **ret, + int type, void *data); + +int lt_error_app_init(struct lt_error_app *app); +#else +static inline int lt_error_app(struct lt_config_app *cfg, + struct lt_error_app *error_app) +{ + return -1; +} + +static inline 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) +{ + return -1; +} + +static inline 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 *ret) +{ + return -1; +} + +static inline int lt_error_run_args(struct lt_config_app *cfg, + struct lt_error_app_run **run, + struct lt_list_head *args) +{ + return -1; +} + +static inline int lt_error_app_init(struct lt_error_app *app) +{ + return -1; +} + +static inline int lt_error_return_ass(struct lt_config_app *cfg, + struct lt_error_app_return **ret, + char *name, unsigned long val) +{ + return -1; +} + +static inline int lt_error_return_filter(struct lt_config_app *cfg, + struct lt_error_app_return **ret, + int type, void *data) +{ + return -1; +} + +#endif + +#endif /* ERROR_H */ @@ -29,6 +29,7 @@ #include <fcntl.h> #include <string.h> #include <sys/inotify.h> +#include <errno.h> #include "config.h" @@ -36,17 +37,12 @@ static char *get_notify_dir(char *dir) { static char notify_dir[LT_MAXFILE]; - static int initialized = 0; int s; - if (initialized) - return notify_dir; - s = snprintf(notify_dir, LT_MAXFILE, "%s/fifo", dir); if (s >= LT_MAXFILE) return NULL; - initialized = 1; return notify_dir; } @@ -93,7 +89,7 @@ int lt_fifo_notify_init(struct lt_config_app *cfg, char *dir) struct stat st; if (stat(notify_dir, &st)) { - if (mkdir(notify_dir, S_IRWXU)) { + if (mkdir(notify_dir, S_IRWXU) && (errno != EEXIST)) { perror("mkdir failed"); return -1; } diff --git a/src/latrace.c b/src/latrace.c index 6c45338..19d0bfd 100644 --- a/src/latrace.c +++ b/src/latrace.c @@ -71,6 +71,11 @@ static int main_latrace(int argc, char **argv) if (-1 == lt_config(&cfg, argc, argv)) return -1; +#ifdef CONFIG_ARCH_HAVE_ERROR_SIM + if (lt_sh(&cfg, error_sim)) + return lt_error_run(&cfg); +#endif + ret = lt_run(&cfg); if ((lt_sh(&cfg, pipe)) && (*lt_sh(&cfg, output))) diff --git a/src/symbol.c b/src/symbol.c index df7d169..f10045a 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -73,7 +73,7 @@ static int compare(const void *a, const void *b) static int symbol_init(struct lt_config_shared *cfg, struct lt_symbol *sym, const char *name) { - struct lt_args_sym* a = NULL; + struct lt_args_sym *a = NULL; if (lt_sh(cfg, args_enabled)) { a = lt_args_sym_get(cfg, name); |