summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJiri Olsa <jolsa@shell.devel.redhat.com>2009-09-04 02:29:08 -0400
committerJiri Olsa <jolsa@shell.devel.redhat.com>2009-09-04 02:29:08 -0400
commit04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5 (patch)
tree8e53039a4f5d1a4571000bc06214053261aebf8d /src
downloadlatrace-04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5.tar.gz
latrace-04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5.tar.xz
latrace-04f3fbbfdb3a5dd197dbc25ca18ad244f1fbf6a5.zip
initial commit - 0.5.7
Diffstat (limited to 'src')
-rw-r--r--src/Makefile67
-rw-r--r--src/args-bison.y302
-rw-r--r--src/args-flex.l89
-rw-r--r--src/args.c1066
-rw-r--r--src/audit-init.c196
-rw-r--r--src/audit.c287
-rw-r--r--src/audit.h102
-rw-r--r--src/autoconf.h.in36
-rw-r--r--src/autoconf.make.in42
-rw-r--r--src/config.c293
-rw-r--r--src/config.h417
-rw-r--r--src/fifo.c150
-rw-r--r--src/latrace.c45
-rw-r--r--src/list.h103
-rw-r--r--src/objsearch.c192
-rw-r--r--src/output.c125
-rw-r--r--src/run.c319
-rw-r--r--src/stats.c328
-rw-r--r--src/sysdeps/arm/stack.c269
-rw-r--r--src/sysdeps/i686/stack.c131
-rw-r--r--src/sysdeps/i686/stack.h27
-rw-r--r--src/sysdeps/x86_64/stack.c615
-rw-r--r--src/sysdeps/x86_64/stack.h90
-rw-r--r--src/thread.c61
24 files changed, 5352 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..93f01ad
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,67 @@
+# Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+#
+# This file is part of the latrace.
+#
+# The latrace is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The latrace is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the latrace (file COPYING). If not, see
+# <http://www.gnu.org/licenses/>.
+
+
+# libltaudit.so
+AUDIT_BIN=libltaudit.so.$(LT_VER)
+AUDIT_LDFLAGS="-Wl,-init=audit_init" "-Wl,-soname,$(AUDIT_BIN)" -fPIC -shared
+AUDIT_LIBS=-liberty
+AUDIT_OBJS=\
+ src/audit.o \
+ src/audit-init.o \
+ src/fifo.o \
+ src/args-bison.o \
+ src/args-flex.o \
+ src/args.o \
+ src/output.o \
+ src/objsearch.o \
+ src/sysdeps/$(CONFIG_SYSDEP_DIR)/stack.o
+
+OBJS+=$(AUDIT_OBJS)
+PROGRAMS+= $(AUDIT_BIN)
+
+$(AUDIT_BIN): $(AUDIT_OBJS)
+ $(QUIET_LD)$(CC) $(AUDIT_LDFLAGS) -o $@ $(AUDIT_OBJS) $(AUDIT_LIBS)
+
+install::
+ $(call install,$(AUDIT_BIN),$(libdir),755)
+
+# latrace binary
+LATRACE_BIN=latrace
+LATRACE_LIB=-liberty
+LATRACE_OBJS=\
+ src/latrace.o \
+ src/config.o \
+ src/run.o \
+ src/stats.o \
+ src/fifo.o \
+ src/thread.o \
+ src/output.o
+
+OBJS+=$(LATRACE_OBJS)
+PROGRAMS+=$(LATRACE_BIN)
+CPPFLAGS+=-DCONFIG_LIBDIR=\"$(libdir)\"
+CPPFLAGS+=-DLT_ARGS_DEF_DIR=\"$(confdir)\"
+CPPFLAGS+=-DLT_ARGS_DEF_CONF=\"$(sysconfdir)/latrace.conf\"
+
+$(LATRACE_BIN): $(LATRACE_OBJS)
+ $(QUIET_LD)$(CC) $(LDFLAGS) -o $@ $(LATRACE_OBJS) $(LATRACE_LIBS) $(LATRACE_LIB)
+
+install::
+ $(call install,$(LATRACE_BIN),$(bindir),755)
+
diff --git a/src/args-bison.y b/src/args-bison.y
new file mode 100644
index 0000000..64c88be
--- /dev/null
+++ b/src/args-bison.y
@@ -0,0 +1,302 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+%{
+
+#define YYERROR_VERBOSE 1
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+
+int yylex (void);
+void yyerror(const char *m);
+
+static struct lt_config_shared *scfg;
+static int struct_alive = 0;
+
+#define ERROR(fmt, args...) \
+do { \
+ char ebuf[1024]; \
+ sprintf(ebuf, fmt, ## args); \
+ yyerror(ebuf); \
+ YYERROR; \
+} while(0)
+
+#define CHK_TYPEDEF(ret, base, new, pointer) \
+do { \
+ switch(ret) { \
+ case -1: \
+ ERROR("unknown typedef - %s%s%s\n", base, (pointer ? "* " : " "), new); \
+ break; \
+ case 1: \
+ ERROR("typedef alrady defined - %s%s%s\n", base, (pointer ? "* " : " "), new); \
+ break; \
+ case 2: \
+ ERROR("typedef limit reached(%d) - %s%s%s\n", \
+ LT_ARGS_DEF_TYPEDEF_NUM, base, (pointer ? "* " : " "), new); \
+ break; \
+ }; \
+} while(0)
+
+#define GET_LIST_HEAD(head) \
+do { \
+ if (NULL == (head = (struct lt_list_head*) malloc(sizeof(*head)))) \
+ ERROR("failed to allocate list head"); \
+ lt_init_list_head(head); \
+} while(0)
+
+%}
+
+%token NAME FILENAME STRUCT ENUM TYPEDEF INCLUDE END POINTER
+
+%union
+{
+ char *s;
+ struct lt_arg *arg;
+ struct lt_enum_elem *enum_elem;
+ struct lt_list_head *head;
+}
+
+%type <s> NAME
+%type <s> FILENAME
+%type <head> STRUCT_DEF
+%type <head> ENUM_DEF
+%type <s> ENUM_REF
+%type <enum_elem> ENUM_ELEM
+%type <head> ARGS
+%type <arg> DEF
+
+%%
+entry:
+entry struct_def
+|
+entry enum_def
+|
+entry func_def
+|
+entry type_def
+|
+entry include_def
+|
+entry END
+{
+ if (lt_args_buf_close(scfg))
+ return 0;
+}
+|
+/* left blank intentionally */
+
+/* struct definitions */
+struct_def:
+STRUCT NAME '{' STRUCT_DEF '}' ';'
+{
+ switch(lt_args_add_struct(scfg, $2, $4)) {
+ case -1:
+ ERROR("failed to add struct %s\n", $2);
+ case 1:
+ ERROR("struct limit reached(%d) - %s\n", LT_ARGS_DEF_STRUCT_NUM, $2);
+ };
+
+ /* force creation of the new list head */
+ struct_alive = 0;
+}
+
+STRUCT_DEF:
+STRUCT_DEF DEF ';'
+{
+ struct lt_arg *def = $2;
+ struct lt_list_head *h = $1;
+
+ if (!struct_alive++)
+ GET_LIST_HEAD(h);
+
+ lt_list_add_tail(&def->args_list, h);
+ $$ = h;
+}
+| /* left blank intentionally,
+ XXX this could be done like the args_def, but user needs to be
+ able to create an empty structure, so thats why we play
+ with the global struct_alive thingie...
+ there could be better way probably */
+{
+}
+
+/* enum definitions */
+enum_def:
+ENUM NAME '{' ENUM_DEF '}' ';'
+{
+ switch(lt_args_add_enum(scfg, $2, $4)) {
+ case -1:
+ ERROR("failed to add enum %s\n", $2);
+ case 1:
+ ERROR("enum limit reached(%d) - %s\n", LT_ARGS_DEF_STRUCT_NUM, $2);
+ };
+}
+
+ENUM_DEF:
+ENUM_DEF ',' ENUM_ELEM
+{
+ struct lt_enum_elem *enum_elem = $3;
+ struct lt_list_head *h = $1;
+
+ lt_list_add_tail(&enum_elem->list, h);
+ $$ = h;
+}
+| ENUM_ELEM
+{
+ struct lt_list_head *h;
+ struct lt_enum_elem *enum_elem = $1;
+
+ GET_LIST_HEAD(h);
+ lt_list_add_tail(&enum_elem->list, h);
+ $$ = h;
+}
+
+ENUM_ELEM:
+NAME '=' NAME
+{
+ if (NULL == ($$ = lt_args_get_enum(scfg, $1, $3)))
+ ERROR("failed to add enum '%s = %s'\n", $1, $3);
+}
+|
+NAME
+{
+ if (NULL == ($$ = lt_args_get_enum(scfg, $1, NULL)))
+ ERROR("failed to add enum '%s = undef'\n", $1);
+}
+
+type_def:
+TYPEDEF NAME NAME ';'
+{
+ int ret = lt_args_add_typedef(scfg, $2, $3, 0);
+ CHK_TYPEDEF(ret, $2, $3, 0);
+}
+|
+TYPEDEF NAME POINTER NAME ';'
+{
+ int ret = lt_args_add_typedef(scfg, $2, $4, 1);
+ CHK_TYPEDEF(ret, $2, $4, 1);
+}
+
+/* function definitions */
+func_def:
+DEF '(' ARGS ')' ';'
+{
+ struct lt_arg *arg = $1;
+
+ if (lt_args_add_sym(scfg, arg, $3))
+ ERROR("failed to add symbol %s\n", arg->name);
+
+ /* force cration of the new list head */
+ $3 = NULL;
+}
+
+ARGS:
+ARGS ',' DEF
+{
+ struct lt_arg *def = $3;
+ struct lt_list_head *h = $1;
+
+ lt_list_add_tail(&def->args_list, h);
+ $$ = h;
+}
+| DEF
+{
+ struct lt_list_head *h;
+ struct lt_arg *def = $1;
+
+ GET_LIST_HEAD(h);
+ lt_list_add_tail(&def->args_list, h);
+ $$ = h;
+}
+| NAME
+{
+ GET_LIST_HEAD($$);
+}
+| /* left intentionaly blank */
+{
+ GET_LIST_HEAD($$);
+}
+
+DEF:
+NAME NAME ENUM_REF
+{
+ struct lt_arg *arg;
+
+ if (NULL == (arg = lt_args_getarg(scfg, $1, $2, 0, 1, $3)))
+ ERROR("unknown argument type - %s\n", $1);
+
+ $$ = arg;
+}
+|
+NAME POINTER NAME ENUM_REF
+{
+ struct lt_arg *arg;
+ if (NULL == (arg = lt_args_getarg(scfg, $1, $3, 1, 1, $4)))
+ ERROR("unknown argument type - %s\n", $1);
+
+ $$ = arg;
+}
+|
+STRUCT NAME NAME
+{
+ struct lt_arg *arg;
+ if (NULL == (arg = lt_args_getarg(scfg, $2, $3, 0, 1, NULL)))
+ ERROR("unknown argument type - %s\n", $2);
+
+ $$ = arg;
+}
+|
+STRUCT NAME POINTER NAME ENUM_REF
+{
+ struct lt_arg *arg;
+ if (NULL == (arg = lt_args_getarg(scfg, $2, $4, 1, 1, $5)))
+ ERROR("unknown argument type - %s\n", $2);
+
+ $$ = arg;
+}
+
+ENUM_REF:
+'=' NAME
+{
+ $$ = $2;
+}
+|
+{
+ $$ = NULL;
+}
+
+/* include definitions */
+include_def: INCLUDE '"' FILENAME '"'
+{
+ if (lt_args_buf_open(scfg, $3))
+ ERROR("failed to process include");
+}
+
+%%
+
+int lt_args_parse_init(struct lt_config_shared *cfg)
+{
+ scfg = cfg;
+ return 0;
+}
diff --git a/src/args-flex.l b/src/args-flex.l
new file mode 100644
index 0000000..e00b619
--- /dev/null
+++ b/src/args-flex.l
@@ -0,0 +1,89 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+%{
+
+#include <string.h>
+
+#include "config.h"
+#include "args-bison.h"
+
+struct lt_args_include* lt_args_buf_get(void);
+
+%}
+
+alphnum [-0-9a-zA-Z_]
+name ({alphnum})+
+filename ([-0-9a-zA-Z\./_])+
+
+%x comment include
+%%
+
+"/*" BEGIN(comment);
+<comment>[^*\n]* /* eat anything that's not a '*' */
+<comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<comment>\n { lt_args_buf_get()->lineno++; }
+<comment>"*"+"/" BEGIN(INITIAL);
+
+"#include" { BEGIN(include); return INCLUDE; }
+<include>{filename} { yylval.s = strdup(yytext); return FILENAME; }
+<include>"\"" { return '"'; }
+<include>\n { BEGIN(INITIAL); }
+<include>. { ; }
+
+"extern" { ; }
+"const" { ; }
+<<EOF>> { return END; }
+"struct" { return STRUCT; }
+"enum" { return ENUM; }
+"typedef" { return TYPEDEF; }
+{name} { yylval.s = strdup(yytext); return NAME; }
+"\*"+ { return POINTER; }
+")" { return ')'; }
+"(" { return '('; }
+"}" { return '}'; }
+"{" { return '{'; }
+";" { return ';'; }
+"," { return ','; }
+"=" { return '='; }
+\ { ; }
+\n { lt_args_buf_get()->lineno++; }
+. { ; }
+
+%%
+
+#ifndef yywrap
+int yywrap()
+{
+ return 1;
+ /* XXX not to get the compiler 'not used' warning */
+ yyunput(0, NULL);
+ input();
+}
+#endif
+
+void yyerror(const char *m)
+{
+ printf("latrace config file [%s] line %d: %s\n",
+ lt_args_buf_get()->file,
+ lt_args_buf_get()->lineno,
+ m);
+}
diff --git a/src/args.c b/src/args.c
new file mode 100644
index 0000000..4bb1252
--- /dev/null
+++ b/src/args.c
@@ -0,0 +1,1066 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "config.h"
+
+
+#define YY_BUF_SIZE 16384
+#define MAX_INCLUDE_DEPTH 10
+#define LT_EQUAL " = "
+
+
+extern int errno;
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+YY_BUFFER_STATE yy_create_buffer(FILE *file, int size);
+extern FILE *yyin;
+
+int yyparse();
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer);
+void yy_delete_buffer(YY_BUFFER_STATE b);
+
+int lt_args_parse_init(struct lt_config_shared *cfg);
+static struct lt_args_include include_stack[MAX_INCLUDE_DEPTH];
+static int include_stack_ptr = 0;
+static int enum_init = 0;
+
+
+/* hardcoded POD types */
+static struct lt_arg args_def_pod[] = {
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_VOID,
+ .type_len = sizeof(void),
+ .type_name = "void",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_SHORT,
+ .type_len = sizeof(short),
+ .type_name = "short",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_USHORT,
+ .type_len = sizeof(unsigned short),
+ .type_name = "u_short",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_INT,
+ .type_len = sizeof(int),
+ .type_name = "int",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_UINT,
+ .type_len = sizeof(unsigned int),
+ .type_name = "u_int",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_LONG,
+ .type_len = sizeof(long),
+ .type_name = "long",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_ULONG,
+ .type_len = sizeof(unsigned long),
+ .type_name = "u_long",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_CHAR,
+ .type_len = sizeof(char),
+ .type_name = "char",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_UCHAR,
+ .type_len = sizeof(unsigned char),
+ .type_name = "u_char",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_LLONG,
+ .type_len = sizeof(long long),
+ .type_name = "llong",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_ULLONG,
+ .type_len = sizeof(unsigned long long),
+ .type_name = "u_llong",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_DOUBLE,
+ .type_len = sizeof(double),
+ .type_name = "double",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+ {
+ .dtype = LT_ARGS_DTYPE_POD,
+ .type_id = LT_ARGS_TYPEID_FLOAT,
+ .type_len = sizeof(float),
+ .type_name = "float",
+ .pointer = 0,
+ .name = "",
+ .mmbcnt = 0,
+ .arch = NULL,
+ .en = NULL,
+ .args_head = NULL,
+ .args_list = { NULL, NULL }
+ },
+};
+
+#define LT_ARGS_DEF_POD_NUM (sizeof(args_def_pod)/sizeof(struct lt_arg))
+
+/* struct, typedef, enum */
+static struct lt_arg args_def_struct[LT_ARGS_DEF_STRUCT_NUM];
+static struct lt_arg args_def_typedef[LT_ARGS_DEF_TYPEDEF_NUM];
+static int args_def_struct_cnt = 0;
+static int args_def_typedef_cnt = 0;
+static struct hsearch_data args_enum_tab;
+
+static struct lt_enum* getenum(struct lt_config_shared *cfg, char *name)
+{
+ struct lt_enum *en;
+ ENTRY e, *ep;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "request for <%s>\n", name);
+
+ if (!enum_init) {
+ PRINT_VERBOSE(cfg->verbose, 1, "no enum added so far\n", name);
+ return NULL;
+ }
+
+ e.key = name;
+ hsearch_r(e, FIND, &ep, &args_enum_tab);
+
+ if (!ep) {
+ PRINT_VERBOSE(cfg->verbose, 1, "failed to find enum <%s>\n", name);
+ return NULL;
+ }
+
+ en = (struct lt_enum*) ep->data;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "found %p <%s>\n", en, en->name);
+ return en;
+}
+
+static int enum_comp(const void *ep1, const void *ep2)
+{
+ struct lt_enum_elem *e1 = (struct lt_enum_elem*) ep1;
+ struct lt_enum_elem *e2 = (struct lt_enum_elem*) ep2;
+
+ return e1->val - e2->val;
+}
+
+static struct lt_enum_elem* get_enumelem(struct lt_config_shared *cfg,
+ long val, struct lt_enum *en)
+{
+ struct lt_enum_elem key;
+ key.val = val;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "looking for %p <%s> value %ld\n",
+ en, en->name, val);
+
+ return bsearch(&key, en->elems, en->cnt,
+ sizeof(struct lt_enum_elem), enum_comp);
+}
+
+int lt_args_add_enum(struct lt_config_shared *cfg, char *name,
+ struct lt_list_head *h)
+{
+ ENTRY e, *ep;
+ struct lt_enum_elem *elem, *last = NULL;
+ struct lt_enum *en;
+ int i = 0;
+
+ if (NULL == (en = malloc(sizeof(*en))))
+ return -1;
+
+ memset(en, 0x0, sizeof(*en));
+ en->name = name;
+
+ /* Initialize the hash table holding enum names */
+ if (!enum_init) {
+ if (!hcreate_r(LT_ARGS_DEF_ENUM_NUM, &args_enum_tab)) {
+ perror("failed to create has table:");
+ return -1;
+ }
+ enum_init = 1;
+ }
+
+ e.key = en->name;
+ e.data = en;
+
+ if (!hsearch_r(e, ENTER, &ep, &args_enum_tab)) {
+ perror("hsearch_r failed");
+ free(en);
+ /* we dont want to exit just because
+ we ran out of our symbol limit */
+ PRINT_VERBOSE(cfg->verbose, 3,
+ "reach the enum number limit %u\n",
+ LT_ARGS_DEF_ENUM_NUM);
+ }
+
+ /* We've got enum inside the hash, let's prepare the enum itself.
+ The 'elems' field is going to be the qsorted list of
+ 'struct enum_elem's */
+ lt_list_for_each_entry(elem, h, list)
+ en->cnt++;
+
+ if (NULL == (en->elems = malloc(sizeof(struct lt_enum_elem) * en->cnt)))
+ return -1;
+
+ PRINT_VERBOSE(cfg->verbose, 3, "enum %s (%d elems)\n",
+ en->name, en->cnt);
+
+ lt_list_for_each_entry(elem, h, list) {
+
+ if (elem->undef) {
+ if (!last)
+ elem->val = 0;
+ else
+ elem->val = last->val + 1;
+ elem->undef = 0;
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 3, "\t %s = %d\n",
+ elem->name, elem->val);
+
+ en->elems[i++] = *elem;
+ last = elem;
+ }
+
+ qsort(en->elems, en->cnt, sizeof(struct lt_enum_elem), enum_comp);
+ return 0;
+}
+
+struct lt_enum_elem* lt_args_get_enum(struct lt_config_shared *cfg,
+ char *name, char *val)
+{
+ struct lt_enum_elem* elem;
+
+ if (NULL == (elem = malloc(sizeof(*elem))))
+ return NULL;
+
+ memset(elem, 0x0, sizeof(*elem));
+ elem->undef = 1;
+
+ if (val) {
+ long num = strtol(val, (char **) NULL, 10);
+ if ((errno == ERANGE && (num == LONG_MAX || num == LONG_MIN)) ||
+ (errno != 0 && num == 0))
+ return NULL;
+
+ elem->val = num;
+ elem->undef = 0;
+ }
+
+ elem->name = strdup(name);
+
+ PRINT_VERBOSE(cfg->verbose, 3, "enum elem %s = %d, undef = %d\n",
+ elem->name, elem->val, elem->undef);
+ return elem;
+}
+
+int lt_args_add_struct(struct lt_config_shared *cfg, char *type_name,
+ struct lt_list_head *h)
+{
+ struct lt_arg *arg, sarg;
+
+ if ((args_def_struct_cnt + 1) == LT_ARGS_DEF_STRUCT_NUM)
+ return 1;
+
+ /* check if the struct name is already
+ defined as a type */
+ if (lt_args_getarg(cfg, type_name, NULL, 0, 1, NULL))
+ return -1;
+
+ memset(&sarg, 0, sizeof(sarg));
+ sarg.dtype = LT_ARGS_DTYPE_STRUCT;
+ sarg.type_id = LT_ARGS_TYPEID_CUSTOM + args_def_struct_cnt;
+ sarg.type_name = type_name;
+ sarg.args_head = h;
+
+ PRINT_VERBOSE(cfg->verbose, 3, "struct [%s] type %d\n",
+ sarg.type_name, sarg.type_id);
+
+ lt_list_for_each_entry(arg, sarg.args_head, args_list) {
+
+ PRINT_VERBOSE(cfg->verbose, 3, "\t %s %s %u\n",
+ arg->type_name, arg->name, arg->type_len);
+
+ /* This is not what sizeof would return on the structure.
+ The sizeof is arch dependent, this is pure sum. */
+ sarg.type_len += arg->type_len;
+ sarg.mmbcnt++;
+ }
+
+ args_def_struct[args_def_struct_cnt++] = sarg;
+
+ PRINT_VERBOSE(cfg->verbose, 3, "%d.struct - final len = %u\n",
+ args_def_struct_cnt, sarg.type_len);
+ return 0;
+}
+
+int lt_args_add_sym(struct lt_config_shared *cfg, struct lt_arg *ret,
+ struct lt_list_head *h)
+{
+ ENTRY e, *ep;
+ struct lt_args_sym *sym;
+ struct lt_arg *arg;
+ int i = 0;
+
+ PRINT_VERBOSE(cfg->verbose, 3, "got symbol '%s %s'\n",
+ ret->type_name, ret->name);
+
+ if (NULL == (sym = (struct lt_args_sym*) malloc(sizeof(*sym))))
+ return -1;
+
+ memset(sym, 0, sizeof(*sym));
+ sym->name = ret->name;
+
+ sym->argcnt = 1;
+ lt_list_for_each_entry(arg, h, args_list)
+ sym->argcnt++;
+
+ sym->args = (struct lt_arg**) malloc(sym->argcnt * sizeof(struct lt_arg**));
+ if (!sym->args)
+ /* no need to fre sym, since we are going
+ to exit the program anyway */
+ return -1;
+
+ PRINT_VERBOSE(cfg->verbose, 3, "got return %s, ptr %d\n",
+ ret->type_name, ret->pointer);
+
+ sym->args[i++] = ret;
+ lt_list_for_each_entry(arg, h, args_list) {
+ PRINT_VERBOSE(cfg->verbose, 3, "\t '%s %s'\n",
+ arg->type_name, arg->name);
+ sym->args[i++] = arg;
+ }
+
+ e.key = sym->name;
+ e.data = sym;
+
+ if (!hsearch_r(e, ENTER, &ep, &cfg->args_tab)) {
+ perror("hsearch_r failed");
+ free(sym);
+ /* we dont want to exit just because
+ we ran out of our symbol limit */
+ PRINT_VERBOSE(cfg->verbose, 3, "reach the symbol number limit %u\n",
+ LT_ARGS_TAB);
+ } else
+ PRINT_VERBOSE(cfg->verbose, 3, "got symbol %s (%d args)\n",
+ sym->name, sym->argcnt);
+
+ return 0;
+}
+
+static struct lt_arg* argdup(struct lt_config_shared *cfg, struct lt_arg *asrc)
+{
+ struct lt_arg *arg, *a;
+ struct lt_list_head *h;
+
+ PRINT_VERBOSE(cfg->verbose, 2, "got arg '%s %s', dtype %d\n",
+ asrc->type_name, asrc->name, asrc->dtype);
+
+ if (NULL == (arg = malloc(sizeof(*arg)))) {
+ perror("malloc failed");
+ return NULL;
+ }
+
+ *arg = *asrc;
+
+ if (arg->dtype != LT_ARGS_DTYPE_STRUCT)
+ return arg;
+
+ /* For structures we need also to copy all its arguments. */
+
+ if (NULL == (h = (struct lt_list_head*) malloc(sizeof(*h)))) {
+ perror("malloc failed");
+ return NULL;
+ }
+
+ lt_init_list_head(h);
+
+ lt_list_for_each_entry(a, asrc->args_head, args_list) {
+ struct lt_arg *aa;
+
+ /* XXX Not sure how safe is this one...
+ might need some attention in future :) */
+ if (NULL == (aa = argdup(cfg, a)))
+ return NULL;
+
+ lt_list_add_tail(&aa->args_list, h);
+ }
+
+ arg->args_head = h;
+ return arg;
+}
+
+static struct lt_arg* find_arg(struct lt_config_shared *cfg, char *type,
+ struct lt_arg argsdef[], int size, int create)
+{
+ int i;
+
+ for(i = 0; i < size; i++) {
+ struct lt_arg *arg;
+ struct lt_arg adef = argsdef[i];
+
+ PRINT_VERBOSE(cfg->verbose, 3, "%d. looking for [%s] - [%s]\n",
+ i, type, adef.type_name);
+
+ if (strcmp(type, adef.type_name))
+ continue;
+
+ if (!create)
+ return &argsdef[i];
+
+ arg = argdup(cfg, &adef);
+
+ PRINT_VERBOSE(cfg->verbose, 3, "found %d\n", arg->type_id);
+ return arg;
+ }
+
+ return NULL;
+}
+
+struct lt_arg* lt_args_getarg(struct lt_config_shared *cfg, char *type,
+ char *name, int pointer, int create, char *enum_name)
+{
+ struct lt_arg *arg;
+
+ do {
+ if ((arg = find_arg(cfg, type,
+ args_def_pod, LT_ARGS_DEF_POD_NUM, create)))
+ break;
+
+ if ((arg = find_arg(cfg, type,
+ args_def_struct, args_def_struct_cnt, create)))
+ break;
+
+ if ((arg = find_arg(cfg, type,
+ args_def_typedef, args_def_typedef_cnt, create)))
+ break;
+
+ return NULL;
+
+ } while(0);
+
+ if (!create)
+ return arg;
+
+ /* Find out the enum definition if the enum
+ name is provided. */
+ if (enum_name)
+ arg->en = getenum(cfg, enum_name);
+
+ arg->name = strdup(name);
+
+ /* If the type is already a pointer (could be for typedef),
+ give it a chance to show up. There's only one pointer for
+ the arg, since there's no reason to go dreper. */
+ if (!arg->pointer)
+ arg->pointer = pointer;
+
+ return arg;
+}
+
+int lt_args_add_typedef(struct lt_config_shared *cfg, char *base,
+ char *new, int pointer)
+{
+ struct lt_arg *arg;
+ int i;
+
+ if ((args_def_typedef_cnt + 1) == LT_ARGS_DEF_TYPEDEF_NUM)
+ return 2;
+
+ /* check if the typedef name is already
+ defined as a type */
+ if (lt_args_getarg(cfg, new, NULL, 0, 0, NULL))
+ return 1;
+
+ do {
+ if ((arg = find_arg(cfg, base,
+ args_def_pod, LT_ARGS_DEF_POD_NUM, 0)))
+ break;
+
+ if ((arg = find_arg(cfg, base,
+ args_def_typedef, args_def_typedef_cnt, 0)))
+ break;
+
+ PRINT_VERBOSE(cfg->verbose, 3, "%s not found\n", base);
+ return -1;
+
+ } while(0);
+
+ PRINT_VERBOSE(cfg->verbose, 3, "got [%s]\n", new);
+
+ args_def_typedef[i = args_def_typedef_cnt++] = *arg;
+
+ arg = &args_def_typedef[i];
+ arg->type_name = strdup(new);
+ arg->pointer = pointer;
+
+ lt_init_list_head(&arg->args_list);
+
+ PRINT_VERBOSE(cfg->verbose, 3, "%d.typedef - got [%s] [%s]\n",
+ args_def_typedef_cnt, base, arg->type_name);
+ return 0;
+}
+
+int lt_args_init(struct lt_config_shared *cfg)
+{
+ char *file = LT_ARGS_DEF_CONF;
+ int ret = 0;
+
+ if (!hcreate_r(LT_ARGS_TAB, &cfg->args_tab)) {
+ perror("failed to create has table:");
+ return -1;
+ }
+
+ lt_args_parse_init(cfg);
+
+ if (*cfg->args_def)
+ file = cfg->args_def;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "arguments definition file %s\n", file);
+
+ if (lt_args_buf_open(cfg, file))
+ return -1;
+
+ if (yyparse()) {
+ printf("failed to parse config file %s\n", file);
+ ret = -1;
+ }
+
+ if (fclose(yyin)) {
+ perror("failed to close " LT_ARGS_DEF_CONF);
+ return -1;
+ }
+
+ return ret;
+}
+
+static struct lt_args_sym* getsym(struct lt_config_shared *cfg, char *sym)
+{
+ struct lt_args_sym *a;
+ ENTRY e, *ep;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "request for <%s>\n", sym);
+
+ e.key = sym;
+ hsearch_r(e, FIND, &ep, &cfg->args_tab);
+
+ if (!ep)
+ return NULL;
+
+ a = (struct lt_args_sym*) ep->data;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "found %p <%s>\n", a, a->name);
+ return a;
+}
+
+static int getstr_addenum(struct lt_config_shared *cfg, struct lt_arg *arg,
+ char *argbuf, int alen, long val)
+{
+ char *enstr = NULL;
+ struct lt_enum_elem *elem;
+
+ if (!arg->en)
+ return 0;
+
+ if (NULL != (elem = get_enumelem(cfg, val, arg->en)))
+ enstr = elem->name;
+
+ if (enstr)
+ return snprintf(argbuf, alen, "%s", enstr);
+
+ return 0;
+}
+
+static int getstr_pod(struct lt_config_shared *cfg, int dspname, struct lt_arg *arg,
+ void *pval, char *argbuf, int *arglen)
+{
+ int len = 0, alen = *arglen;
+ int namelen = strlen(arg->name);
+
+ PRINT_VERBOSE(cfg->verbose, 1, "\t arg '%s %s', pval %p, len %d, pointer %d, dtype %d, type_id %d\n",
+ arg->type_name, arg->name, pval, alen, arg->pointer, arg->dtype, arg->type_id);
+
+ if (alen < 5)
+ return 0;
+
+ *arglen = 0;
+
+ if ((dspname) &&
+ (namelen < (alen - 5 - sizeof(LT_EQUAL)))) {
+ *arglen = sprintf(argbuf, "%s"LT_EQUAL, arg->name);
+ argbuf += *arglen;
+ alen -= *arglen;
+ }
+
+ /* Get enum resolve for pointers now, the rest
+ POD is done in ARGS_SPRINTF macro. The char
+ pointers need special handling later. */
+ if ((arg->pointer) &&
+ (arg->type_id != LT_ARGS_TYPEID_CHAR)) {
+
+ void *ptr = *((void**) pval);
+
+ /* Try to get enumed value first. */
+ len = getstr_addenum(cfg, arg, argbuf, alen, (long)ptr);
+
+ /* If there's no enum resolved,
+ just display the ptr value */
+ if (!len) {
+ if (ptr)
+ len = snprintf(argbuf, alen, "%p", ptr);
+ else
+ len = snprintf(argbuf, alen, "NULL");
+ }
+
+ goto out;
+ }
+
+#define ARGS_SPRINTF(FMT, TYPE) \
+do { \
+ if (!(len = getstr_addenum(cfg, arg, argbuf, alen, \
+ (long) *((TYPE*) pval)))) \
+ len = snprintf(argbuf, alen, FMT, *((TYPE*) pval)); \
+} while(0)
+
+ switch(arg->type_id) {
+ case LT_ARGS_TYPEID_SHORT: ARGS_SPRINTF("%hd", short); break;
+ case LT_ARGS_TYPEID_USHORT: ARGS_SPRINTF("%hu", unsigned short); break;
+ case LT_ARGS_TYPEID_INT: ARGS_SPRINTF("%d", int); break;
+ case LT_ARGS_TYPEID_UINT: ARGS_SPRINTF("%u", unsigned int); break;
+ case LT_ARGS_TYPEID_LONG: ARGS_SPRINTF("%ld", long); break;
+ case LT_ARGS_TYPEID_ULONG: ARGS_SPRINTF("%lu", unsigned long); break;
+ case LT_ARGS_TYPEID_LLONG: ARGS_SPRINTF("%lld", long long); break;
+ case LT_ARGS_TYPEID_ULLONG: ARGS_SPRINTF("%llu", unsigned long long); break;
+ case LT_ARGS_TYPEID_DOUBLE: ARGS_SPRINTF("%lf", double); break;
+ case LT_ARGS_TYPEID_FLOAT: ARGS_SPRINTF("%f", float); break;
+#undef ARGS_SPRINTF
+ case LT_ARGS_TYPEID_CHAR:
+ if (arg->pointer) {
+
+ void *val = *((void**) pval);
+
+ if (val) {
+ char *s = val;
+ int slen = strlen(s);
+ int left = alen;
+
+ if ((slen + 2) > left) {
+ snprintf(argbuf, left, "\"%s", s);
+ strncpy(argbuf + left - sizeof("...\"") + 1, "...\"", sizeof("...\""));
+ } else {
+ strcpy(argbuf, "\"");
+ strcat(argbuf, s);
+ strcat(argbuf, "\"");
+ }
+ } else
+ len = snprintf(argbuf, alen, "NULL");
+ } else {
+
+ if (*((char*) pval) <= ' ')
+ len = snprintf(argbuf, alen, "0x%02x",
+ *((char*) pval));
+ else
+ len = snprintf(argbuf, alen, "0x%02x \'%c\'",
+ *((char*) pval), *((char*) pval));
+ }
+ break;
+
+ case LT_ARGS_TYPEID_VOID:
+ len = snprintf(argbuf, alen, "void");
+ break;
+ }
+
+ if (LT_ARGS_DTYPE_STRUCT == arg->dtype) {
+ if (pval)
+ len = snprintf(argbuf, alen, "v(%p)", pval);
+ else
+ len = snprintf(argbuf, alen, "v(REG)");
+
+ }
+
+out:
+ *arglen += strlen(argbuf);
+
+ PRINT_VERBOSE(cfg->verbose, 1, "\t arg out len %d - [%s]\n",
+ *arglen, argbuf);
+ return 0;
+}
+
+int lt_args_cb_arg(struct lt_config_shared *cfg, struct lt_arg *arg, void *pval,
+ struct lt_args_data *data, int last, int dspname)
+{
+ int len = data->arglen;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "arg '%s %s', pval %p, last %d\n",
+ arg->type_name, arg->name, pval, last);
+
+ getstr_pod(cfg, dspname, arg, pval,
+ data->args_buf + data->args_totlen, &len);
+ data->args_totlen += len;
+
+ if (!last) {
+ strcat(data->args_buf, ", ");
+ data->args_totlen += 2;
+ }
+
+ return 0;
+}
+
+int lt_args_cb_struct(struct lt_config_shared *cfg, int type, struct lt_arg *arg,
+ void *pval, struct lt_args_data *data, int last)
+{
+ PRINT_VERBOSE(cfg->verbose, 1,
+ "type %d, arg '%s %s', pval %p, last %d, pointer %d\n",
+ type, arg->type_name, arg->name, pval, last, arg->pointer);
+
+ /* initiall call for the structure argument */
+ if (type == LT_ARGS_STRUCT_ITSELF) {
+
+ data->argsd_totlen += sprintf(data->argsd_buf + data->argsd_totlen,
+ "struct %s %s = { ",
+ arg->type_name, arg->name);
+ return 0;
+
+ /* subsequent calls for all structure arguments */
+ } else if (type == LT_ARGS_STRUCT_ARG) {
+
+ int len = cfg->args_detail_maxlen - data->argsd_totlen;
+
+ getstr_pod(cfg, 1, arg, pval, data->argsd_buf + data->argsd_totlen, &len);
+ data->argsd_totlen += len;
+
+ if (!last) {
+ strcat(data->argsd_buf, ", ");
+ data->argsd_totlen += 2;
+ } else
+ data->argsd_totlen += sprintf(data->argsd_buf +
+ data->argsd_totlen, " }\n");
+ }
+
+ return 0;
+}
+
+static int getargs(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_regs *regs, char **abuf, char **adbuf)
+{
+ struct lt_args_data data;
+ int arglen;
+ char *buf, *bufd;
+
+ if (NULL == (buf = malloc(cfg->args_maxlen)))
+ return -1;
+
+ memset(&data, 0, sizeof(data));
+
+ *buf = 0;
+ *abuf = buf;
+
+ if (cfg->args_detailed) {
+ if (NULL == (bufd = malloc(cfg->args_detail_maxlen)))
+ return -1;
+
+ *bufd = 0;
+ *adbuf = bufd;
+ data.argsd_buf = bufd;
+ data.argsd_len = cfg->args_detail_maxlen;
+ }
+
+
+ /* makeup the final space for each
+ argument textual representation */
+ arglen = (cfg->args_maxlen
+ - ((asym->argcnt - 1) * 2) /* args separating commas */
+ )/ asym->argcnt;
+
+
+ data.arglen = arglen;
+ data.args_buf = buf;
+ data.args_len = cfg->args_maxlen;
+
+ return lt_stack_process(cfg, asym, regs, &data);
+}
+
+static FILE* open_include(struct lt_config_shared *cfg, char *file)
+{
+ FILE *f;
+ char fn[LT_MAXFILE];
+
+ /* we got an absolute path */
+ if ((NULL != (f = fopen(file, "r")))) {
+ PRINT_VERBOSE(cfg->verbose, 1, "open ok [%s]\n", file);
+ return f;
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 1, "open failed [%s]: %s\n",
+ file, strerror(errno));
+
+ /* give up if there was already the absolute name */
+ if (*file == '/') {
+ printf("open failed [%s]: %s\n", file, strerror(errno));
+ return NULL;
+ }
+
+ /* not an absolute name, give it a chance
+ inside of the /etc config directory */
+ if (strlen(file) > (LT_MAXFILE - sizeof(LT_ARGS_DEF_DIR))) {
+ printf("file name length crossed the max %u: %s\n",
+ (u_int) (LT_MAXFILE - sizeof(LT_ARGS_DEF_DIR)), file);
+ return NULL;
+ }
+
+ sprintf(fn, "%s/%s", LT_ARGS_DEF_DIR, file);
+
+ if ((NULL == (f = fopen(fn, "r")))) {
+ PRINT_VERBOSE(cfg->verbose, 1, "open failed [%s]: %s\n",
+ fn, strerror(errno));
+ printf("open failed [%s]: %s\n", file, strerror(errno));
+ return NULL;
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 1, "open ok [%s]\n", fn);
+ return f;
+}
+
+int lt_args_buf_open(struct lt_config_shared *cfg, char *file)
+{
+ struct lt_args_include *inc;
+
+ PRINT_VERBOSE(cfg->verbose, 1, "opening buffer for [%s] depth %d\n",
+ file, include_stack_ptr);
+
+ if ((include_stack_ptr + 1) == MAX_INCLUDE_DEPTH) {
+ printf("include depth overstep");
+ return -1;
+ }
+
+ if (NULL == (yyin = open_include(cfg, file)))
+ return -1;
+
+ inc = &include_stack[include_stack_ptr++];
+ memset(inc, 0, sizeof(*inc));
+
+ inc->yyin = yyin;
+ inc->file = strdup(file);
+ inc->lineno = 1;
+ inc->yybuf = yy_create_buffer(yyin, YY_BUF_SIZE);
+
+ yy_switch_to_buffer(inc->yybuf);
+
+ PRINT_VERBOSE(cfg->verbose, 1, "opened buffer for [%s] depth %d\n",
+ file, include_stack_ptr);
+ return 0;
+}
+
+int lt_args_buf_close(struct lt_config_shared *cfg)
+{
+ struct lt_args_include *inc = &include_stack[--include_stack_ptr];
+
+ PRINT_VERBOSE(cfg->verbose, 1, "buffer closed [%s], depth [%d]\n",
+ inc->file, include_stack_ptr);
+
+ free(inc->file);
+
+ /* EOF with no other includes on stack */
+ if (!include_stack_ptr)
+ return -1;
+
+ /* looks like the base buffer is cleaned up by the
+ flex itself, so we do the actual cleaning
+ only for includes */
+ yy_delete_buffer(inc->yybuf);
+ fclose(inc->yyin);
+
+ inc = &include_stack[include_stack_ptr - 1];
+ yy_switch_to_buffer(inc->yybuf);
+ return 0;
+}
+
+struct lt_args_include* lt_args_buf_get(void)
+{
+ struct lt_args_include *inc = &include_stack[include_stack_ptr - 1];
+ return inc;
+}
+
+int lt_args_sym_entry(struct lt_config_shared *cfg, char *sym, La_regs *regs,
+ char **argbuf, char **argdbuf)
+{
+ struct lt_args_sym *asym;
+
+ if (NULL == (asym = getsym(cfg, sym)))
+ return -1;
+
+ return getargs(cfg, asym, regs, argbuf, argdbuf);
+}
+
+static int getargs_ret(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_retval *regs, char **abuf, char **adbuf)
+{
+ struct lt_args_data data;
+ int arglen, totlen;
+ char *buf, *bufd;
+
+ if (NULL == (buf = malloc(cfg->args_maxlen)))
+ return -1;
+
+ memset(&data, 0, sizeof(data));
+
+ *buf = 0;
+ *abuf = buf;
+
+ /* TODO get together with getargs function somehow... */
+ if (cfg->args_detailed) {
+
+ if (NULL == (bufd = malloc(cfg->args_detail_maxlen)))
+ return -1;
+
+ *bufd = 0;
+ *adbuf = bufd;
+ data.argsd_buf = bufd;
+ data.argsd_len = cfg->args_detail_maxlen;
+ }
+
+ arglen = cfg->args_maxlen - sizeof(LT_EQUAL);
+ totlen = sizeof(LT_EQUAL) - 1;
+ strcat(buf, LT_EQUAL);
+
+ data.arglen = arglen;
+ data.args_buf = buf;
+ data.args_len = cfg->args_maxlen;
+ data.args_totlen = totlen;
+
+ return lt_stack_process_ret(cfg, asym, regs, &data);
+}
+
+int lt_args_sym_exit(struct lt_config_shared *cfg, char *sym, La_regs *inregs, La_retval *outregs,
+ char **argbuf, char **argdbuf)
+{
+ struct lt_args_sym *asym;
+
+ if (NULL == (asym = getsym(cfg, sym)))
+ return -1;
+
+ return getargs_ret(cfg, asym, outregs, argbuf, argdbuf);
+}
diff --git a/src/audit-init.c b/src/audit-init.c
new file mode 100644
index 0000000..5ab978c
--- /dev/null
+++ b/src/audit-init.c
@@ -0,0 +1,196 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "config.h"
+
+struct lt_config_audit cfg;
+
+static int read_config(char *dir)
+{
+ int fd;
+ off_t len;
+ char file[LT_MAXFILE];
+
+ memset(&cfg, 0, sizeof(cfg));
+
+ cfg.dir = dir;
+ sprintf(file, "%s/config", dir);
+
+ if (-1 == (fd = open(file, O_RDONLY))) {
+ perror("open failed");
+ return -1;
+ }
+
+ if (-1 == read(fd, &cfg.sh, sizeof(cfg.sh))) {
+ perror("read failed");
+ return -1;
+ }
+
+ if (-1 == (len = lseek(fd, 0, SEEK_END))) {
+ perror("lseek failed");
+ return -1;
+ }
+
+ if (len != sizeof(cfg.sh)) {
+ printf("config file size differs\n");
+ return -1;
+ }
+
+ if (LT_MAGIC != cfg.sh.magic) {
+ printf("config file magic check failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_names(struct lt_config_audit *cfg, char *names, char **ptr)
+{
+ char* s;
+ int cnt = 0;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "names: [%s] max: %d\n",
+ names, LT_NAMES_MAX);
+
+ s = strchr(names, LT_NAMES_SEP);
+ while(NULL != (s = strchr(names, LT_NAMES_SEP)) && (cnt < LT_NAMES_MAX)) {
+ *s = 0x0;
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "got: %s", names);
+ ptr[cnt++] = names;
+ names = ++s;
+ }
+
+ if (cnt) {
+ ptr[cnt++] = names;
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "got: %s\n", names);
+ }
+
+ if (!cnt && *names) {
+ ptr[0] = names;
+ cnt = 1;
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "got: %s\n", names);
+ }
+
+ ptr[cnt] = NULL;
+
+ if (!cnt)
+ return -1;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "got %d entries\n", cnt);
+ return cnt;
+}
+
+int audit_init(int argc, char **argv, char **env)
+{
+ if (-1 == read_config(getenv("LT_DIR")))
+ return -1;
+
+ /* -Aa */
+ if (cfg.sh.args_enabled && lt_args_init(&cfg.sh))
+ return -1;
+
+ /* -t */
+ if ((*cfg.sh.libs_to) &&
+ (-1 == (cfg.libs_to_cnt = get_names(&cfg, cfg.sh.libs_to, cfg.libs_to)))) {
+ printf("latrace failed to parse libs to\n");
+ return -1;
+ }
+
+ /* -f */
+ if ((*cfg.sh.libs_from) &&
+ (-1 == (cfg.libs_from_cnt = get_names(&cfg, cfg.sh.libs_from, cfg.libs_from)))) {
+ printf("latrace failed to parse libs from\n");
+ return -1;
+ }
+
+ /* -l */
+ if ((*cfg.sh.libs_both) &&
+ (-1 == (cfg.libs_both_cnt = get_names(&cfg, cfg.sh.libs_both, cfg.libs_both)))) {
+ printf("latrace failed to parse libs from\n");
+ return -1;
+ }
+
+ /* -s */
+ if ((*cfg.sh.symbols) &&
+ (-1 == (cfg.symbols_cnt = get_names(&cfg, cfg.sh.symbols, cfg.symbols)))) {
+ printf("latrace failed to parse symbols\n");
+ return -1;
+ }
+
+ /* -b */
+ if ((*cfg.sh.flow_below) &&
+ (-1 == (cfg.flow_below_cnt = get_names(&cfg, cfg.sh.flow_below, cfg.flow_below)))) {
+ printf("latrace failed to parse symbols in flow-below option\n");
+ return -1;
+ }
+
+ /* -L */
+ if (*cfg.sh.libs_subst) {
+
+ char *ptr[LT_NAMES_MAX];
+ int cnt;
+
+ if (-1 == (cnt = get_names(&cfg, cfg.sh.libs_subst, ptr))) {
+ printf("latrace failed to parse input for subst option\n");
+ return -1;
+ }
+
+ if (-1 == lt_objsearch_init(&cfg, ptr, cnt)) {
+ printf("latrace failed to nitialize subst option\n");
+ return -1;
+ }
+ }
+
+ /* -o */
+ cfg.sh.fout = stdout;
+ if ((*cfg.sh.output) && (NULL == (cfg.sh.fout = fopen(cfg.sh.output, "w")))) {
+ printf("latrace failed to open output file %s\n", cfg.sh.output);
+ return -1;
+ }
+
+ /* -E */
+ if (cfg.sh.not_follow_exec)
+ unsetenv("LD_AUDIT");
+
+ /* -F */
+ if (cfg.sh.not_follow_fork)
+ cfg.sh.pid = getpid();
+
+ cfg.init_ok = 1;
+ return 0;
+}
+
+void finalize(void) __attribute__((destructor));
+
+void
+finalize(void)
+{
+ if ((!cfg.sh.pipe) && (*cfg.sh.output))
+ fclose(cfg.sh.fout);
+}
diff --git a/src/audit.c b/src/audit.c
new file mode 100644
index 0000000..d50a01f
--- /dev/null
+++ b/src/audit.c
@@ -0,0 +1,287 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <link.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <bits/wordsize.h>
+#include <gnu/lib-names.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+
+extern struct lt_config_audit cfg;
+
+static __thread int pipe_fd = 0;
+static __thread int flow_below_stack = 0;
+
+
+static int check_names(char *name, char **ptr)
+{
+ char *n;
+
+ for(n = *ptr; n; n = *(++ptr)) {
+ if (strstr(name, n)) {
+ PRINT_VERBOSE(cfg.sh.verbose, 2,
+ "return %d for name %s\n", 1, name);
+ return 1;
+ }
+ }
+
+ PRINT_VERBOSE(cfg.sh.verbose, 2, "return %d for name %s\n",
+ 0, name);
+ return 0;
+}
+
+static int check_flow_below(const char *symname, int in)
+{
+ int ret = flow_below_stack;
+
+ if (check_names((char*) symname, cfg.flow_below))
+ in ? ret = ++flow_below_stack : flow_below_stack--;
+
+ return ret;
+}
+
+static int sym_entry(const char *symname, char *lib_from, char *lib_to,
+ La_regs *regs)
+{
+ int argret;
+ char *argbuf = "", *argdbuf = "";
+
+ PRINT_VERBOSE(cfg.sh.verbose, 2, "%s@%s\n", symname, lib_to);
+
+ if (cfg.flow_below_cnt && !check_flow_below(symname, 1))
+ return 0;
+
+ argret = cfg.sh.args_enabled ?
+ lt_args_sym_entry(&cfg.sh, (char*) symname, regs, &argbuf, &argdbuf) : -1;
+
+ if (cfg.sh.pipe) {
+ char buf[FIFO_MSG_MAXLEN];
+ int len;
+
+ if (!pipe_fd)
+ pipe_fd = lt_fifo_create(&cfg, cfg.dir);
+
+ len = lt_fifo_msym_get(&cfg, buf, FIFO_MSG_TYPE_ENTRY,
+ (char*) symname, lib_to, argbuf, argdbuf);
+
+ return lt_fifo_send(&cfg, pipe_fd, buf, len);
+ }
+
+ cfg.sh.indent_depth++;
+
+ lt_out_entry(&cfg.sh, symname, lib_to,
+ argbuf, argdbuf);
+
+ if (!argret) {
+ free(argbuf);
+ if (cfg.sh.args_detailed && (*argdbuf))
+ free(argdbuf);
+ }
+
+ return 0;
+}
+
+static int sym_exit(const char *symname, char *lib_from, char *lib_to,
+ const La_regs *inregs, La_retval *outregs)
+{
+ int argret;
+ char *argbuf = "", *argdbuf = "";
+
+ PRINT_VERBOSE(cfg.sh.verbose, 2, "%s@%s\n", symname, lib_to);
+
+ if (cfg.flow_below_cnt && !check_flow_below(symname, 0))
+ return 0;
+
+ argret = cfg.sh.args_enabled ?
+ lt_args_sym_exit(&cfg.sh, (char*) symname,
+ (La_regs*) inregs, outregs, &argbuf, &argdbuf) : -1;
+
+ if (cfg.sh.pipe) {
+ char buf[FIFO_MSG_MAXLEN];
+ int len;
+
+ len = lt_fifo_msym_get(&cfg, buf, FIFO_MSG_TYPE_EXIT,
+ (char*) symname, lib_to, argbuf, argdbuf);
+
+ return lt_fifo_send(&cfg, pipe_fd, buf, len);
+ }
+
+ lt_out_exit(&cfg.sh, symname, lib_from,
+ argbuf, argdbuf);
+
+ cfg.sh.indent_depth--;
+
+ if (!argret) {
+ free(argbuf);
+
+ if (cfg.sh.args_detailed && (*argdbuf))
+ free(argdbuf);
+ }
+
+ return 0;
+}
+
+static int check_pid()
+{
+ pid_t pid = getpid();
+
+ PRINT_VERBOSE(cfg.sh.verbose, 1, "tid = %d, cfg tid = %d\n",
+ pid, cfg.sh.pid);
+
+ if (pid != cfg.sh.pid)
+ return -1;
+
+ return 0;
+}
+
+#define CHECK_PID(ret) \
+do { \
+ if (cfg.sh.not_follow_fork && \
+ check_pid()) \
+ return ret; \
+} while(0)
+
+unsigned int la_version(unsigned int v)
+{
+ return v;
+}
+
+unsigned int la_objopen(struct link_map *l, Lmid_t a, uintptr_t *cookie)
+{
+ char *name = l->l_name;
+
+ if (!cfg.init_ok)
+ return 0;
+
+ if (!name)
+ return 0;
+
+ /* executable itself */
+ if (!(*name))
+ return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+
+ /* audit all as default */
+ if ((!cfg.libs_to_cnt) &&
+ (!cfg.libs_from_cnt) &&
+ (!cfg.libs_both_cnt))
+ return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+
+ if (check_names(name, cfg.libs_to))
+ return LA_FLG_BINDTO;
+
+ if (check_names(name, cfg.libs_from))
+ return LA_FLG_BINDFROM;
+
+ if (check_names(name, cfg.libs_both))
+ return LA_FLG_BINDTO | LA_FLG_BINDFROM;
+
+ /* wrong library name specified ? */
+ return 0;
+}
+
+static unsigned int la_symbind(const char *symname)
+{
+ unsigned int flags = 0;
+
+ if (cfg.symbols_cnt) {
+ flags = LA_SYMB_NOPLTENTER;
+ if (check_names((char*) symname, cfg.symbols))
+ flags = 0;
+ }
+
+ return flags;
+}
+
+void la_activity(uintptr_t *cookie, unsigned int act)
+{
+ PRINT_VERBOSE(cfg.sh.verbose, 2, "entry\n");
+}
+
+char* la_objsearch(const char *name, uintptr_t *cookie, unsigned int flag)
+{
+ if (flag == LA_SER_ORIG)
+ return (char*) name;
+
+ return lt_objsearch(&cfg, name, cookie, flag);
+}
+
+void la_preinit(uintptr_t *__cookie)
+{
+ PRINT_VERBOSE(cfg.sh.verbose, 2, "entry\n");
+}
+
+unsigned int la_objclose(uintptr_t *__cookie)
+{
+ PRINT_VERBOSE(cfg.sh.verbose, 2, "entry\n");
+ return 0;
+}
+
+uintptr_t la_symbind32(Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, unsigned int *flags, const char *symname)
+{
+ *flags = la_symbind(symname);
+ return sym->st_value;
+}
+
+uintptr_t la_symbind64(Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, unsigned int *flags, const char *symname)
+{
+ *flags = la_symbind(symname);
+ return sym->st_value;
+}
+
+ElfW(Addr)
+pltenter(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, La_regs *regs, unsigned int *flags,
+ const char *symname, long int *framesizep)
+{
+ struct link_map *lr = (struct link_map*) *refcook;
+ struct link_map *ld = (struct link_map*) *defcook;
+
+ CHECK_PID(sym->st_value);
+
+ sym_entry(symname, lr ? lr->l_name : NULL, ld ? ld->l_name : NULL, regs);
+ *framesizep = cfg.sh.framesize;
+ return sym->st_value;
+}
+
+unsigned int pltexit(ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, const La_regs *inregs, La_retval *outregs,
+ const char *symname)
+{
+ struct link_map *lr = (struct link_map*) *refcook;
+ struct link_map *ld = (struct link_map*) *defcook;
+
+ CHECK_PID(0);
+
+ sym_exit(symname, lr ? lr->l_name : NULL, ld ? ld->l_name : NULL, inregs, outregs);
+ return 0;
+}
diff --git a/src/audit.h b/src/audit.h
new file mode 100644
index 0000000..edb72d3
--- /dev/null
+++ b/src/audit.h
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef AUDIT_H
+#define AUDIT_H
+
+#include <link.h>
+
+/* stolen from glibc :) */
+#ifdef __i386__
+# define pltenter la_i86_gnu_pltenter
+# define pltexit la_i86_gnu_pltexit
+# define La_regs La_i86_regs
+# define La_retval La_i86_retval
+# define int_retval lrv_eax
+#elif defined __x86_64__
+# define pltenter la_x86_64_gnu_pltenter
+# define pltexit la_x86_64_gnu_pltexit
+# define La_regs La_x86_64_regs
+# define La_retval La_x86_64_retval
+# define int_retval lrv_rax
+#elif defined __arm__
+# define pltenter la_arm_gnu_pltenter
+# define pltexit la_arm_gnu_pltexit
+# define La_regs La_arm_regs
+# define La_retval La_arm_retval
+# define int_retval lrv_reg[0]
+#elif defined __powerpc__ && __WORDSIZE == 32
+# define pltenter la_ppc32_gnu_pltenter
+# define pltexit la_ppc32_gnu_pltexit
+# define La_regs La_ppc32_regs
+# define La_retval La_ppc32_retval
+# define int_retval lrv_r3
+#elif defined __powerpc__ && __WORDSIZE == 64
+# define pltenter la_ppc64_gnu_pltenter
+# define pltexit la_ppc64_gnu_pltexit
+# define La_regs La_ppc64_regs
+# define La_retval La_ppc64_retval
+# define int_retval lrv_r3
+#elif defined __sh__
+# define pltenter la_sh_gnu_pltenter
+# define pltexit la_sh_gnu_pltexit
+# define La_regs La_sh_regs
+# define La_retval La_sh_retval
+# define int_retval lrv_r0
+#elif defined __alpha__
+# define pltenter la_alpha_gnu_pltenter
+# define pltexit la_alpha_gnu_pltexit
+# define La_regs La_alpha_regs
+# define La_retval La_alpha_retval
+# define int_retval lrv_r0
+#elif defined __s390__ && __WORDSIZE == 32
+# define pltenter la_s390_32_gnu_pltenter
+# define pltexit la_s390_32_gnu_pltexit
+# define La_regs La_s390_32_regs
+# define La_retval La_s390_32_retval
+# define int_retval lrv_r2
+#elif defined __s390__ && __WORDSIZE == 64
+# define pltenter la_s390_64_gnu_pltenter
+# define pltexit la_s390_64_gnu_pltexit
+# define La_regs La_s390_64_regs
+# define La_retval La_s390_64_retval
+# define int_retval lrv_r2
+#elif defined __ia64__
+# define pltenter la_ia64_gnu_pltenter
+# define pltexit la_ia64_gnu_pltexit
+# define La_regs La_ia64_regs
+# define La_retval La_ia64_retval
+# define int_retval lrv_r8
+#elif defined __sparc__ && __WORDSIZE == 32
+# define pltenter la_sparc32_gnu_pltenter
+# define pltexit la_sparc32_gnu_pltexit
+# define La_regs La_sparc32_regs
+# define La_retval La_sparc32_retval
+# define int_retval lrv_reg[0]
+#elif defined __sparc__ && __WORDSIZE == 64
+# define pltenter la_sparc64_gnu_pltenter
+# define pltexit la_sparc64_gnu_pltexit
+# define La_regs La_sparc64_regs
+# define La_retval La_sparc64_retval
+# define int_retval lrv_reg[0]
+#endif
+
+#endif // !AUDIT_H
diff --git a/src/autoconf.h.in b/src/autoconf.h.in
new file mode 100644
index 0000000..efcc490
--- /dev/null
+++ b/src/autoconf.h.in
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef AUTOCONF_H
+
+/* Temporary directory prefix. */
+#undef CONFIG_LT_CONFIG
+
+/* Version define. */
+#undef LT_VER
+
+/* x86 define. */
+#undef LT_ARCH_X86
+
+/* x86_64 define. */
+#undef LT_ARCH_X86_64
+
+#endif
diff --git a/src/autoconf.make.in b/src/autoconf.make.in
new file mode 100644
index 0000000..c162165
--- /dev/null
+++ b/src/autoconf.make.in
@@ -0,0 +1,42 @@
+# Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+#
+# This file is part of the latrace.
+#
+# The latrace is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The latrace is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the latrace (file COPYING). If not, see
+# <http://www.gnu.org/licenses/>.
+
+
+# @configure_input@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+datadir = @datadir@
+mandir = @mandir@
+datarootdir = @datarootdir@
+sysconfdir = @sysconfdir@
+
+RM = rm
+FIND = find
+CC = @CC@
+LEX = @LEX@
+YACC = @YACC@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+CONFIG_SYSDEP_DIR = @CONFIG_SYSDEP_DIR@
+LT_VER = @LT_VER@
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..42d28b2
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,293 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <string.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+static void usage() NORETURN;
+static void usage()
+{
+ printf("usage: latrace [-ltfsbcCpADaoyIiBdvTFELVh] command [arg ... ]\n\n");
+ printf(" -l, --libs lib1,lib2... audit from and to lib1, lib2 ...\n");
+ printf(" -t, --libs-to lib1,lib2... audit to lib1, lib2 ...\n");
+ printf(" -f, --libs-from lib1,lib2... audit from lib1, lib2 ...\n");
+ printf(" -s, --sym sym1,sym2... audit symbols sym1, sym2 ... \n");
+ printf(" -L, --lib-subst s1,s2... objsearch LD_AUDIT interface (see man page)\n");
+ printf("\n");
+ printf(" -c, --counts display statistics counts of symbols\n");
+ printf(" -C, --sort-counts stat implies -c, plus sort the statistics by 'stat':\n");
+ printf(" time,per,call,ucall,lib,sym (default is call)\n");
+ printf(" -p, --pipe use pipe to latrace process to send audit data\n");
+ printf(" latrace app is then the one displaying the output\n");
+ printf("\n");
+ printf(" -A, --enable-args enable arguments output (definitions from /etc/latrace.conf)\n");
+ printf(" -D, --detail-args display struct arguments in more detail\n");
+ printf(" -a, --args file arguments definition file, implies \'-A\'\n");
+ printf("\n");
+ printf(" -y, --framesize number framesize for storing the stack before pltexit (default 1000)\n");
+ printf(" -F, --not-follow-fork dont follow fork calls - childs\n");
+ printf(" -E, --not-follow-exec dont follow exec calls\n");
+ printf("\n");
+ printf(" -b, --flow-below sym1,sym2... display flow only for sym1, sym2 ... \n");
+ printf(" -I, --no-indent-sym do not indent symbols based on the their stack depth\n");
+ printf(" -i, --indent-sym indent_size specify indent size specification\n");
+ printf(" -B, --braces allways display braces {}\n");
+ printf(" -d, --demangle run the symbol name throught the C++ demangler\n");
+ printf(" -T, --hide-tid dont display thread id\n");
+ printf(" -o, --output file store output to file\n");
+ printf("\n");
+ printf(" -v, --verbose verbose output\n");
+ printf(" -V, --version display version\n");
+ printf(" -h, --help display help\n");
+ printf("\n");
+
+ exit(1);
+}
+
+static void version() NORETURN;
+static void version()
+{
+ printf("latrace version " LT_VER "\n");
+ exit(0);
+}
+
+static struct lt_config_tv counts_sort[] = {
+ { .type = LT_CSORT_TIME, .name = "time" },
+ { .type = LT_CSORT_PERCENT, .name = "per" },
+ { .type = LT_CSORT_CALL, .name = "call" },
+ { .type = LT_CSORT_UCALL, .name = "ucall" },
+ { .type = LT_CSORT_LIB, .name = "lib" },
+ { .type = LT_CSORT_SYM, .name = "sym" }
+};
+
+#define COUNTS_SORT_NUM (sizeof(counts_sort)/sizeof(struct lt_config_tv))
+
+static int get_type(struct lt_config_app *cfg, struct lt_config_tv *tv,
+ int num, char *name)
+{
+ int i;
+
+ for(i = 0; i < num; i++)
+ if (!strcmp(name, tv[i].name))
+ return tv[i].type;
+
+ printf("failed to find name: %s\n", name);
+ return -1;
+}
+
+int lt_config(struct lt_config_app *cfg, int argc, char **argv)
+{
+ memset(cfg, 0, sizeof(*cfg));
+
+ /* default values settings */
+ cfg->sh.magic = LT_MAGIC;
+ cfg->sh.framesize = 1000;
+ cfg->sh.fout = stdout;
+ cfg->sh.indent_sym = 1;
+ cfg->sh.indent_size = 2;
+ cfg->sh.args_maxlen = LR_ARGS_MAXLEN;
+ cfg->sh.args_detail_maxlen = LR_ARGS_DETAIL_MAXLEN;
+ cfg->csort = LT_CSORT_CALL;
+
+ while (1) {
+ int c;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"sym", required_argument, 0, 's'},
+ {"libs", required_argument, 0, 'l'},
+ {"libs-to", required_argument, 0, 't'},
+ {"libs-from", required_argument, 0, 'f'},
+ {"no-indent-sym", no_argument, 0, 'I'},
+ {"indent-sym", required_argument, 0, 'i'},
+ {"braces", no_argument, 0, 'B'},
+ {"demangle", no_argument, 0, 'd'},
+ {"flow-below", required_argument, 0, 'b'},
+ {"counts", no_argument, 0, 'c'},
+ {"sort-counts", required_argument, 0, 'C'},
+ {"pipe", no_argument, 0, 'p'},
+ {"output", required_argument, 0, 'o'},
+ {"args", required_argument, 0, 'a'},
+ {"enable-args", required_argument, 0, 'A'},
+ {"detail-args", required_argument, 0, 'D'},
+ {"framesize", required_argument, 0, 'y'},
+ {"lib-subst", required_argument, 0, 'L'},
+ {"verbose", no_argument, 0, 'v'},
+ {"hide-tid", no_argument, 0, 'T'},
+ {"not-follow-fork", no_argument, 0, 'F'},
+ {"not-follow-exec", no_argument, 0, 'E'},
+ {"version", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "+s:l:t:f:vhi:BdIb:cC:y:L:po:a:ADVTFE",
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'l':
+ if (strlen(optarg) > LT_LIBS_MAXSIZE)
+ return -1;
+
+ strncpy(cfg->sh.libs_both, optarg, strlen(optarg));
+ break;
+
+ case 't':
+ if (strlen(optarg) > LT_LIBS_MAXSIZE)
+ return -1;
+
+ strncpy(cfg->sh.libs_to, optarg, strlen(optarg));
+ break;
+
+ case 'f':
+ if (strlen(optarg) > LT_LIBS_MAXSIZE)
+ return -1;
+
+ strncpy(cfg->sh.libs_from, optarg, strlen(optarg));
+ break;
+
+ case 's':
+ if (strlen(optarg) > LT_SYMBOLS_MAXSIZE)
+ return -1;
+
+ strncpy(cfg->sh.symbols, optarg, strlen(optarg));
+ break;
+
+ case 'b':
+ if (strlen(optarg) > LT_SYMBOLS_MAXSIZE)
+ return -1;
+
+ strncpy(cfg->sh.flow_below, optarg, strlen(optarg));
+ break;
+
+ case 'v':
+ cfg->sh.verbose++;
+ break;
+
+ case 'T':
+ cfg->sh.hide_tid = 1;
+ break;
+
+ case 'F':
+ cfg->sh.not_follow_fork = 1;
+ break;
+
+ case 'E':
+ cfg->sh.not_follow_exec = 1;
+ break;
+
+ case 'i':
+ cfg->sh.indent_size = atoi(optarg);
+ break;
+
+ case 'B':
+ cfg->sh.braces = 1;
+ break;
+
+ case 'd':
+ cfg->sh.demangle = 1;
+ break;
+
+ case 'I':
+ cfg->sh.indent_sym = 0;
+ break;
+
+ case 'y':
+ cfg->sh.framesize = atoi(optarg);
+ break;
+
+ case 'L':
+ if (strlen(optarg) > LT_SYMBOLS_MAXSIZE)
+ return -1;
+
+ strncpy(cfg->sh.libs_subst, optarg, strlen(optarg));
+ break;
+
+ case 'C':
+
+ if (-1 == (cfg->csort = get_type(cfg, counts_sort, COUNTS_SORT_NUM, optarg)))
+ usage();
+ /* falling through */
+ case 'c':
+ cfg->sh.counts = 1;
+ /* falling through */
+ case 'p':
+ cfg->sh.pipe = 1;
+ break;
+
+ case 'a':
+ strcpy(cfg->sh.args_def, optarg);
+ /* falling through */
+ case 'A':
+ cfg->sh.args_enabled = 1;
+ break;
+
+ case 'D':
+ cfg->sh.args_detailed = 1;
+ break;
+
+ case 'o':
+ strcpy(cfg->sh.output, optarg);
+ break;
+
+ case 'V':
+ version();
+ break;
+
+ case 'h':
+ usage();
+ break;
+
+ default:
+ printf("unknown option '%c'", c);
+ } // switch (c)
+ } // while(1)
+
+ if (optind < argc) {
+ int i_arg = 1;
+ cfg->prog = argv[optind++];
+ cfg->arg[0] = cfg->prog;
+
+ while ((optind < argc) && (i_arg < LT_NUM_ARG)) {
+ cfg->arg[i_arg++] = argv[optind++];
+ }
+ cfg->arg_num = i_arg;
+ }
+
+ if (!cfg->prog) {
+ printf("failed: no program specified\n");
+ usage();
+ }
+
+ if ((cfg->sh.pipe) && (*cfg->sh.output) &&
+ (NULL == (cfg->sh.fout = fopen(cfg->sh.output, "w")))) {
+ printf("failed to fopen output file %s\n", cfg->sh.output);
+ usage();
+ }
+
+ return 0;
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..680ab07
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,417 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <stdio.h>
+#include <search.h>
+#include <sys/time.h>
+#include <sys/syscall.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include "audit.h"
+#include "list.h"
+#include "autoconf.h"
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#ifndef __attribute__
+#define __attribute__(x)
+#endif
+#endif
+
+/* TODO put this to autoconf.h */
+#define LT_VERSION_MINOR 1
+#define LT_VERSION_MAJOR 0
+#define LT_VERSION (LT_VERSION_MAJOR*256 + LT_VERSION_MINOR)
+
+#define LT_NAMES_MAX 50
+#define LT_NAMES_SEP ','
+
+#define LT_SYM_HMAX 1000
+
+#define LT_ARGS_DEF_STRUCT_NUM 1000
+#define LT_ARGS_DEF_TYPEDEF_NUM 1000
+#define LT_ARGS_DEF_ENUM_NUM 1000
+
+
+enum {
+ LT_CSORT_TIME = 0,
+ LT_CSORT_PERCENT,
+ LT_CSORT_CALL,
+ LT_CSORT_UCALL,
+ LT_CSORT_LIB,
+ LT_CSORT_SYM
+};
+
+struct lt_config_tv {
+ int type;
+ char *name;
+};
+
+struct lt_config_shared {
+#define LT_MAGIC ((LT_VERSION << 16) + 0xdead)
+ unsigned int magic;
+
+#define LT_LIBS_MAXSIZE 200
+ char libs_to[LT_LIBS_MAXSIZE];
+ char libs_both[LT_LIBS_MAXSIZE];
+ char libs_from[LT_LIBS_MAXSIZE];
+
+ char libs_subst[LT_LIBS_MAXSIZE];
+
+#define LT_SYMBOLS_MAXSIZE 200
+ char symbols[LT_SYMBOLS_MAXSIZE];
+
+ char flow_below[LT_SYMBOLS_MAXSIZE];
+
+#define LT_MAXFILE 200
+ char output[LT_MAXFILE];
+ FILE *fout;
+
+ char args_def[LT_MAXFILE];
+ char args_enabled;
+ char args_detailed;
+#define LR_ARGS_MAXLEN 1000
+ int args_maxlen;
+#define LR_ARGS_DETAIL_MAXLEN 1000
+ int args_detail_maxlen;
+#define LT_ARGS_TAB 10000
+ struct hsearch_data args_tab;
+
+ int verbose;
+ int debug;
+ int indent_sym;
+ int indent_size;
+ int braces;
+ int demangle;
+ int counts;
+ int pipe;
+ int hide_tid;
+ int not_follow_exec;
+ int not_follow_fork;
+ unsigned int framesize;
+
+ /* for 'not_follow_fork' */
+ pid_t pid;
+
+ /* used by both app and lib */
+ unsigned int indent_depth;
+};
+
+struct lt_config_app {
+ struct lt_config_shared sh;
+
+ char *prog;
+#define LT_NUM_ARG 500
+ char *arg[LT_NUM_ARG];
+ int arg_num;
+
+ int csort;
+
+ struct lt_thread *threads;
+ struct lt_thread *iter;
+};
+
+enum {
+ LT_OS_PATH = 1, /* '=' */
+ LT_OS_PTN, /* '%' */
+ LT_OS_PTN2PATH, /* '~' */
+};
+
+struct lt_objsearch {
+ int type;
+ char *src;
+ char *dst;
+};
+
+struct lt_config_audit {
+ struct lt_config_shared sh;
+
+ char *libs_to[LT_NAMES_MAX];
+ int libs_to_cnt;
+
+ char *libs_from[LT_NAMES_MAX];
+ int libs_from_cnt;
+
+ char *libs_both[LT_NAMES_MAX];
+ int libs_both_cnt;
+
+ char *symbols[LT_NAMES_MAX];
+ int symbols_cnt;
+
+ char *flow_below[LT_NAMES_MAX];
+ int flow_below_cnt;
+
+ struct lt_objsearch subst[LT_NAMES_MAX];
+ int subst_cnt;
+
+ char *dir;
+ int init_ok;
+};
+
+#define FIFO_MSG_MAXLEN 2000
+
+/* common message data */
+struct lt_fifo_mbase {
+#define FIFO_MSG_TYPE_ENTRY 1
+#define FIFO_MSG_TYPE_EXIT 2
+ uint32_t type;
+ struct timeval tv;
+ pid_t tid;
+ int len; /* the rest of the message size */
+};
+
+/* symbol message */
+struct lt_fifo_msym {
+ struct lt_fifo_mbase h;
+
+ int sym;
+ int lib;
+ int arg;
+ int argd;
+ char data[0];
+};
+
+struct lt_stats_sym {
+ char *name;
+ char *sym;
+ char *lib;
+
+ struct timeval tv_cur;
+ struct timeval tv_all;
+
+ unsigned int call;
+
+ /* post mortem statistics */
+ float percent;
+ unsigned int usec_call;
+};
+
+struct lt_thread {
+ /* global */
+ int fifo_fd;
+ pid_t tid;
+
+ /* start/stop time */
+ struct timeval tv_start;
+ struct timeval tv_stop;
+
+ /* symbol statistics */
+ struct lt_stats_sym **sym_array;
+ struct hsearch_data sym_htab;
+ unsigned int sym_cnt;
+ unsigned int sym_max;
+
+ struct lt_thread *next;
+};
+
+enum {
+ LT_ARGS_DTYPE_POD = 1,
+ LT_ARGS_DTYPE_STRUCT,
+};
+
+enum {
+ LT_ARGS_TYPEID_VOID = 1,
+ LT_ARGS_TYPEID_SHORT,
+ LT_ARGS_TYPEID_USHORT,
+ LT_ARGS_TYPEID_INT,
+ LT_ARGS_TYPEID_UINT,
+ LT_ARGS_TYPEID_LONG,
+ LT_ARGS_TYPEID_ULONG,
+ LT_ARGS_TYPEID_CHAR,
+ LT_ARGS_TYPEID_UCHAR,
+ LT_ARGS_TYPEID_LLONG,
+ LT_ARGS_TYPEID_ULLONG,
+ LT_ARGS_TYPEID_DOUBLE,
+ LT_ARGS_TYPEID_FLOAT,
+
+ LT_ARGS_TYPEID_CUSTOM = 1000
+};
+
+struct lt_enum_elem {
+ char *name;
+ long val;
+ int undef;
+ struct lt_list_head list;
+};
+
+struct lt_enum {
+ char *name;
+ int cnt;
+ struct lt_enum_elem *elems;
+};
+
+struct lt_arg {
+ /* argument type */
+ int dtype;
+
+ /* argument type properties */
+ int type_id;
+ u_int type_len;
+ char *type_name;
+
+ /* argument value properties */
+ int pointer;
+ char *name;
+
+ /* for structures only */
+ int mmbcnt;
+
+ /* architecture dependent */
+ void *arch;
+
+ /* enum record */
+ struct lt_enum *en;
+
+ /* struct arguments head */
+ struct lt_list_head *args_head;
+ /* nested arguments list if present */
+ struct lt_list_head args_list;
+};
+
+struct lt_args_sym {
+ char *name;
+
+ int argcnt;
+#define LT_ARGS_RET 0
+ struct lt_arg **args;
+};
+
+struct lt_args_include {
+ FILE *yyin;
+ void *yybuf;
+ char *file;
+ int lineno;
+};
+
+/* used in lt_args_cb_struct for argument type */
+enum {
+ LT_ARGS_STRUCT_ITSELF = 0,
+ LT_ARGS_STRUCT_ARG
+};
+
+struct lt_args_data {
+ int arglen;
+
+ /* function arguments */
+ char *args_buf;
+ int args_len;
+ int args_totlen;
+
+ /* detailed structs */
+ char *argsd_buf;
+ int argsd_len;
+ int argsd_totlen;
+};
+
+/* global */
+int lt_config(struct lt_config_app *cfg, int argc, char **argv);
+int lt_run(struct lt_config_app *cfg);
+
+/* fifo */
+int lt_fifo_create(struct lt_config_audit *cfg, char *dir);
+int lt_fifo_open(struct lt_config_app *cfg, char *name);
+int lt_fifo_send(struct lt_config_audit *cfg, int fd, char *buf, int len);
+int lt_fifo_recv(struct lt_config_app *cfg, struct lt_thread *t,
+ void *buf, int bufsize);
+int lt_fifo_msym_get(struct lt_config_audit *cfg, char *buf, int type,
+ char *symname, char *libto, char *arg, char *argd);
+
+/* counts */
+int lt_stats_init(struct lt_config_app *cfg);
+int lt_stats_sym(struct lt_config_app *cfg, struct lt_thread *t,
+ struct lt_fifo_msym* m);
+int lt_stats_alloc(struct lt_config_app *cfg, struct lt_thread *t);
+int lt_stats_show(struct lt_config_app *cfg);
+
+/* thread */
+struct lt_thread *lt_thread_add(struct lt_config_app *cfg, int fd, pid_t pid);
+struct lt_thread *lt_thread_first(struct lt_config_app *cfg);
+struct lt_thread *lt_thread_next(struct lt_config_app *cfg);
+
+/* arguments */
+int lt_args_init(struct lt_config_shared *cfg);
+int lt_args_sym_entry(struct lt_config_shared *cfg, char *sym, La_regs *regs,
+ char **argbuf, char **argdbuf);
+int lt_args_sym_exit(struct lt_config_shared *cfg, char *sym, La_regs *inregs,
+ La_retval *outregs, char **argbuf, char **argdbuf);
+int lt_args_add_enum(struct lt_config_shared *cfg, char *name,
+ struct lt_list_head *h);
+struct lt_enum_elem* lt_args_get_enum(struct lt_config_shared *cfg, char *name, char *val);
+int lt_args_add_struct(struct lt_config_shared *cfg, char *type_name,
+ struct lt_list_head *h);
+int lt_args_add_sym(struct lt_config_shared *cfg, struct lt_arg *sym,
+ struct lt_list_head *h);
+int lt_args_add_typedef(struct lt_config_shared *cfg, char *base,
+ char *new, int pointer);
+int lt_args_buf_open(struct lt_config_shared *cfg, char *file);
+int lt_args_buf_close(struct lt_config_shared *cfg);
+struct lt_arg* lt_args_getarg(struct lt_config_shared *cfg, char *type,
+ char *name, int pointer, int create, char *enum_name);
+int lt_args_cb_arg(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *pval, struct lt_args_data *data, int last,
+ int dspname);
+int lt_args_cb_struct(struct lt_config_shared *cfg, int type,
+ struct lt_arg *arg, void *pval,
+ struct lt_args_data *data, int last);
+
+/* output */
+int lt_out_entry(struct lt_config_shared *cfg, const char *symname,
+ char *lib_to, char *argbuf, char *argdbuf);
+int lt_out_exit(struct lt_config_shared *cfg, const char *symname,
+ char *lib_to, char *argbuf, char *argdbuf);
+
+/* stack handling */
+int lt_stack_process(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_regs *regs, struct lt_args_data *data);
+int lt_stack_process_ret(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_retval *regs, struct lt_args_data *data);
+
+/* la_objsearch */
+int lt_objsearch_init(struct lt_config_audit *cfg, char **ptr, int cnt);
+char* lt_objsearch(struct lt_config_audit *cfg, const char *name,
+ uintptr_t *cookie, unsigned int flag);
+
+#define PRINT(fmt, args...) \
+do { \
+ char lpbuf[1024]; \
+ sprintf(lpbuf, "[%d %s:%05d] %s", \
+ (pid_t) syscall(SYS_gettid), \
+ __FUNCTION__, \
+ __LINE__, \
+ fmt); \
+ printf(lpbuf, ## args); \
+} while(0)
+
+#define PRINT_VERBOSE(verbose, cond, fmt, args...) \
+do { \
+ if (cond > verbose) \
+ break; \
+ PRINT(fmt, ## args); \
+} while(0)
+
+
+#endif // !CONFIG_H
diff --git a/src/fifo.c b/src/fifo.c
new file mode 100644
index 0000000..08aad32
--- /dev/null
+++ b/src/fifo.c
@@ -0,0 +1,150 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "config.h"
+
+
+int lt_fifo_create(struct lt_config_audit *cfg, char *dir)
+{
+ int fd;
+ char fifo[100];
+
+ sprintf(fifo, "%s/fifo-%d", dir, (pid_t) syscall(SYS_gettid));
+
+ if (-1 == mkfifo(fifo, 0666)) {
+ perror("mkfifo failed");
+ return -1;
+ }
+
+ if (-1 == (fd = open(fifo, O_WRONLY))) {
+ perror("open fifo failed");
+ return -1;
+ }
+
+ return fd;
+}
+
+int lt_fifo_open(struct lt_config_app *cfg, char *name)
+{
+ int fd;
+
+ if (-1 == (fd = open(name, O_RDONLY)))
+ perror("open fifo failed");
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "pipe openned fd: %d\n", fd);
+ return fd;
+}
+
+int lt_fifo_send(struct lt_config_audit *cfg, int fd, char *buf, int len)
+{
+ static unsigned int written = 0;
+
+ if (-1 == write(fd, buf, len)) {
+ perror("write failed");
+ return -1;
+ }
+
+ written += len;
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "sending %d, total %u\n",
+ len, written);
+ return 0;
+}
+
+int lt_fifo_recv(struct lt_config_app *cfg, struct lt_thread *t, void *buf,
+ int bufsize)
+{
+ static unsigned int red = 0;
+ ssize_t size;
+ struct lt_fifo_mbase *h = buf;
+
+ if (-1 == (size = read(t->fifo_fd, h, sizeof(struct lt_fifo_mbase)))) {
+ perror("read failed");
+ return -1;
+ }
+
+ if (size == 0)
+ return -1;
+
+ red += size;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "received %d\n", h->len);
+
+ if ((size + h->len) > bufsize) {
+ printf("thread %d - buffer max size reached\n", t->tid);
+ return -1;
+ }
+
+ if (-1 == (size = read(t->fifo_fd, buf + sizeof(*h), h->len))) {
+ perror("read failed");
+ return -1;
+ }
+
+ red += size;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "received %d, total %u\n",
+ size + sizeof(*h), red);
+ return 0;
+}
+
+int lt_fifo_msym_get(struct lt_config_audit *cfg, char *buf, int type,
+ char *symname, char *libto, char *arg, char *argd)
+{
+ struct lt_fifo_msym *m = (struct lt_fifo_msym*) buf;
+ int len_data, len = sizeof(struct lt_fifo_msym);
+
+ /* TODO need proper buf size checking */
+ m->h.type = type;
+ m->h.tid = (pid_t) syscall(SYS_gettid);
+ gettimeofday(&m->h.tv, NULL);
+
+ m->sym = 0;
+ m->lib = strlen(symname);
+ m->arg = m->lib + strlen(libto) + 1;
+ m->argd = m->arg + strlen(arg) + 1;
+
+ len_data = sprintf(m->data, "%s %s %s %s", symname, libto, arg, argd);
+
+ m->data[m->lib++] = 0x0;
+ m->data[m->arg++] = 0x0;
+ m->data[m->argd++] = 0x0;
+ m->data[len_data++] = 0x0;
+
+ len += len_data;
+ m->h.len = len_data + (sizeof(*m) - sizeof(struct lt_fifo_mbase));
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "sending data %d <%s> <%s> <%s> <%s>\n",
+ m->h.len,
+ m->data + m->sym,
+ m->data + m->lib,
+ m->data + m->arg,
+ m->data + m->argd);
+ return len;
+}
diff --git a/src/latrace.c b/src/latrace.c
new file mode 100644
index 0000000..9527fcf
--- /dev/null
+++ b/src/latrace.c
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdio.h>
+
+#include "config.h"
+
+
+static struct lt_config_app cfg;
+
+
+int main(int argc, char **argv)
+{
+ if (-1 == lt_config(&cfg, argc, argv))
+ return -1;
+
+ if (-1 == lt_run(&cfg))
+ return -1;
+
+ if ((cfg.sh.pipe) && (*cfg.sh.output))
+ fclose(cfg.sh.fout);
+
+ if (cfg.sh.counts)
+ lt_stats_show(&cfg);
+
+ return 0;
+}
diff --git a/src/list.h b/src/list.h
new file mode 100644
index 0000000..d54f67b
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,103 @@
+/*
+ Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+
+ Taken from the libnl project and customized to fit for latrace.
+ Released undder following license notice.
+
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef LIST_H
+#define LIST_H
+
+struct lt_list_head
+{
+ struct lt_list_head * next;
+ struct lt_list_head * prev;
+};
+
+
+static inline void __lt_list_add(struct lt_list_head *obj,
+ struct lt_list_head *prev,
+ struct lt_list_head *next)
+{
+ prev->next = obj;
+ obj->prev = prev;
+ next->prev = obj;
+ obj->next = next;
+}
+
+static inline void lt_list_add_tail(struct lt_list_head *obj,
+ struct lt_list_head *head)
+{
+ __lt_list_add(obj, head->prev, head);
+}
+
+static inline void lt_list_add_head(struct lt_list_head *obj,
+ struct lt_list_head *head)
+{
+ __lt_list_add(obj, head, head->next);
+}
+
+static inline void lt_list_del(struct lt_list_head *obj)
+{
+ obj->next->prev = obj->prev;
+ obj->prev->next = obj->next;
+}
+
+static inline int lt_list_empty(struct lt_list_head *head)
+{
+ return head->next == head;
+}
+
+#define lt_container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - ((size_t) &((type *)0)->member));})
+
+#define lt_list_entry(ptr, type, member) \
+ lt_container_of(ptr, type, member)
+
+#define lt_list_at_tail(pos, head, member) \
+ ((pos)->member.next == (head))
+
+#define lt_list_at_head(pos, head, member) \
+ ((pos)->member.prev == (head))
+
+#define LT_LIST_HEAD(name) \
+ struct lt_list_head name = { &(name), &(name) }
+
+#define lt_list_first_entry(head, type, member) \
+ lt_list_entry((head)->next, type, member)
+
+#define lt_list_for_each_entry(pos, head, member) \
+ for (pos = lt_list_entry((head)->next, typeof(*pos), member); \
+ &(pos)->member != (head); \
+ (pos) = lt_list_entry((pos)->member.next, typeof(*(pos)), member))
+
+#define lt_list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = lt_list_entry((head)->next, typeof(*pos), member), \
+ n = lt_list_entry(pos->member.next, typeof(*pos), member); \
+ &(pos)->member != (head); \
+ pos = n, n = lt_list_entry(n->member.next, typeof(*n), member))
+
+#define lt_init_list_head(head) \
+ do { (head)->next = (head); (head)->prev = (head); } while (0)
+
+#endif
diff --git a/src/objsearch.c b/src/objsearch.c
new file mode 100644
index 0000000..ae0823f
--- /dev/null
+++ b/src/objsearch.c
@@ -0,0 +1,192 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <string.h>
+
+#include "config.h"
+
+
+static int add_subst(struct lt_config_audit *cfg, char *subst)
+{
+ struct lt_objsearch *s;
+ char *dst, *src = subst;
+ int type;
+
+ if (cfg->subst_cnt == LT_NAMES_MAX)
+ return -1;
+
+ do {
+ if ((dst = strchr(subst, '='))) {
+ type = LT_OS_PATH;
+ break;
+ }
+
+ if ((dst = strchr(subst, '%'))) {
+ type = LT_OS_PTN;
+ break;
+ }
+
+ if ((dst = strchr(subst, '~'))) {
+ type = LT_OS_PTN2PATH;
+ break;
+ }
+
+ } while(0);
+
+ if (!dst)
+ return -1;
+
+ *dst++ = 0x0;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 2, "adding subst type %d, src [%s], dst [%s]\n",
+ type, src, dst);
+
+ s = &cfg->subst[cfg->subst_cnt++];
+ s->type = type;
+ s->src = src;
+ s->dst = dst;
+
+ return 0;
+}
+
+/*
+ Comparing src with the name, if matched
+ replace the name with dst.
+
+ name -> /lib/krava.so
+ src -> /lib/krava.so
+ dst -> /lib/debil.so
+
+ ret -> /lib/debil.so
+*/
+static int match_path(struct lt_config_audit *cfg, const char *name,
+ struct lt_objsearch *s, char **ret)
+{
+ PRINT_VERBOSE(cfg->sh.verbose, 2, "name [%s], src [%s], dst [%s]\n",
+ name, s->src, s->dst);
+
+ *ret = s->dst;
+
+ if (!strcmp(name, s->src))
+ return 1;
+
+ return 0;
+}
+
+/*
+ Matching src in the name, if found,
+ replace the src with dst part.
+
+ name -> /lib/krava.so
+ src -> krava
+ dst -> debil
+
+ ret -> /lib/debil.so
+*/
+static int match_ptn(struct lt_config_audit *cfg, const char *name,
+ struct lt_objsearch *s, char **ret)
+{
+ char *pat, *r;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 2, "name [%s], src [%s], dst [%s]\n",
+ name, s->src, s->dst);
+
+ pat = strstr(name, s->src);
+ if (!pat)
+ return 0;
+
+ r = *ret = malloc(strlen(name) + strlen(s->dst));
+ if (!r) {
+ perror("malloc failed");
+ return 0;
+ }
+
+ strncpy(r, name, pat - name);
+ r += (pat - name);
+ strcpy(r, s->dst);
+ strcat(r, pat + strlen(s->src));
+
+ PRINT_VERBOSE(cfg->sh.verbose, 2, "return %s\n", *ret);
+
+ return 1;
+}
+
+/*
+ Looking for src in the name, if found,
+ replace the name with dst.
+
+ name -> /lib/krava.so
+ src -> krava
+ dst -> /lib/krava1.so
+
+ ret -> /lib/krava1.so
+*/
+static int match_ptn2path(struct lt_config_audit *cfg, const char *name,
+ struct lt_objsearch *s, char **ret)
+{
+ PRINT_VERBOSE(cfg->sh.verbose, 2, "name [%s], src [%s], dst [%s]\n",
+ name, s->src, s->dst);
+
+ *ret = s->dst;
+ if (strstr(name, s->src))
+ return 1;
+
+ return 0;
+}
+
+static int match(struct lt_config_audit *cfg, const char *name,
+ struct lt_objsearch *s, char **ret)
+{
+ switch(s->type) {
+ case LT_OS_PATH:
+ return match_path(cfg, name, s, ret);
+ case LT_OS_PTN:
+ return match_ptn(cfg, name, s, ret);
+ case LT_OS_PTN2PATH:
+ return match_ptn2path(cfg, name, s, ret);
+ };
+
+ return 0;
+}
+
+int lt_objsearch_init(struct lt_config_audit *cfg, char **ptr, int cnt)
+{
+ int i;
+
+ for(i = 0; i < cnt; i++)
+ if (-1 == add_subst(cfg, ptr[i]))
+ return -1;
+
+ return 0;
+}
+
+char* lt_objsearch(struct lt_config_audit *cfg, const char *name,
+ uintptr_t *cookie, unsigned int flag)
+{
+ int i;
+ char *ret = NULL;
+
+ for(i = 0; i < cfg->subst_cnt; i++)
+ if (match(cfg, name, &cfg->subst[i], &ret))
+ return ret;
+
+ return (char*) name;
+}
diff --git a/src/output.c b/src/output.c
new file mode 100644
index 0000000..b8e8bed
--- /dev/null
+++ b/src/output.c
@@ -0,0 +1,125 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#include "config.h"
+
+static char spaces[] = " ";
+
+static int print_details(struct lt_config_shared *cfg, char *argdbuf)
+{
+ fprintf(cfg->fout, "%s\n", argdbuf);
+ return 0;
+}
+
+#define PRINT_TID() \
+do { \
+ fprintf(cfg->fout, "%5d ", (pid_t) syscall(SYS_gettid)); \
+} while(0)
+
+/* libiberty external */
+extern char* cplus_demangle(const char *mangled, int options);
+
+#define DEMANGLE(sym, d) \
+do { \
+ char *dem; \
+ dem = cplus_demangle(sym, 0); \
+ if (dem) { \
+ sym = dem; \
+ d = 1; \
+ } \
+} while(0)
+
+int lt_out_entry(struct lt_config_shared *cfg, const char *symname, char *lib_to,
+ char *argbuf, char *argdbuf)
+{
+ int demangled = 0;
+
+ /* Print thread ID */
+ if (!cfg->hide_tid)
+ PRINT_TID();
+
+ /* Print indentation. */
+ if (cfg->indent_depth && cfg->indent_sym)
+ fprintf(cfg->fout, "%.*s", cfg->indent_depth * cfg->indent_size, spaces);
+
+ /* Demangle the symbol if needed */
+ if (cfg->demangle)
+ DEMANGLE(symname, demangled);
+
+ /* Print the symbol and arguments. */
+ if (*argbuf)
+ fprintf(cfg->fout, "%s(%s) [%s] {\n", symname, argbuf, lib_to);
+ else
+ fprintf(cfg->fout, "%s [%s] %c\n",
+ symname, lib_to,
+ cfg->braces ? '{' : ' ');
+
+ if (demangled)
+ free((char*) symname);
+
+ /* Print arguments' details. */
+ if (cfg->args_detailed && *argdbuf)
+ print_details(cfg, argdbuf);
+
+ fflush(NULL);
+ return 0;
+}
+
+int lt_out_exit(struct lt_config_shared *cfg, const char *symname, char *lib_to,
+ char *argbuf, char *argdbuf)
+{
+ int demangled = 0;
+
+ if (!*argbuf && (!cfg->braces))
+ return 0;
+
+ /* Print thread ID */
+ if (!cfg->hide_tid)
+ PRINT_TID();
+
+ /* Print indentation. */
+ if (cfg->indent_depth && cfg->indent_sym)
+ fprintf(cfg->fout, "%.*s", cfg->indent_depth * cfg->indent_size, spaces);
+
+ /* We got here, because we have '-B' option enabled. */
+ if (!*argbuf && (cfg->braces)) {
+ fprintf(cfg->fout, "}\n");
+ return 0;
+ }
+
+ /* Demangle the symbol if needed */
+ if (cfg->demangle)
+ DEMANGLE(symname, demangled);
+
+ /* Print the symbol and arguments. */
+ fprintf(cfg->fout, "} %s%s\n", symname, argbuf);
+
+ if (demangled)
+ free((char*) symname);
+
+ /* Print arguments' details. */
+ if (cfg->args_detailed && *argdbuf)
+ print_details(cfg, argdbuf);
+
+ fflush(NULL);
+ return 0;
+}
diff --git a/src/run.c b/src/run.c
new file mode 100644
index 0000000..23eb983
--- /dev/null
+++ b/src/run.c
@@ -0,0 +1,319 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+
+#include "config.h"
+
+extern char **environ;
+extern struct timeval tv_program_start;
+extern struct timeval tv_program_stop;
+
+
+static int store_config(struct lt_config_app *cfg, char *file)
+{
+ int fd;
+
+ if (-1 == (fd = open(file, O_CREAT | O_TRUNC | O_RDWR,
+ S_IRUSR))) {
+ perror("open failed");
+ return -1;
+ }
+
+ if (-1 == write(fd, &cfg->sh, sizeof(cfg->sh))) {
+ perror("read failed");
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int set_dir_notify(char *dir)
+{
+ int fd;
+
+ if (-1 == (fd = inotify_init())) {
+ perror("inotify_init failed");
+ return -1;
+ }
+
+ if (-1 == inotify_add_watch(fd, dir, IN_CREATE)) {
+ perror("inotify_add_watch failed");
+ return -1;
+ }
+
+ return fd;
+}
+
+static int get_fifo_dir(char *buf, int len)
+{
+ snprintf(buf, len , "%s-XXXXXX", CONFIG_LT_CONFIG);
+ if (NULL == mkdtemp(buf)) {
+ perror("mkdtemp failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_fifo(struct lt_config_app *cfg, int notify_fd,
+ char *dir, pid_t *pid)
+{
+ char str_fifo[LT_MAXFILE];
+ unsigned char buf[1000];
+ struct inotify_event *event = (struct inotify_event*) buf;
+
+ if (-1 == read(notify_fd, event, 1000)) {
+ perror("read notify failed");
+ return -1;
+ }
+
+ sscanf(event->name, "fifo-%d", pid);
+ sprintf(str_fifo, "%s/%s", dir, event->name);
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "thread id %d, got fifo: %s\n",
+ *pid, str_fifo);
+
+ return lt_fifo_open(cfg, str_fifo);
+}
+
+static int process_fifo(struct lt_config_app *cfg, struct lt_thread *t)
+{
+ static char buf[FIFO_MSG_MAXLEN];
+
+ struct lt_fifo_mbase *mbase = (struct lt_fifo_mbase*) buf;
+
+ if (-1 == lt_fifo_recv(cfg, t, mbase, FIFO_MSG_MAXLEN))
+ return -1;
+
+ if ((FIFO_MSG_TYPE_ENTRY != mbase->type) &&
+ (FIFO_MSG_TYPE_EXIT != mbase->type)) {
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "unexpected message type %d\n",
+ mbase->type);
+ return -1;
+ }
+
+ struct lt_fifo_msym *msym = (struct lt_fifo_msym*) buf;
+
+ if (cfg->sh.counts)
+ return lt_stats_sym(cfg, t, msym);
+
+ if (FIFO_MSG_TYPE_ENTRY == msym->h.type) {
+
+ cfg->sh.indent_depth++;
+ lt_out_entry(&cfg->sh,
+ msym->data + msym->sym,
+ msym->data + msym->lib,
+ msym->data + msym->arg,
+ msym->data + msym->argd);
+
+ } else if (FIFO_MSG_TYPE_EXIT == msym->h.type) {
+
+ lt_out_exit(&cfg->sh,
+ msym->data + msym->sym,
+ msym->data + msym->lib,
+ msym->data + msym->arg,
+ msym->data + msym->argd);
+
+ cfg->sh.indent_depth--;
+ }
+
+ return 0;
+}
+
+static int process(struct lt_config_app *cfg, pid_t pid, char *dir, int notify_fd)
+{
+ int finish = 0, getin = 1;
+ int max_fd = notify_fd;
+ fd_set cfg_set, wrk_set;
+ int status;
+
+ FD_ZERO(&cfg_set);
+ FD_SET(notify_fd, &cfg_set);
+
+ while(!waitpid(pid, &status, WNOHANG) ||
+ /* let all the thread fifo close */
+ (finish) ||
+ /* Get inside at least once, in case the traced program
+ finished before we got here. Another case is if there's
+ an input on notify descriptor, we want to try another
+ select to be sure we dont miss any other event. */
+ (getin))
+ {
+
+ struct timeval tv = { 0, 100 };
+ struct lt_thread *t;
+ int ret;
+
+ getin = 0;
+
+ wrk_set = cfg_set;
+ if (-1 == (ret = select(max_fd + 1, &wrk_set, NULL, NULL, &tv))) {
+ perror("select failed");
+ return -1;
+ }
+
+ if (!ret)
+ continue;
+
+ /* process notify */
+ if (FD_ISSET(notify_fd, &wrk_set)) {
+ int fd;
+ pid_t pid;
+
+ /* try to get any event at least once again */
+ getin = 1;
+
+ if (-1 == (fd = get_fifo(cfg, notify_fd, dir, &pid)))
+ continue;
+
+ if (NULL == (t = lt_thread_add(cfg, fd, pid))) {
+ close(fd);
+ continue;
+ }
+
+ finish++;
+
+ FD_SET(fd, &cfg_set);
+ max_fd = (max_fd < fd ? fd : max_fd);
+ ret--;
+ }
+
+ /* process fifo */
+ for(t = cfg->threads; t ; t = t->next) {
+ if (FD_ISSET(t->fifo_fd, &wrk_set)) {
+ if (-1 == process_fifo(cfg, t)) {
+ FD_CLR(t->fifo_fd, &cfg_set);
+ gettimeofday(&t->tv_stop, NULL);
+ finish--;
+ }
+ ret--;
+ }
+ }
+ }
+
+ return status;
+}
+
+static int remove_dir(struct lt_config_app *cfg, char *name)
+{
+ DIR *dir;
+ struct dirent *d;
+
+ if (NULL == (dir = opendir(name))) {
+ perror("opendir failed");
+ return -1;
+ }
+
+ while((d = readdir(dir))) {
+ char file[LT_MAXFILE];
+
+ if (!strcmp(".", d->d_name) ||
+ (!strcmp("..", d->d_name)))
+ continue;
+
+ sprintf(file, "%s/%s", name, d->d_name);
+ if (-1 == (unlink(file)))
+ perror("unlink failed");
+ }
+
+ closedir(dir);
+
+ if (-1 == remove(name)) {
+ perror("remove failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+int lt_run(struct lt_config_app *cfg)
+{
+ pid_t child_pid;
+ char str_dir[LT_MAXFILE];
+ char str_cfg[LT_MAXFILE];
+ int status;
+ int notify_fd = -1;
+
+ if (-1 == get_fifo_dir(str_dir, LT_MAXFILE))
+ return -1;
+
+ sprintf(str_cfg, "%s/config", str_dir);
+ if (-1 == store_config(cfg, str_cfg))
+ return -1;
+
+ if (cfg->sh.pipe &&
+ (-1 == (notify_fd = set_dir_notify(str_dir))))
+ return -1;
+
+ gettimeofday(&tv_program_start, NULL);
+
+ if (0 == (child_pid = fork())) {
+ char str_audit[100];
+
+ sprintf(str_audit, "%s/libltaudit.so.%s", CONFIG_LIBDIR, LT_VER);
+ setenv("LD_AUDIT", str_audit, 1);
+ setenv("LT_DIR", str_dir, 1);
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "executing %s\n", cfg->prog);
+
+ if (-1 == execvp(cfg->prog, cfg->arg)) {
+ printf("execve failed for \"%s\" : %s\n",
+ cfg->prog, strerror(errno));
+ return -1;
+ }
+ } else if (child_pid < 0) {
+ perror("fork failed");
+ return -1;
+ }
+
+ if (cfg->sh.pipe)
+ status = process(cfg, child_pid, str_dir, notify_fd);
+ else
+ waitpid(child_pid, &status, 0);
+
+ gettimeofday(&tv_program_stop, NULL);
+
+ printf("\n%s finished - ", cfg->prog);
+
+ if (WIFEXITED(status)) {
+ printf("exited, status=%d\n", WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ printf("killed by signal %d\n", WTERMSIG(status));
+ }
+
+ remove_dir(cfg, str_dir);
+ return 0;
+}
diff --git a/src/stats.c b/src/stats.c
new file mode 100644
index 0000000..59e0d9c
--- /dev/null
+++ b/src/stats.c
@@ -0,0 +1,328 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <search.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+
+#include "config.h"
+
+
+struct timeval tv_program_start;
+struct timeval tv_program_stop;
+
+/* res = a - b*/
+static int tv_sub(struct timeval *res, struct timeval *a, struct timeval *b)
+{
+ res->tv_sec = a->tv_sec - b->tv_sec;
+
+ if (a->tv_usec > b->tv_usec)
+ res->tv_usec = a->tv_usec - b->tv_usec;
+ else {
+ res->tv_usec = a->tv_usec + (1000000 - b->tv_usec);
+ if (res->tv_sec)
+ res->tv_sec--;
+ }
+
+ return 0;
+}
+
+/* res = a + b*/
+static int tv_add(struct timeval *res, struct timeval *a, struct timeval *b)
+{
+ struct timeval tv;
+ tv.tv_sec = a->tv_sec + b->tv_sec;
+ tv.tv_usec = a->tv_usec + b->tv_usec;
+ if (tv.tv_usec > 1000000) {
+ tv.tv_usec -= 1000000;
+ tv.tv_sec++;
+ }
+
+ *res = tv;
+ return 0;
+}
+
+int lt_stats_alloc(struct lt_config_app *cfg, struct lt_thread *t)
+{
+ int i;
+
+ if (!t->sym_array) {
+ if (!hcreate_r(t->sym_max = LT_SYM_HMAX, &t->sym_htab)) {
+ perror("hcreate_r failed");
+ return -1;
+ }
+
+ t->sym_array = (struct lt_stats_sym**) malloc(
+ t->sym_max * sizeof(struct lt_stats_sym*));
+
+ if (!t->sym_array) {
+ perror("malloc failed");
+ return -1;
+ }
+
+ return 0;
+ }
+
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1,
+ "too bad, out of symbols, reallocation... %p - %d\n",
+ t->sym_array, t->sym_cnt);
+
+ /* hash table reallocation */
+ t->sym_max += LT_SYM_HMAX;
+ hdestroy_r(&t->sym_htab);
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "creating new hash table\n");
+
+ if (!hcreate_r(t->sym_max, &t->sym_htab)) {
+ perror("hcreate_r failed");
+ return -1;
+ }
+
+ t->sym_array = (struct lt_stats_sym**) realloc(t->sym_array,
+ t->sym_max * sizeof(struct lt_stats_sym*));
+
+ if (!t->sym_array) {
+ perror("realloc failed");
+ return -1;
+ }
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1,
+ "adding symbols to new hash table %p\n",
+ t->sym_array);
+
+ for(i = 0; i < t->sym_cnt; i++) {
+ ENTRY e, *ep;
+ struct lt_stats_sym *sym;
+
+ sym = t->sym_array[i];
+ e.key = sym->name;
+ e.data = sym;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1,
+ "adding symbol %s\n", sym->name);
+
+ if (0 == hsearch_r(e, ENTER, &ep, &t->sym_htab)) {
+ printf("failed to add hash item during reallocation\n");
+ return -1;
+ }
+ }
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "reallocation ok\n");
+ return 0;
+}
+
+int lt_stats_sym(struct lt_config_app *cfg, struct lt_thread *t,
+ struct lt_fifo_msym* m)
+{
+ ENTRY e, *ep;
+ char buf[1000];
+ struct lt_stats_sym *sym;
+ jmp_buf env;
+ int realloc = 0;
+
+ sprintf(buf, "%s%s", m->data + m->sym, m->data + m->lib);
+ e.key = strdup(buf);
+ e.data = 0;
+
+ /* array got out of space */
+ if ((t->sym_cnt == t->sym_max) && (-1 == lt_stats_alloc(cfg, t)))
+ return -1;
+
+ /* hash table got out of space */
+ if (setjmp(env))
+ realloc = 1;
+
+ if (0 == hsearch_r(e, ENTER, &ep, &t->sym_htab)) {
+ if (!realloc) {
+ if (-1 == lt_stats_alloc(cfg, t))
+ return -1;
+ longjmp(env, 1);
+ }
+ return -1;
+ }
+
+ if (!ep->data) {
+ sym = malloc(sizeof(struct lt_stats_sym));
+ memset(sym, 0, sizeof(struct lt_stats_sym));
+
+ ep->data = sym;
+
+ sym->name = e.key;
+ sym->sym = strdup(m->data + m->sym);
+ sym->lib = strdup(m->data + m->lib);
+
+ t->sym_array[t->sym_cnt] = sym;
+ t->sym_cnt++;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1,
+ "adding symbol %d %s\n", t->sym_cnt, sym->name);
+ } else
+ free(e.key);
+
+ sym = ep->data;
+ if (FIFO_MSG_TYPE_ENTRY == m->h.type) {
+ sym->call++;
+ sym->tv_cur = m->h.tv;
+ }
+
+ if (FIFO_MSG_TYPE_EXIT == m->h.type) {
+ struct timeval tv;
+ tv_sub(&tv, &m->h.tv, &sym->tv_cur);
+ tv_add(&sym->tv_all, &sym->tv_all, &tv);
+ }
+
+ return 0;
+}
+
+static int csort;
+
+static int qsort_compar(const void *a, const void *b)
+{
+ struct lt_stats_sym *data_a = *((struct lt_stats_sym**) a);
+ struct lt_stats_sym *data_b = *((struct lt_stats_sym**) b);
+
+ if (csort == LT_CSORT_TIME) {
+ if (data_a->tv_all.tv_sec != data_b->tv_all.tv_sec)
+ return data_b->tv_all.tv_sec - data_a->tv_all.tv_sec;
+ return data_b->tv_all.tv_usec - data_a->tv_all.tv_usec;
+ }
+
+ if (csort == LT_CSORT_PERCENT) {
+ int percent_a = data_a->percent * 100000;
+ int percent_b = data_b->percent * 100000;
+ return percent_b - percent_a;
+ }
+
+ if (csort == LT_CSORT_CALL) {
+ return data_b->call - data_a->call;
+ }
+
+ if (csort == LT_CSORT_UCALL) {
+ return data_b->usec_call - data_a->usec_call;
+ }
+
+ if (csort == LT_CSORT_LIB) {
+ int ret = strcmp(data_a->lib, data_b->lib);
+ if (ret)
+ return ret;
+ return strcmp(data_a->sym, data_b->sym);
+ }
+
+ if (csort == LT_CSORT_SYM) {
+ int ret = strcmp(data_a->sym, data_b->sym);
+ if (ret)
+ return ret;
+ return strcmp(data_a->lib, data_b->lib);
+ }
+
+ return 0;
+}
+
+static int lt_stats_show_thread(struct lt_config_app *cfg, struct lt_thread *t)
+{
+ int i;
+ struct timeval tv_thread_real;
+ struct timeval tv_thread_accu = { 0, 0};
+ float time_global;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "counting total time\n");
+ for(i = 0; i < t->sym_cnt; i++) {
+ struct lt_stats_sym *sym = t->sym_array[i];
+ tv_add(&tv_thread_accu, &tv_thread_accu, &sym->tv_all);
+ }
+
+ time_global = tv_thread_accu.tv_sec * 1000000 + tv_thread_accu.tv_usec;
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "counting post mortem statistics\n");
+
+ for(i = 0; i < t->sym_cnt; i++) {
+ struct lt_stats_sym *sym = t->sym_array[i];
+ u_int time_sym = sym->tv_all.tv_sec*1000000 + sym->tv_all.tv_usec;
+
+ sym->percent = time_sym / (time_global/100);
+ sym->usec_call = time_sym/sym->call;
+ }
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "sorting\n");
+
+ csort = cfg->csort;
+ qsort(t->sym_array, t->sym_cnt, sizeof(struct lt_stats_sym*), qsort_compar);
+
+ PRINT_VERBOSE(cfg->sh.verbose, 1, "printing\n");
+
+ tv_sub(&tv_thread_real, &t->tv_stop, &t->tv_start);
+ printf("\nThread %d (runtime %u.%06u sec)\n",
+ t->tid, (u_int) tv_thread_real.tv_sec, (u_int) tv_thread_real.tv_usec);
+ printf("%3s %-6s %10s %10s %10s %-30s\n",
+ "sec", "usec", "%", "usec/call", "calls", "symbol");
+ printf("%3s %-6s %10s %10s %10s %-30s\n",
+ "---", "------", "----------", "----------", "----------", "------------------------------");
+
+ for(i = 0; i < t->sym_cnt; i++) {
+ struct lt_stats_sym *sym = t->sym_array[i];
+
+ /* sec:usec */
+ printf("%3u:%06u ", (u_int) sym->tv_all.tv_sec, (u_int) sym->tv_all.tv_usec);
+
+ /* % */
+ printf("%10f ", sym->percent);
+
+ /* usec/call */
+ printf("%10u ", sym->usec_call);
+
+ /* call */
+ printf("%10u ", sym->call);
+
+ if (LT_CSORT_LIB == cfg->csort) {
+ /* calls symbol */
+ printf("%s ", sym->lib);
+ printf("%s\n", sym->sym);
+ } else {
+ printf("%s@", sym->sym);
+ printf("%s\n", sym->lib);
+ }
+ }
+
+ return 0;
+}
+
+int lt_stats_show(struct lt_config_app *cfg)
+{
+ struct lt_thread *t;
+
+ struct timeval tv_program_real;
+ tv_sub(&tv_program_real, &tv_program_stop, &tv_program_start);
+
+ printf("\n--------------------------------------------------------------------------\n");
+ printf("Statistics for [%s] total runtime: %u.%06u sec\n",
+ cfg->prog,
+ (u_int) tv_program_real.tv_sec,
+ (u_int) tv_program_real.tv_usec);
+
+ for(t = lt_thread_first(cfg); t; t = lt_thread_next(cfg))
+ lt_stats_show_thread(cfg, t);
+
+ return 0;
+}
+
diff --git a/src/sysdeps/arm/stack.c b/src/sysdeps/arm/stack.c
new file mode 100644
index 0000000..d1388e0
--- /dev/null
+++ b/src/sysdeps/arm/stack.c
@@ -0,0 +1,269 @@
+/*
+ ARM EABI argument extraction
+
+ Copyright (C) 2009 Akos Pasztory <akos.pasztory@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "config.h"
+
+static inline unsigned long align_up(unsigned long v, unsigned long a)
+{
+ return (v + a-1) & ~(a-1);
+}
+
+/*
+ * The .arch field is split the following way:
+ * -- valid :1
+ * -- size :15
+ * -- offset :16
+ */
+
+static inline void arch_set(struct lt_arg *arg,
+ unsigned int size, unsigned int offs)
+{
+ arg->arch = (void *)((size << 16) | offs | 0x80000000);
+}
+
+static inline int arch_valid(struct lt_arg *arg)
+{
+ return (unsigned int)arg->arch & 0x80000000;
+}
+
+static inline unsigned int arch_get_size(struct lt_arg *arg)
+{
+ return ((unsigned int)arg->arch & ~0x80000000) >> 16;
+}
+
+static inline unsigned int arch_get_offs(struct lt_arg *arg)
+{
+ return (unsigned int)arg->arch & 0xffff;
+}
+
+/* Processes a struct entirely in memory. */
+static int process_struct_mem(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *pval, struct lt_args_data *data)
+{
+ int i;
+ unsigned long npval;
+ struct lt_arg *a;
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg, 0, data, 0);
+ npval = (unsigned long)pval;
+ i = 0;
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, a,
+ (void *)(npval + arch_get_offs(a)),
+ data, i+1 == arg->mmbcnt);
+ ++i;
+ }
+ return 0;
+}
+
+/* Returns a bytearray slice from OFFS with SIZE length, treating the
+ * registers and the stack consecutively. Return value is static,
+ * only valid until the next get() call. */
+static void *get(unsigned long offs, int size, La_regs *regs)
+{
+ /* NB: size of buf is fixed. But if we only copy POD types, hopefully
+ * it won't become a problem. */
+ static __thread char buf[64];
+ int i;
+
+ for (i = 0; i < size; ++i) {
+ if (offs+i < 16)
+ buf[i] = ((char *)regs->lr_reg)[offs+i];
+ else
+ buf[i] = *(char *)(regs->lr_sp + (offs+i-16));
+ }
+ return buf;
+}
+
+/* Calculate member sizes and offsets of a structure. Returns the
+ * size of the entire structure. */
+static unsigned int calcstruct(struct lt_arg *arg)
+{
+ unsigned int maxl, moffs;
+ struct lt_arg *a;
+
+ /* XXX: this is incorrect for struct-in-struct... */
+ maxl = moffs = 0;
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+ /* We need to know the strictest aligned member to
+ * determine the size of the entire struct. */
+ if (a->type_len > maxl)
+ maxl = a->type_len;
+ /* Align members naturally. */
+ moffs = align_up(moffs, a->type_len);
+ arch_set(a, a->type_len, moffs);
+ moffs += a->type_len;
+ }
+ return align_up(moffs, maxl);
+}
+
+/* Calculates all offsets and `real' (padded) sizes of arguments
+ * (including struct members). Stores them in ->arch. */
+static void calcofs(struct lt_args_sym *asym)
+{
+ int i;
+ unsigned int offs;
+ struct lt_arg *arg;
+
+ /* If function returns a struct by value larger than a word,
+ * it is stored in callee-allocated memory and r0 contains the
+ * address. */
+ arg = asym->args[LT_ARGS_RET];
+ offs = 0;
+ if (arg->dtype == LT_ARGS_DTYPE_STRUCT) {
+ if (!arg->pointer)
+ offs = 4;
+ arch_set(arg, calcstruct(arg), 0);
+ }
+ for (i = 1; i < asym->argcnt; ++i) {
+ unsigned int asize;
+
+ arg = asym->args[i];
+ /* Struct sizes/offsets should be calculated at all times. */
+ if (arg->dtype == LT_ARGS_DTYPE_STRUCT)
+ calcstruct(arg);
+ if (arg->dtype != LT_ARGS_DTYPE_STRUCT || arg->pointer) {
+ /* Non-composite type. */
+ /* Size is 4 if arg is smaller than a word. */
+ asize = arg->type_len;
+ if (asize < 4)
+ asize = 4;
+ /* Types are aligned to their sizes. */
+ offs = align_up(offs, asize);
+ arch_set(arg, asize, offs);
+ } else {
+ /* Composite type. */
+ asize = calcstruct(arg);
+ arch_set(arg, asize, offs);
+ }
+ offs += asize;
+ /* If we are allocating from registers (4*4 bytes),
+ * each arg must start in a new one. */
+ if (offs < 16)
+ offs = align_up(offs, 4);
+ }
+}
+
+/*
+ * -- args which fit are in r0..r3, rest is on the stack
+ * -- if return type is struct, r0 contains the address for it
+ * -- dword sized args must be aligned at even registers
+ * -- one argument may be split between registers and stack
+ * -- on stack, 8byte values are aligned % 8 == 0
+ */
+int lt_stack_process(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_regs *regs, struct lt_args_data *data)
+{
+ int i;
+
+ for (i = 1; i < asym->argcnt; ++i) {
+ struct lt_arg *arg;
+ void *p;
+ unsigned int o, lastp;
+
+ lastp = i+1 == asym->argcnt;
+ arg = asym->args[i];
+ if (!arch_valid(arg))
+ calcofs(asym);
+ /* POD */
+ if (arg->dtype != LT_ARGS_DTYPE_STRUCT) {
+ p = get(arch_get_offs(arg), arch_get_size(arg), regs);
+ lt_args_cb_arg(cfg, arg, p, data, lastp, 1);
+ continue;
+ }
+ /* struct pointer */
+ if (arg->pointer) {
+ p = get(arch_get_offs(arg), sizeof(void *), regs);
+ p = *(void **)p;
+ lt_args_cb_arg(cfg, arg, p, data, lastp, 1);
+ if (cfg->args_detailed)
+ process_struct_mem(cfg, arg, p, data);
+ continue;
+ }
+ /* struct by value */
+ o = arch_get_offs(arg);
+ /* If part of the struct is in registers this outputs 'REG',
+ * otherwise, the stack address is printed. */
+ lt_args_cb_arg(cfg, arg,
+ (void *)(o < 16 ? 0 : regs->lr_sp + o-16),
+ data, lastp, 1);
+ if (cfg->args_detailed) {
+ int j;
+ struct lt_arg *m;
+
+ j = 0;
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg,
+ 0, data, 0);
+ lt_list_for_each_entry(m, arg->args_head, args_list) {
+ unsigned int o;
+
+ o = arch_get_offs(arg) + arch_get_offs(m);
+ p = get(o, arch_get_size(m), regs);
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, m,
+ p, data, j+1 == arg->mmbcnt);
+ ++j;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * -- 32bit return values in r0
+ * -- 64bit in {r0, r1}
+ * -- composite < 4bytes in r0
+ * -- composite > 4bytes in memory (address is passed as the first parameter
+ * of the call)
+ */
+int lt_stack_process_ret(struct lt_config_shared *cfg,
+ struct lt_args_sym *asym, La_retval *regs,
+ struct lt_args_data *data)
+{
+ struct lt_arg *arg;
+ void *pval;
+
+ /* I think this works for both 32 and 64 bit values (r0 and r1
+ * are consecutive in lrv_reg[]). */
+ pval = &(regs->lrv_reg[0]);
+ arg = asym->args[LT_ARGS_RET];
+ lt_args_cb_arg(cfg, arg, pval, data, 1, 0);
+ if (cfg->args_detailed && arg->dtype == LT_ARGS_DTYPE_STRUCT) {
+ unsigned int asize;
+ /* AAPCS says: a composite type less than 4 bytes is
+ * returned in r0. Larger types have memory allocated
+ * by caller and its address passed as the (hidden)
+ * 1st argument.
+ *
+ * NB: Though I, personally, haven't found it in the
+ * AAPCS, it's both logical (and apparently gcc does
+ * so) to return this address in r0. */
+ asize = arch_get_size(arg);
+ if (arg->pointer || asize > 4)
+ pval = (void *)regs->lrv_reg[0];
+ else if (asize > 4)
+ pval = &regs->lrv_reg[0];
+ process_struct_mem(cfg, arg, pval, data);
+ }
+ return 0;
+}
+/* End of stack.c */
diff --git a/src/sysdeps/i686/stack.c b/src/sysdeps/i686/stack.c
new file mode 100644
index 0000000..5d2c539
--- /dev/null
+++ b/src/sysdeps/i686/stack.c
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "config.h"
+#include "stack.h"
+
+static int process_struct(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *pval, struct lt_args_data *data)
+{
+ struct lt_arg *a;
+ int i = 0;
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg, pval, data, 0);
+
+ if (arg->pointer)
+ pval = *((void**) pval);
+
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+ int last = (i + 1) == arg->mmbcnt;
+
+ /* For the type size up to word (4), the offset of the member
+ is aligned to its size. For the type size over the word,
+ the offset of the member is aligned to word. */
+
+ int nsize = a->type_len > 4 ? 4 : a->type_len;
+ int naligned = (u_int) pval % nsize;
+
+ if (naligned)
+ pval += nsize - naligned;
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, a, pval, data, last);
+
+ pval += a->type_len;
+ i++;
+ }
+
+ return 0;
+}
+
+/*
+ the traced program stack (in regs)
+ should look like this:
+
+ ...
+ esp + 12 2nd argument
+ esp + 8 1st argument
+ esp + 4 possible return structure/union address
+ esp return function address
+
+*/
+int lt_stack_process(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_regs *regs, struct lt_args_data *data)
+{
+ int i;
+ void *pval;
+ struct lt_arg *argret;
+
+ /* get the esp reg and skip the return address */
+ pval = (void*) regs->lr_esp;
+ pval += sizeof(void*);
+
+
+ /* if the function returns structure by value,
+ there's a hidden first argument we need to skip */
+ argret = asym->args[LT_ARGS_RET];
+ if ((!argret->pointer) &&
+ (argret->dtype == LT_ARGS_DTYPE_STRUCT))
+ pval += sizeof(void*);
+
+
+ for(i = 1; i < asym->argcnt; i++) {
+
+ struct lt_arg *arg = asym->args[i];
+ int last = (i + 1) == asym->argcnt;
+
+ lt_args_cb_arg(cfg, arg, pval, data, last, 1);
+
+ if ((cfg->args_detailed) &&
+ (LT_ARGS_DTYPE_STRUCT == arg->dtype))
+ process_struct(cfg, arg, pval, data);
+
+ pval += arg->pointer ? sizeof(void*) : LT_STACK_ALIGN(arg->type_len);
+ }
+
+ return 0;
+}
+
+/* x86 is easy, everything is in eax register */
+int lt_stack_process_ret(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_retval *regs, struct lt_args_data *data)
+{
+ struct lt_arg *arg;
+ void *pval;
+
+ pval = (void*) &(regs->lrv_eax);
+ arg = asym->args[LT_ARGS_RET];
+
+ lt_args_cb_arg(cfg, arg, pval, data, 1, 0);
+
+ if ((cfg->args_detailed) &&
+ (LT_ARGS_DTYPE_STRUCT == arg->dtype)) {
+
+ /* The process_struct function does its own
+ dereference of pval value in case of pointer
+ argument, so we need to prepare pval correctly. */
+ if (!arg->pointer)
+ pval = (void*) regs->lrv_eax;
+
+ process_struct(cfg, arg, pval, data);
+ }
+
+ return 0;
+}
diff --git a/src/sysdeps/i686/stack.h b/src/sysdeps/i686/stack.h
new file mode 100644
index 0000000..0060132
--- /dev/null
+++ b/src/sysdeps/i686/stack.h
@@ -0,0 +1,27 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef STACK_H
+#define STACK_H
+
+#define LT_STACK_ALIGN(arg) ((arg + 3) & ~3)
+
+#endif // !STACK_H
diff --git a/src/sysdeps/x86_64/stack.c b/src/sysdeps/x86_64/stack.c
new file mode 100644
index 0000000..2273506
--- /dev/null
+++ b/src/sysdeps/x86_64/stack.c
@@ -0,0 +1,615 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "config.h"
+#include "stack.h"
+
+static __thread int ass_integer;
+static __thread int ass_sse;
+static __thread int ass_memory;
+
+#define ASS_CLEANUP() \
+do { \
+ ass_integer = ass_sse = ass_memory = 0; \
+} while(0)
+
+/* INTEGER registers used for function arguments. */
+static long ass_regs_integer[] = {
+ LT_64_REG_RDI,
+ LT_64_REG_RSI,
+ LT_64_REG_RDX,
+ LT_64_REG_RCX,
+ LT_64_REG_R8,
+ LT_64_REG_R9,
+};
+#define ASS_REGS_INTEGER_CNT (sizeof(ass_regs_integer)/sizeof(long))
+#define ASS_REGS_INTEGER_SIZE (sizeof(ass_regs_integer))
+
+/* INTEGER registers used for function return arguments. */
+static long ass_regs_integer_ret[] = {
+ LT_64_REG_RAX,
+ LT_64_REG_RDX,
+};
+#define ASS_REGS_INTEGER_RET_CNT (sizeof(ass_regs_integer_ret)/sizeof(long))
+#define ASS_REGS_INTEGER_RET_SIZE (sizeof(ass_regs_integer_ret))
+
+/* SSE registers used for function arguments. */
+static long ass_regs_sse[] = {
+ LT_64_REG_XMM0,
+ LT_64_REG_XMM1,
+ LT_64_REG_XMM2,
+ LT_64_REG_XMM3,
+ LT_64_REG_XMM4,
+ LT_64_REG_XMM5,
+ LT_64_REG_XMM6,
+ LT_64_REG_XMM7,
+};
+#define ASS_REGS_SSE_CNT (sizeof(ass_regs_sse)/sizeof(long))
+
+/* SSE registers used for function return arguments. They
+ are xmm0 and xmm1, so we reuse the ass_regs_sse array.*/
+#define ASS_REGS_SSE_RET_CNT (2)
+
+static int classificate_arg(struct lt_config_shared *cfg, struct lt_arg *arg,
+ int ret, int align);
+
+static int classificate_memory(struct lt_config_shared *cfg,
+ struct lt_arg *arg, int align)
+{
+ if (align)
+ ass_memory = LT_STACK_ALIGN(ass_memory);
+ else {
+ int naligned = ass_memory % arg->type_len;
+
+ if (naligned)
+ ass_memory += arg->type_len - naligned;
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 2, "%s - ass %d\n",
+ arg->name, ass_memory);
+
+ ARCH_SET(arg, ARCH_FLAG_MEM, ass_memory);
+
+ if (arg->pointer)
+ ass_memory += sizeof(void*);
+ else
+ ass_memory += arg->type_len;
+
+ return 0;
+}
+
+static int classificate_integer(struct lt_config_shared *cfg,
+ struct lt_arg *arg, int align, int regs_size)
+{
+ if (!align) {
+ int ass = LT_STACK_ALIGN(ass_integer);
+ int naligned = ass_integer % arg->type_len;
+
+ if (naligned)
+ ass_integer += arg->type_len - naligned;
+
+ if ((ass_integer + arg->type_len) > ass)
+ ass_integer = LT_STACK_ALIGN(ass_integer);
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 2,
+ "ass %s - reg size %d - ass_integer %d\n",
+ arg->name, regs_size, ass_integer);
+
+ if (ass_integer >= regs_size)
+ return -1;
+
+ ARCH_SET(arg, ARCH_FLAG_REG_INTEGER, ass_integer);
+
+ if (arg->pointer)
+ ass_integer += sizeof(void*);
+ else
+ ass_integer += align ? sizeof(void*) : arg->type_len;
+
+ return 0;
+}
+
+static int classificate_sse(struct lt_config_shared *cfg, struct lt_arg *arg,
+ int sse_cnt)
+{
+ PRINT_VERBOSE(cfg->verbose, 2, "ass %s %d %d\n",
+ arg->name, ASS_REGS_SSE_CNT, ass_sse);
+
+ if (sse_cnt == ass_sse)
+ return -1;
+
+ ARCH_SET(arg, ARCH_FLAG_REG_SSE, ass_sse++);
+ return 0;
+}
+
+static void struct_arch(struct lt_config_shared *cfg, struct lt_arg *sarg,
+ struct lt_arg *farg)
+{
+ if (sarg->pointer)
+ return;
+
+ /* Structures passed by value dont have the arch assigned by default.
+ If the first argument is returned in memory, the structure takes
+ this pointer, if it is register, we keep it NULL, and lt_args_cb_arg
+ will print out the REG kw for value.*/
+ PRINT_VERBOSE(cfg->verbose, 2,
+ "first argument for struct %s has flag: %d\n",
+ sarg->name, ARCH_GET_FLAG(farg));
+
+ if (ARCH_GET_FLAG(farg) == ARCH_FLAG_MEM)
+ sarg->arch = farg->arch;
+ else
+ ARCH_SET(sarg, ARCH_FLAG_SVREG, 0);
+}
+
+
+static int classificate_struct_try(struct lt_config_shared *cfg,
+ struct lt_arg *arg, int allmem, int ret)
+{
+ struct lt_arg *a;
+ int first = 1;
+ int saved_ass_integer = ass_integer;
+ int saved_ass_sse = ass_sse;
+ int saved_ass_memory = ass_memory;
+
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+
+ if (allmem)
+ classificate_memory(cfg, a, first);
+ else {
+ if (-1 == classificate_arg(cfg, a, ret, 0))
+ return -1;
+
+ if (ARCH_GET_FLAG(arg) == ARCH_FLAG_MEM) {
+ /* There is not enough registers, reset to
+ have the structure in memory only. */
+ ass_integer = saved_ass_integer;
+ ass_sse = saved_ass_sse;
+ ass_memory = saved_ass_memory;
+ return -1;
+ }
+
+ }
+
+ if (first) {
+ struct_arch(cfg, arg, a);
+ first = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int get_sizeof(struct lt_config_shared *cfg, struct lt_arg *arg)
+{
+ struct lt_arg *a;
+ int size = 0;
+
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+
+ int naligned = (u_int) size % a->type_len;
+
+ if (naligned)
+ size += a->type_len - naligned;
+
+ size += a->type_len;
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 2, "sizeof(struct %s) = %d\n",
+ arg->type_name, size);
+
+ return size;
+}
+
+static int classificate_struct(struct lt_config_shared *cfg, struct lt_arg *arg,
+ int ret)
+{
+ int allmem = 0;
+ int size = get_sizeof(cfg, arg);
+
+ /* If the structure is bigger than 16B or passed as pointer then
+ it is in the memory (no registers). If the structure is up to
+ 16B and passed by value, we might need t classificate its members,
+ because they could fit in registers. */
+ if ((size > 16) || (arg->pointer))
+ allmem = 1;
+
+ PRINT_VERBOSE(cfg->verbose, 2,
+ "struct %s - length sum %d - allmem %d\n",
+ arg->type_name, arg->type_len, allmem);
+
+ if (-1 == classificate_struct_try(cfg, arg, allmem, ret))
+ classificate_struct_try(cfg, arg, 1, ret);
+
+ return 0;
+}
+
+static int classificate_arg_type(struct lt_config_shared *cfg,
+ struct lt_arg *arg)
+{
+ int class;
+
+ do {
+ /* pointers are INTEGER class by default */
+ if (arg->pointer) {
+ class = LT_CLASS_INTEGER;
+ break;
+ }
+
+ switch(arg->type_id) {
+ case LT_ARGS_TYPEID_CHAR:
+ case LT_ARGS_TYPEID_UCHAR:
+ case LT_ARGS_TYPEID_SHORT:
+ case LT_ARGS_TYPEID_USHORT:
+ case LT_ARGS_TYPEID_INT:
+ case LT_ARGS_TYPEID_UINT:
+ case LT_ARGS_TYPEID_LONG:
+ case LT_ARGS_TYPEID_ULONG:
+ case LT_ARGS_TYPEID_LLONG:
+ case LT_ARGS_TYPEID_ULLONG:
+ class = LT_CLASS_INTEGER;
+ break;
+
+ case LT_ARGS_TYPEID_DOUBLE:
+ case LT_ARGS_TYPEID_FLOAT:
+ class = LT_CLASS_SSE;
+ break;
+
+ default:
+ class = LT_CLASS_NONE;
+ break;
+ }
+
+ } while(0);
+
+ PRINT_VERBOSE(cfg->verbose, 2,
+ "argument %s dtype %d - type %s(%d) - class %d\n",
+ arg->name, arg->dtype, arg->type_name, arg->type_id,
+ class);
+ return class;
+}
+
+static int classificate_arg(struct lt_config_shared *cfg, struct lt_arg *arg,
+ int ret, int align)
+{
+ int class;
+ int class_failed = 0;
+
+ PRINT_VERBOSE(cfg->verbose, 2, "got arg \"%s\"\n",
+ arg->name);
+
+ ARCH_ZERO(arg);
+
+ if (arg->dtype != LT_ARGS_DTYPE_POD) {
+
+ if (-1 == classificate_struct(cfg, arg, ret))
+ return -1;
+
+ /* If the structure is passed by pointer, we
+ still need the pointer classification. */
+ if (!arg->pointer)
+ return 0;
+ }
+
+ class = classificate_arg_type(cfg, arg);
+ if (-1 == class)
+ return -1;
+
+ /* Nothing to be done for NONE class (void type) */
+ if (LT_CLASS_NONE == class)
+ return 0;
+
+ switch(class) {
+ case LT_CLASS_SSE:
+ class_failed = classificate_sse(cfg, arg,
+ ret ? ASS_REGS_SSE_RET_CNT :
+ ASS_REGS_SSE_CNT);
+ break;
+
+ case LT_CLASS_INTEGER:
+ class_failed = classificate_integer(cfg, arg, align,
+ ret ? ASS_REGS_INTEGER_RET_SIZE :
+ ASS_REGS_INTEGER_SIZE);
+ break;
+
+ case LT_CLASS_MEMORY:
+ classificate_memory(cfg, arg, 1);
+ break;
+ }
+
+ /* If class INTEGER or SSE ran out of registers,
+ then arg is in memory. */
+ if (class_failed)
+ classificate_memory(cfg, arg, 1);
+
+ return 0;
+}
+
+static int classificate(struct lt_config_shared *cfg, struct lt_args_sym *sym)
+{
+ int i;
+ struct lt_arg *arg = sym->args[LT_ARGS_RET];
+
+ PRINT_VERBOSE(cfg->verbose, 2, "got symbol \"%s\"\n",
+ sym->name);
+
+ ASS_CLEANUP();
+
+ /* Classificate the return value first. */
+ if (-1 == classificate_arg(cfg, arg, 1, 1))
+ return -1;
+
+ ASS_CLEANUP();
+
+ /* If the return value is memory class,
+ then the edi register is used as a first hidden arg.*/
+ if (ARCH_GET_FLAG(arg) == ARCH_FLAG_MEM)
+ ass_integer += 8;
+
+ for(i = 1; i < sym->argcnt; i++) {
+ arg = sym->args[i];
+
+ if (-1 == classificate_arg(cfg, arg, 0, 1))
+ return -1;
+ }
+
+ return 0;
+}
+
+static void *get_value_mem(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *regs, int ret)
+{
+ long offset = ARCH_GET_OFFSET(arg);
+ void *pval;
+
+ if (ret) {
+ void *base = (void*) ((struct La_x86_64_retval*) regs)->lrv_rax;
+ pval = base + offset;
+ } else {
+ void *base = (void*) ((struct La_x86_64_regs*) regs)->lr_rsp;
+ pval = base + /* current stack pointer */
+ sizeof(void*) + /* return function address */
+ offset;
+ }
+
+ PRINT_VERBOSE(cfg->verbose, 2, "offset = %ld, %s = %p, ret = %d\n",
+ offset, arg->name, pval, ret);
+ return pval;
+}
+
+static void *get_value_reg_integer(struct lt_config_shared *cfg,
+ struct lt_arg *arg, void *regs, int ret)
+{
+ struct La_x86_64_retval *regs_ret = regs;
+ struct La_x86_64_regs *regs_in = regs;
+ void *pval = NULL;
+ long offset = ARCH_GET_OFFSET(arg);
+ long qoff = offset % sizeof(long);
+ long reg = ret ? ass_regs_integer_ret[offset / sizeof(long)] :
+ ass_regs_integer[offset / sizeof(long)];
+
+
+ PRINT_VERBOSE(cfg->verbose, 2,
+ "offset %ld - reg %ld - qoff %ld - ASS_REGS_CNT %ld - ret %d\n",
+ offset, reg, qoff, ASS_REGS_INTEGER_CNT, ret);
+
+ switch(reg) {
+ case LT_64_REG_RAX:
+ pval = &regs_ret->lrv_rax;
+ break;
+ case LT_64_REG_RDX:
+ pval = ret ? &regs_ret->lrv_rdx : &regs_in->lr_rdx;
+ break;
+ case LT_64_REG_RDI:
+ pval = &regs_in->lr_rdi;
+ break;
+ case LT_64_REG_RSI:
+ pval = &regs_in->lr_rsi;
+ break;
+ case LT_64_REG_RCX:
+ pval = &regs_in->lr_rcx;
+ break;
+ case LT_64_REG_R8:
+ pval = &regs_in->lr_r8;
+ break;
+ case LT_64_REG_R9:
+ pval = &regs_in->lr_r9;
+ break;
+ }
+
+ pval += qoff;
+
+ PRINT_VERBOSE(cfg->verbose, 2, "argument %s = %p (%lx)\n",
+ arg->name, pval, *((u_long*)pval));
+ return pval;
+}
+
+static void *get_value_reg_sse(struct lt_config_shared *cfg,
+ struct lt_arg *arg, void *regs, int ret)
+{
+ int i = ARCH_GET_OFFSET(arg);
+ void *pval = NULL;
+
+ if (ret) {
+ struct La_x86_64_retval *regs_ret = regs;
+
+ switch(i) {
+ case 0:
+ pval = &regs_ret->lrv_xmm0;
+ break;
+ case 1:
+ pval = &regs_ret->lrv_xmm1;
+ break;
+ }
+ } else {
+ struct La_x86_64_regs *regs_in = regs;
+
+ pval = &regs_in->lr_xmm[i];
+ }
+
+
+ PRINT_VERBOSE(cfg->verbose, 2, "argument %s = %p (%lx), ret %d\n",
+ arg->name, pval, *((u_long*)pval), ret);
+ return pval;
+}
+
+static void* get_value(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *regs, int ret)
+{
+ void *pval = NULL;
+
+ PRINT_VERBOSE(cfg->verbose, 2, "get value for %s - arch = %lx, flag = %d\n",
+ arg->name, ARCH_GET(arg), ARCH_GET_FLAG(arg));
+
+ switch(ARCH_GET_FLAG(arg)) {
+ case ARCH_FLAG_MEM:
+ pval = get_value_mem(cfg, arg, regs, ret);
+ break;
+
+ case ARCH_FLAG_REG_INTEGER:
+ pval = get_value_reg_integer(cfg, arg, regs, ret);
+ break;
+
+ case ARCH_FLAG_REG_SSE:
+ pval = get_value_reg_sse(cfg, arg, regs, ret);
+ break;
+ }
+
+ return pval;
+}
+
+/* Process structure stored completelly in the
+ memory - pointed to by 'pval' arg. */
+static int process_struct_mem(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *pval, struct lt_args_data *data)
+{
+ struct lt_arg *a;
+ int i = 0;
+
+ PRINT_VERBOSE(cfg->verbose, 2, "for %s - arch = %llx\n",
+ arg->name, ARCH_GET(arg));
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg, NULL, data, 0);
+
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+ int last = (i + 1) == arg->mmbcnt;
+ void *pv = pval + ARCH_GET_OFFSET(a);
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, a, pv, data, last);
+
+ i++;
+ }
+
+ return 0;
+}
+
+static int process_struct_arg(struct lt_config_shared *cfg, struct lt_arg *arg,
+ void *regs, struct lt_args_data *data, int ret)
+{
+ struct lt_arg *a;
+ int i = 0;
+
+ PRINT_VERBOSE(cfg->verbose, 2, "for %s - arch = %llx\n",
+ arg->name, ARCH_GET(arg));
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ITSELF, arg, NULL, data, 0);
+
+ lt_list_for_each_entry(a, arg->args_head, args_list) {
+ int last = (i + 1) == arg->mmbcnt;
+ void *pval = NULL;
+
+ pval = get_value(cfg, a, regs, ret);
+
+ lt_args_cb_struct(cfg, LT_ARGS_STRUCT_ARG, a, pval, data, last);
+ i++;
+ }
+
+ return 0;
+}
+
+static void process_detailed_struct(struct lt_config_shared *cfg,
+ struct lt_arg *arg, void *pval, struct lt_args_data *data,
+ void *regs, int ret)
+{
+ if (arg->pointer)
+ pval = *((void**) pval);
+
+ if (ARCH_GET_FLAG(arg) == ARCH_FLAG_MEM) {
+ process_struct_mem(cfg, arg, pval, data);
+ } else {
+ if (arg->pointer)
+ process_struct_mem(cfg, arg, pval, data);
+ else
+ process_struct_arg(cfg, arg, regs, data, ret);
+ }
+}
+
+
+int lt_stack_process(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_regs *regs, struct lt_args_data *data)
+{
+ int i;
+
+ if (!ARCH_GET(asym->args[LT_ARGS_RET]) &&
+ (-1 == classificate(cfg, asym)))
+ return -1;
+
+ for(i = 1; i < asym->argcnt; i++) {
+ void *pval = NULL;
+ struct lt_arg *arg = asym->args[i];
+ int last = (i + 1) == asym->argcnt;
+
+ pval = get_value(cfg, arg, regs, 0);
+ if ((!pval) &&
+ (arg->pointer || (LT_ARGS_DTYPE_STRUCT != arg->dtype))) {
+ PRINT_VERBOSE(cfg->verbose, 2,
+ "THIS SHOULD NEVER HAPPEN - arg '%s %s'\n",
+ arg->type_name, arg->name);
+ continue;
+ }
+
+ lt_args_cb_arg(cfg, arg, pval, data, last, 1);
+
+ if ((cfg->args_detailed) &&
+ (LT_ARGS_DTYPE_STRUCT == arg->dtype))
+ process_detailed_struct(cfg, arg, pval, data, regs, 0);
+ }
+
+ return 0;
+}
+
+int lt_stack_process_ret(struct lt_config_shared *cfg, struct lt_args_sym *asym,
+ La_retval *regs, struct lt_args_data *data)
+{
+ struct lt_arg *arg;
+ void *pval;
+
+ arg = asym->args[LT_ARGS_RET];
+ pval = get_value(cfg, arg, regs, 1);
+
+ lt_args_cb_arg(cfg, arg, pval, data, 1, 0);
+
+ if ((cfg->args_detailed) &&
+ (LT_ARGS_DTYPE_STRUCT == arg->dtype))
+ process_detailed_struct(cfg, arg, pval, data, regs, 0);
+
+ return 0;
+}
diff --git a/src/sysdeps/x86_64/stack.h b/src/sysdeps/x86_64/stack.h
new file mode 100644
index 0000000..7a24617
--- /dev/null
+++ b/src/sysdeps/x86_64/stack.h
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef STACK_H
+#define STACK_H
+
+/*
+ TODO Describe the implementation in more details TODO
+
+ Architecture specific data inside lt_arg.
+
+ bits 64 32|31 0
+ --------------------|--------------------
+ fields flags | offset
+
+
+ offset
+ - offset or memory or registers
+ flags
+ - memory/registers switch
+*/
+
+#define LT_STACK_ALIGN(arg) ((arg + 7) & ~7)
+
+#define ARCH_POS_FLAGS 32
+
+#define ARCH_FLAG_MEM 1L
+#define ARCH_FLAG_REG_INTEGER 2L
+#define ARCH_FLAG_REG_SSE 3L
+#define ARCH_FLAG_SVREG 4L
+
+#define ARCH_GET(arg) ((int64_t) ((arg)->arch))
+#define ARCH_GET_FLAG(arg) ((int) ((long) arg->arch >> ARCH_POS_FLAGS))
+#define ARCH_GET_OFFSET(arg) ((long) ((long) arg->arch & 0xFFFFFFFFL))
+
+#define ARCH_ZERO(arg) \
+do {\
+ arg->arch = NULL; \
+} while(0)
+
+#define ARCH_SET(arg, flags, offset) \
+do { \
+ arg->arch = (void*) (offset + \
+ (flags << ARCH_POS_FLAGS)); \
+} while(0)
+
+/* registers index */
+#define LT_64_REG_RAX 1
+#define LT_64_REG_RCX 2
+#define LT_64_REG_RDX 3
+#define LT_64_REG_RDI 4
+#define LT_64_REG_RSI 5
+#define LT_64_REG_R8 6
+#define LT_64_REG_R9 7
+#define LT_64_REG_RBP 8
+#define LT_64_REG_RSP 9
+#define LT_64_REG_XMM0 10
+#define LT_64_REG_XMM1 11
+#define LT_64_REG_XMM2 12
+#define LT_64_REG_XMM3 13
+#define LT_64_REG_XMM4 14
+#define LT_64_REG_XMM5 15
+#define LT_64_REG_XMM6 16
+#define LT_64_REG_XMM7 17
+
+/* argument classes */
+#define LT_CLASS_NONE 1
+#define LT_CLASS_INTEGER 2
+#define LT_CLASS_SSE 3
+#define LT_CLASS_MEMORY 4096
+
+#endif // !STACK_H
diff --git a/src/thread.c b/src/thread.c
new file mode 100644
index 0000000..70ff1ef
--- /dev/null
+++ b/src/thread.c
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2008, 2009 Jiri Olsa <olsajiri@gmail.com>
+
+ This file is part of the latrace.
+
+ The latrace is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ The latrace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the latrace (file COPYING). If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+
+
+struct lt_thread *lt_thread_add(struct lt_config_app *cfg, int fd, pid_t pid)
+{
+ struct lt_thread *t;
+
+ if (NULL == (t = (struct lt_thread*) malloc(sizeof(struct lt_thread)))) {
+ perror("malloc failed");
+ return NULL;
+ }
+
+ memset(t, 0x0, sizeof(*t));
+
+ if (-1 == lt_stats_alloc(cfg, t)) {
+ free(t);
+ return NULL;
+ }
+
+ t->fifo_fd = fd;
+ t->tid = pid;
+ gettimeofday(&t->tv_start, NULL);
+
+ t->next = cfg->threads;
+ cfg->threads = t;
+ return t;
+}
+
+struct lt_thread *lt_thread_first(struct lt_config_app *cfg)
+{
+ return cfg->iter = cfg->threads;
+}
+
+struct lt_thread *lt_thread_next(struct lt_config_app *cfg)
+{
+ return cfg->iter = (cfg->iter ? cfg->iter->next : NULL);
+}