Fix to not run gdbserver.
[nethome.git] / src / itrace.c
1 /*
2 gcc -o itrace.so -shared -fPIC -Wall -ggdb2 itrace.c -ldw
3 -finstrument-functions
4 LD_PRELOAD=./itrace.so
5 */
6
7 #include <stdio.h>
8 #include <elfutils/libdwfl.h>
9 #include <assert.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13
14 #define PREFIX "itrace: "
15 #define INDENT_MAX 20
16
17 static Dwfl *
18 dwfl_get (void)
19 {
20   static char *debuginfo_path;
21   static Dwfl *dwfl;
22
23   static const Dwfl_Callbacks proc_callbacks =
24    {
25      .find_debuginfo = dwfl_standard_find_debuginfo,
26      .debuginfo_path = &debuginfo_path,
27
28      .find_elf = dwfl_linux_proc_find_elf,
29    };
30
31   if (dwfl == NULL)
32     {
33       dwfl = dwfl_begin (&proc_callbacks);
34       assert (dwfl != NULL);
35
36       errno = 0;
37       if (dwfl_linux_proc_report (dwfl, getpid ()) != 0
38          || dwfl_report_end (dwfl, NULL, NULL) != 0)
39         {
40           fprintf (stderr, "dwfl reporting: %m\n");
41           dwfl_end (dwfl);
42           dwfl = NULL;
43           abort ();
44         }
45     }
46   return dwfl;
47 }
48
49 struct getmodules_callback_arg
50   {
51     void *addr;
52     const char *name;
53   };
54
55 static int getmodules_callback (Dwfl_Module *module,
56                                 void **module_userdata_pointer,
57                                 const char *module_name,
58                                 Dwarf_Addr module_low_addr, void *arg_voidp)
59 {
60   struct getmodules_callback_arg *arg = arg_voidp;
61
62   arg->name = dwfl_module_addrname (module, (GElf_Addr) arg->addr);
63   return arg->name ? DWARF_CB_ABORT : DWARF_CB_OK;
64 }
65
66 static const char *
67 addr_lookup (void *addr)
68 {
69   Dwfl *dwfl = dwfl_get ();
70   struct getmodules_callback_arg arg;
71
72   arg.name = NULL;
73   arg.addr = addr;
74   dwfl_getmodules (dwfl, getmodules_callback, &arg, 0);
75
76   return arg.name;
77 }
78
79 static const char *
80 addr_print (void *addr)
81 {
82   const char *name;
83
84   name = addr_lookup (addr);
85   if (!name)
86     {
87       static char buf[20];
88
89       sprintf (buf, "%p", addr);
90       name = buf;
91     }
92   return name;
93 }
94
95 struct stack
96   {
97     void *this_fn;
98     void *call_site;
99   };
100 static struct stack *stack;
101 static unsigned stack_at, stack_size;
102
103 void __cyg_profile_func_enter (void *this_fn, void *call_site)
104 {
105   int indent;
106
107   if (stack_at == stack_size)
108     {
109       unsigned size_new = stack_size ? stack_size * 2 : 0x100;
110       struct stack *stack_new;
111
112       stack_new = realloc (stack, sizeof (*stack) * size_new);
113       if (!stack_new)
114         {
115           fprintf (stderr, PREFIX "stack size %u allocation failure!\n",
116                    size_new);
117           return;
118         }
119       stack = stack_new;
120       stack_size = size_new;
121     }
122   stack[stack_at].this_fn = this_fn;
123   stack[stack_at].call_site = call_site;
124   stack_at++;
125
126   indent = stack_at - 1 <= INDENT_MAX ? stack_at - 1 : INDENT_MAX;
127
128   fprintf (stderr, PREFIX "%*s->%s", indent, "", addr_print (this_fn));
129   fprintf (stderr, " (%s)\n", addr_print (call_site));
130 }
131
132 void __cyg_profile_func_exit  (void *this_fn, void *call_site)
133 {
134   int indent;
135
136   indent = stack_at - 1 <= INDENT_MAX ? stack_at - 1 : INDENT_MAX;
137
138   fprintf (stderr, PREFIX "%*s<-%s", indent, "", addr_print (this_fn));
139   fprintf (stderr, " (%s)\n", addr_print (call_site));
140
141   if (!stack_at)
142     {
143       fprintf (stderr, PREFIX "stack empty!\n");
144       return;
145     }
146   if (stack[stack_at - 1].this_fn != this_fn
147       || stack[stack_at - 1].call_site != call_site)
148     {
149       fprintf (stderr, PREFIX "Unexpected function leave, resetting the stack\n");
150       stack_at = 0;
151       return;
152     }
153   stack_at--;
154 }