summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Haitzler (Rasterman) <raster@rasterman.com>2015-05-05 11:35:16 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2015-05-08 14:13:17 +0900
commit664708b817ab0cdc7177df3743b5d9c9ab7dd2b0 (patch)
tree04becb4abc16c79b2383629df16c661b20a36407
parent38faeacee1a1aa10eabebb52edfaf91e2a2c158b (diff)
eina - start a much improved eina dbug infra and have eina_log use it
this makes eina_log give bt's for all error logs. this is very useful in finding just where a problem happens. the problem int he past is that these have not been too useful due to backtrace_symbols() being "useless". thus use the eina_btlog tool i added too. also started infra for a debug monitor that can use the backtrace infra to collect runtime stats ANY TIME for a process (don't need to run under a debugger). @feat
-rw-r--r--configure.ac8
-rw-r--r--src/Makefile_Efl.am17
-rw-r--r--src/Makefile_Eina.am25
-rw-r--r--src/bin/efl/.gitignore2
-rw-r--r--src/bin/efl/efl_debug.c179
-rw-r--r--src/bin/efl/efl_debugd.c219
-rw-r--r--src/bin/eina/.gitignore1
-rw-r--r--src/bin/eina/eina_btlog.c198
-rw-r--r--src/lib/eina/eina_debug.c69
-rw-r--r--src/lib/eina/eina_debug.h86
-rw-r--r--src/lib/eina/eina_debug_bt.c31
-rw-r--r--src/lib/eina/eina_debug_bt_file.c145
-rw-r--r--src/lib/eina/eina_debug_chunk.c228
-rw-r--r--src/lib/eina/eina_debug_monitor.c320
-rw-r--r--src/lib/eina/eina_debug_proto.c19
-rw-r--r--src/lib/eina/eina_debug_thread.c80
-rw-r--r--src/lib/eina/eina_log.c32
-rw-r--r--src/lib/eina/eina_main.c2
-rw-r--r--src/lib/eina/eina_thread.c11
19 files changed, 1648 insertions, 24 deletions
diff --git a/configure.ac b/configure.ac
index 63cc54ddce..ab7661560a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -849,6 +849,12 @@ case "${build_profile}" in
849 ;; 849 ;;
850esac 850esac
851 851
852PKG_CHECK_MODULES(UNWIND, [libunwind libunwind-generic],
853 [have_unwind=yes], [have_unwind=no])
854AS_IF([test "x$have_unwind" = "xyes"],
855 [AC_DEFINE([HAVE_UNWIND], [1], [Have libunwind])])
856AM_CONDITIONAL(HAVE_UNWIND, test "x$have_unwind" = "xyes")
857
852EINA_CONFIG([HAVE_ALLOCA_H], [test "x${ac_cv_working_alloca_h}" = "xyes"]) 858EINA_CONFIG([HAVE_ALLOCA_H], [test "x${ac_cv_working_alloca_h}" = "xyes"])
853EINA_CONFIG([SAFETY_CHECKS], [test "x${have_safety_checks}" = "xyes"]) 859EINA_CONFIG([SAFETY_CHECKS], [test "x${have_safety_checks}" = "xyes"])
854EINA_CONFIG([DEFAULT_MEMPOOL], [test "x${want_default_mempool}" = "xyes"]) 860EINA_CONFIG([DEFAULT_MEMPOOL], [test "x${want_default_mempool}" = "xyes"])
@@ -4801,7 +4807,7 @@ echo " Cryptography..: ${build_crypto}"
4801echo " X11...........: ${with_x11}" 4807echo " X11...........: ${with_x11}"
4802echo " OpenGL........: ${with_opengl}" 4808echo " OpenGL........: ${with_opengl}"
4803echo " C++11.........: ${have_cxx11}" 4809echo " C++11.........: ${have_cxx11}"
4804echo "Eina............: yes (${features_eina})" 4810echo "Eina............: yes (${features_eina} unwind=$have_unwind)"
4805echo "Eo..............: yes (${features_eo})" 4811echo "Eo..............: yes (${features_eo})"
4806echo "Eolian..........: yes (${features_eolian})" 4812echo "Eolian..........: yes (${features_eolian})"
4807echo "Emile...........: yes (${features_emile})" 4813echo "Emile...........: yes (${features_emile})"
diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am
index cf6310a00c..b80a46c210 100644
--- a/src/Makefile_Efl.am
+++ b/src/Makefile_Efl.am
@@ -67,3 +67,20 @@ installed_eflluadir = $(datadir)/elua/modules/efl
67nodist_installed_efllua_DATA = $(generated_efl_lua_all) 67nodist_installed_efllua_DATA = $(generated_efl_lua_all)
68 68
69endif 69endif
70
71### Binary
72
73bin_PROGRAMS += \
74bin/efl/efl_debugd \
75bin/efl/efl_debug
76
77bin_efl_efl_debugd_SOURCES = bin/efl/efl_debugd.c
78bin_efl_efl_debugd_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@
79bin_efl_efl_debugd_LDADD = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
80bin_efl_efl_debugd_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
81
82bin_efl_efl_debug_SOURCES = bin/efl/efl_debug.c
83bin_efl_efl_debug_CPPFLAGS = -I$(top_builddir)/src/bin/efl @EINA_CFLAGS@ @ECORE_CFLAGS@ @ECORE_CON_CFLAGS@
84bin_efl_efl_debug_LDADD = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
85bin_efl_efl_debug_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@ @USE_ECORE_CON_INTERNAL_LIBS@
86
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 3591b10de1..a92b758de1 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -101,6 +101,14 @@ lib/eina/eina_convert.c \
101lib/eina/eina_counter.c \ 101lib/eina/eina_counter.c \
102lib/eina/eina_cow.c \ 102lib/eina/eina_cow.c \
103lib/eina/eina_cpu.c \ 103lib/eina/eina_cpu.c \
104lib/eina/eina_debug.h \
105lib/eina/eina_debug.c \
106lib/eina/eina_debug_chunk.c \
107lib/eina/eina_debug_bt.c \
108lib/eina/eina_debug_bt_file.c \
109lib/eina/eina_debug_thread.c \
110lib/eina/eina_debug_monitor.c \
111lib/eina/eina_debug_proto.c \
104lib/eina/eina_error.c \ 112lib/eina/eina_error.c \
105lib/eina/eina_file_common.h \ 113lib/eina/eina_file_common.h \
106lib/eina/eina_file_common.c \ 114lib/eina/eina_file_common.c \
@@ -221,15 +229,30 @@ endif
221 229
222lib_eina_libeina_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ 230lib_eina_libeina_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
223@EINA_CFLAGS@ \ 231@EINA_CFLAGS@ \
232@UNWIND_CFLAGS@ \
224-DPACKAGE_BIN_DIR=\"$(bindir)\" \ 233-DPACKAGE_BIN_DIR=\"$(bindir)\" \
225-DPACKAGE_LIB_DIR=\"$(libdir)\" \ 234-DPACKAGE_LIB_DIR=\"$(libdir)\" \
226-DPACKAGE_DATA_DIR=\"$(datadir)/eina\" \ 235-DPACKAGE_DATA_DIR=\"$(datadir)/eina\" \
227@VALGRIND_CFLAGS@ 236@VALGRIND_CFLAGS@
228 237
229lib_eina_libeina_la_LIBADD = @EINA_LIBS@ @DL_LIBS@ 238lib_eina_libeina_la_LIBADD = @EINA_LIBS@ @DL_LIBS@ @UNWIND_LIBS@
230lib_eina_libeina_la_DEPENDENCIES = @EINA_INTERNAL_LIBS@ @DL_INTERNAL_LIBS@ 239lib_eina_libeina_la_DEPENDENCIES = @EINA_INTERNAL_LIBS@ @DL_INTERNAL_LIBS@
231lib_eina_libeina_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ 240lib_eina_libeina_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@
232 241
242### Binaries
243
244bin_PROGRAMS += bin/eina/eina_btlog
245
246bin_eina_eina_btlog_SOURCES = bin/eina/eina_btlog.c
247bin_eina_eina_btlog_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
248-DPACKAGE_BIN_DIR=\"$(bindir)\" \
249-DPACKAGE_LIB_DIR=\"$(libdir)\" \
250-DPACKAGE_DATA_DIR=\"$(datadir)/eina\" \
251@EINA_CFLAGS@
252
253bin_eina_eina_btlog_LDADD = @USE_EINA_LIBS@
254bin_eina_eina_btlog_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@
255
233### Script 256### Script
234 257
235bin_SCRIPTS += scripts/eina/eina-bench-cmp 258bin_SCRIPTS += scripts/eina/eina-bench-cmp
diff --git a/src/bin/efl/.gitignore b/src/bin/efl/.gitignore
new file mode 100644
index 0000000000..b38a2ee5ea
--- /dev/null
+++ b/src/bin/efl/.gitignore
@@ -0,0 +1,2 @@
1efl_debugd
2efl_debug
diff --git a/src/bin/efl/efl_debug.c b/src/bin/efl/efl_debug.c
new file mode 100644
index 0000000000..b1cd730ea4
--- /dev/null
+++ b/src/bin/efl/efl_debug.c
@@ -0,0 +1,179 @@
1#include <Eina.h>
2#include <Ecore.h>
3#include <Ecore_Con.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <string.h>
8
9static unsigned char *buf;
10static unsigned int buf_size;
11
12static int my_argc;
13static char **my_argv;
14static const char *expect = NULL;
15
16static Ecore_Con_Server *svr;
17
18static void
19_proto(unsigned char *d, unsigned int size)
20{
21 if (size >= 4)
22 {
23 char *cmd = (char *)d;
24
25 if (!strncmp(cmd, "CLST", 4))
26 {
27 int i, n;
28
29 n = (size - 4) / sizeof(int);
30 if (n < 10000)
31 {
32 int *pids = malloc(n * sizeof(int));
33 memcpy(pids, d + 4, n * sizeof(int));
34 for (i = 0; i < n; i++)
35 {
36 if (pids[i] > 0) printf("%i\n", pids[i]);
37 }
38 free(pids);
39 }
40 }
41 if ((expect) && (!strncmp(cmd, expect, 4)))
42 ecore_main_loop_quit();
43 }
44}
45
46
47static Eina_Bool
48_server_proto(void)
49{
50 unsigned int size, newsize;
51 unsigned char *b;
52 if (!buf) return EINA_FALSE;
53 if (buf_size < 4) return EINA_FALSE;
54 memcpy(&size, buf, 4);
55 if (buf_size < (size + 4)) return EINA_FALSE;
56 _proto(buf + 4, size);
57 newsize = buf_size - (size + 4);
58 if (buf_size == newsize)
59 {
60 free(buf);
61 buf = NULL;
62 buf_size = 0;
63 }
64 else
65 {
66 b = malloc(newsize);
67 memcpy(b, buf + size + 4, newsize);
68 free(buf);
69 buf = b;
70 buf_size = newsize;
71 }
72 return EINA_TRUE;
73}
74
75Eina_Bool
76_server_add(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Add *ev)
77{
78 int i;
79 for (i = 1; i < my_argc; i++)
80 {
81 if (!strcmp(my_argv[i], "list"))
82 {
83 unsigned int size = 4;
84 char *head = "LIST";
85 expect = "CLST";
86 ecore_con_server_send(svr, &size, 4);
87 ecore_con_server_send(svr, head, 4);
88 }
89 else if ((!strcmp(my_argv[i], "pon")) &&
90 (i < (my_argc - 2)))
91 {
92 unsigned int size = 12;
93 char *head = "PLON";
94 int pid = atoi(my_argv[i + 1]);
95 unsigned int freq = atoi(my_argv[i + 2]);
96 i++;
97 ecore_con_server_send(svr, &size, 4);
98 ecore_con_server_send(svr, head, 4);
99 ecore_con_server_send(svr, &pid, 4);
100 ecore_con_server_send(svr, &freq, 4);
101 ecore_main_loop_quit();
102 }
103 else if ((!strcmp(my_argv[i], "poff")) &&
104 (i < (my_argc - 1)))
105 {
106 unsigned int size = 8;
107 char *head = "PLOFF";
108 int pid = atoi(my_argv[i + 1]);
109 i++;
110 ecore_con_server_send(svr, &size, 4);
111 ecore_con_server_send(svr, head, 4);
112 ecore_con_server_send(svr, &pid, 4);
113 ecore_main_loop_quit();
114 }
115 }
116 return ECORE_CALLBACK_RENEW;
117}
118
119Eina_Bool
120_server_del(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Del *ev)
121{
122 ecore_main_loop_quit();
123 return ECORE_CALLBACK_RENEW;
124}
125
126static Eina_Bool
127_server_data(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Data *ev)
128{
129 if (!buf)
130 {
131 buf = malloc(ev->size);
132 if (buf)
133 {
134 buf_size = ev->size;
135 memcpy(buf, ev->data, ev->size);
136 }
137 }
138 else
139 {
140 unsigned char *b = realloc(buf, buf_size + ev->size);
141 if (b)
142 {
143 buf = b;
144 memcpy(buf + buf_size, ev->data, ev->size);
145 buf_size += ev->size;
146 }
147 }
148 while (_server_proto());
149 return ECORE_CALLBACK_RENEW;
150}
151
152int
153main(int argc, char **argv)
154{
155 eina_init();
156 ecore_init();
157 ecore_con_init();
158
159 my_argc = argc;
160 my_argv = argv;
161
162 svr = ecore_con_server_connect(ECORE_CON_LOCAL_USER, "efl_debug", 0, NULL);
163 if (!svr)
164 {
165 fprintf(stderr, "ERROR: Cannot connetc to debug daemon.\n");
166 return -1;
167 }
168
169 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb)_server_add, NULL);
170 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, (Ecore_Event_Handler_Cb)_server_del, NULL);
171 ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb)_server_data, NULL);
172
173 ecore_main_loop_begin();
174 ecore_con_server_flush(svr);
175
176 ecore_con_shutdown();
177 ecore_shutdown();
178 eina_shutdown();
179}
diff --git a/src/bin/efl/efl_debugd.c b/src/bin/efl/efl_debugd.c
new file mode 100644
index 0000000000..7f369ac636
--- /dev/null
+++ b/src/bin/efl/efl_debugd.c
@@ -0,0 +1,219 @@
1#include <Eina.h>
2#include <Ecore.h>
3#include <Ecore_Con.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <string.h>
8
9typedef struct _Client Client;
10
11struct _Client
12{
13 Ecore_Con_Client *client;
14 int version;
15 pid_t pid;
16 unsigned char *buf;
17 unsigned int buf_size;
18};
19
20static Ecore_Con_Server *svr = NULL;
21static Eina_List *clients = NULL;
22
23static void
24_proto(Client *c, unsigned char *d, unsigned int size)
25{
26 if (size >= 4)
27 {
28 char *cmd = (char *)d;
29
30 if (!strncmp(cmd, "HELO", 4))
31 {
32 int version;
33 int pid;
34
35 memcpy(&version, d + 4, 4);
36 memcpy(&pid, d + 8, 4);
37 c->version = version;
38 c->pid = pid;
39 }
40 else if (!strncmp(cmd, "LIST", 4))
41 {
42 int n = eina_list_count(clients), i;
43 unsigned int *pids, size2;
44 Client *c2;
45 Eina_List *l;
46 char *head = "CLST";
47
48 pids = malloc(n * sizeof(int));
49 i = 0;
50 size2 = 4 + (n * sizeof(int));
51 EINA_LIST_FOREACH(clients, l, c2)
52 {
53 pids[i] = c2->pid;
54 i++;
55 }
56 ecore_con_client_send(c->client, &size2, 4);
57 ecore_con_client_send(c->client, head, 4);
58 ecore_con_client_send(c->client, pids, n * sizeof(int));
59 free(pids);
60 }
61 else if (!strncmp(cmd, "PLON", 4))
62 {
63 int pid;
64 unsigned int freq = 1000;
65 Client *c2;
66 Eina_List *l;
67
68 memcpy(&pid, d + 4, 4);
69 memcpy(&freq, d + 8, 4);
70 if (pid > 0)
71 {
72 EINA_LIST_FOREACH(clients, l, c2)
73 {
74 if (c2->pid == pid)
75 {
76 unsigned int size2 = 8;
77
78 ecore_con_client_send(c2->client, &size2, 4);
79 ecore_con_client_send(c2->client, d, 4);
80 ecore_con_client_send(c2->client, &freq, 4);
81 break;
82 }
83 }
84 }
85 }
86 else if (!strncmp(cmd, "PLOF", 4))
87 {
88 int pid;
89 Client *c2;
90 Eina_List *l;
91
92 memcpy(&pid, d + 4, 4);
93 if (pid > 0)
94 {
95 EINA_LIST_FOREACH(clients, l, c2)
96 {
97 if (c2->pid == pid)
98 {
99 unsigned int size2 = 4;
100
101 ecore_con_client_send(c2->client, &size2, 4);
102 ecore_con_client_send(c2->client, d, 4);
103 break;
104 }
105 }
106 }
107 }
108 }
109}
110
111static Eina_Bool
112_client_proto(Client *c)
113{
114 unsigned int size, newsize;
115 unsigned char *b;
116 if (!c->buf) return EINA_FALSE;
117 if (c->buf_size < 4) return EINA_FALSE;
118 memcpy(&size, c->buf, 4);
119 if (c->buf_size < (size + 4)) return EINA_FALSE;
120 _proto(c, c->buf + 4, size);
121 newsize = c->buf_size - (size + 4);
122 if (c->buf_size == newsize)
123 {
124 free(c->buf);
125 c->buf = NULL;
126 c->buf_size = 0;
127 }
128 else
129 {
130 b = malloc(newsize);
131 memcpy(b, c->buf + size + 4, newsize);
132 free(c->buf);
133 c->buf = b;
134 c->buf_size = newsize;
135 }
136 return EINA_TRUE;
137}
138
139static Eina_Bool
140_client_add(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Add *ev)
141{
142 Client *c = calloc(1, sizeof(Client));
143 if (c)
144 {
145 c->client = ev->client;
146 clients = eina_list_append(clients, c);
147 ecore_con_client_data_set(c->client, c);
148 }
149 return ECORE_CALLBACK_RENEW;
150}
151
152static Eina_Bool
153_client_del(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Del *ev)
154{
155 Client *c = ecore_con_client_data_get(ev->client);
156 if (c)
157 {
158 clients = eina_list_remove(clients, c);
159 free(c);
160 }
161 return ECORE_CALLBACK_RENEW;
162}
163
164static Eina_Bool
165_client_data(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Client_Data *ev)
166{
167 Client *c = ecore_con_client_data_get(ev->client);
168 if (c)
169 {
170 if (!c->buf)
171 {
172 c->buf = malloc(ev->size);
173 if (c->buf)
174 {
175 c->buf_size = ev->size;
176 memcpy(c->buf, ev->data, ev->size);
177 }
178 }
179 else
180 {
181 unsigned char *b = realloc(c->buf, c->buf_size + ev->size);
182 if (b)
183 {
184 c->buf = b;
185 memcpy(c->buf + c->buf_size, ev->data, ev->size);
186 c->buf_size += ev->size;
187 }
188 }
189 while (_client_proto(c));
190 }
191 return ECORE_CALLBACK_RENEW;
192}
193
194int
195main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
196{
197 eina_init();
198 ecore_init();
199 ecore_con_init();
200
201 svr = ecore_con_server_add(ECORE_CON_LOCAL_USER, "efl_debug", 0, NULL);
202 if (!svr)
203 {
204 fprintf(stderr, "ERROR: Cannot create debug daemon.\n");
205 return -1;
206 }
207
208 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb)_client_add, NULL);
209 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb)_client_del, NULL);
210 ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb)_client_data, NULL);
211
212 ecore_main_loop_begin();
213
214 ecore_con_server_del(svr);
215
216 ecore_con_shutdown();
217 ecore_shutdown();
218 eina_shutdown();
219}
diff --git a/src/bin/eina/.gitignore b/src/bin/eina/.gitignore
new file mode 100644
index 0000000000..ec4ebaa080
--- /dev/null
+++ b/src/bin/eina/.gitignore
@@ -0,0 +1 @@
eina_btlog
diff --git a/src/bin/eina/eina_btlog.c b/src/bin/eina/eina_btlog.c
new file mode 100644
index 0000000000..2b4d5303ab
--- /dev/null
+++ b/src/bin/eina/eina_btlog.c
@@ -0,0 +1,198 @@
1#include <Eina.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5
6// right now this is quick and dirty and may have some parsing ... frailty,
7// so don't put malicious data through it... :) but cat in eina bt's through
8// this to get a nicely clean and readable bt with filenames of binaries,
9// shared objects, source files, and line numbers. even nicely colored and
10// columnated. this is more the start of a bunch of debug tools for efl to make
11// it easier to identify issues.
12//
13// how to use:
14//
15// cat mybacktrace.txt | eina_btlog
16//
17// (or just run it and copy & paste in on stdin - what i do mostly, and out
18// pops a nice backtrace, hit ctrl+d to end)
19
20typedef struct _Bt Bt;
21
22struct _Bt
23{
24 char *bin_dir;
25 char *bin_name;
26 char *file_dir;
27 char *file_name;
28 char *func_name;
29 int line;
30};
31
32static void
33path_split(const char *path, char **dir, char **file)
34{
35 const char *p = strrchr(path, '/');
36
37 if (!path)
38 {
39 *dir = NULL;
40 *file = NULL;
41 return;
42 }
43 if (!p)
44 {
45 *dir = NULL;
46 *file = strdup(path);
47 return;
48 }
49 *dir = malloc(p - path + 1);
50 if (!dir)
51 {
52 *dir = NULL;
53 *file = NULL;
54 return;
55 }
56 strncpy(*dir, path, p - path);
57 (*dir)[p - path] = 0;
58 *file = strdup(p + 1);
59}
60
61static Eina_Bool
62_addr2line(const char *bin_dir, const char *bin_name, unsigned long long addr,
63 char **file_dir, char **file_name, char **func_name, int *file_line)
64{
65 char buf[4096], func[4096], *f1 = NULL, *f2 = NULL;
66 Eina_Bool ok = EINA_FALSE;
67 int line;
68 FILE *p;
69
70 snprintf(buf, sizeof(buf), "addr2line -f -e %s/%s -C -a 0x%llx",
71 bin_dir, bin_name, addr);
72 p = popen(buf, "r");
73 if (!p) return EINA_FALSE;
74 fscanf(p, "%s\n", buf);
75 if (fscanf(p, "%s\n", func) == 1)
76 {
77 if (fscanf(p, "%[^:]:%i\n", buf, &line) == 2)
78 {
79 path_split(buf, &(f1), &(f2));
80 if ((!f1) || (!f2))
81 {
82 free(f1);
83 free(f2);
84 pclose(p);
85 return EINA_FALSE;
86 }
87 }
88 else
89 {
90 f1 = strdup("??");
91 f2 = strdup("??");
92 }
93 *file_dir = f1;
94 *file_name = f2;
95 *func_name = strdup(func);
96 *file_line = line;
97 ok = EINA_TRUE;
98 }
99 pclose(p);
100 return ok;
101}
102
103static Eina_List *
104bt_append(Eina_List *btl, const char *btline)
105{
106 Bt *bt = calloc(1, sizeof(Bt));
107 if (!bt) return btl;
108 char *bin = strdup(btline);
109 unsigned long long offset = 0, base = 0;
110
111 // parse:
112 // /usr/local/lib/libeina.so.1 0x1ec88
113 // /usr/local/lib/libelementary.so.1 0x10f695
114 // /usr/local/lib/libeo.so.1 0xa474
115 // /usr/local/lib/libelementary.so.1 0x139bd6
116 // /usr/local/bin/elementary_test 0x8196d
117 // /usr/local/bin/elementary_test 0x81b6a
118 if (sscanf(btline, "%s %llx %llx", bin, &offset, &base) == 3)
119 {
120 path_split(bin, &(bt->bin_dir), &(bt->bin_name));
121 if (!bt->bin_dir) bt->bin_dir = strdup("");
122 if (!bt->bin_name) bt->bin_name = strdup("");
123 if (!_addr2line(bt->bin_dir, bt->bin_name, offset - base,
124 &(bt->file_dir), &(bt->file_name),
125 &(bt->func_name), &(bt->line)))
126 {
127 if (!_addr2line(bt->bin_dir, bt->bin_name, offset,
128 &(bt->file_dir), &(bt->file_name),
129 &(bt->func_name), &(bt->line)))
130 {
131 bt->file_dir = strdup("");
132 bt->file_name = strdup("");
133 bt->func_name = strdup("");
134 }
135 }
136 btl = eina_list_append(btl, bt);
137 }
138 free(bin);
139 return btl;
140}
141
142int
143main(int argc, char **argv)
144{
145 Eina_List *btl = NULL, *l;
146 char buf[4096];
147 Bt *bt;
148 int cols[6] = { 0 }, len, i;
149
150 eina_init();
151 while (fgets(buf, sizeof(buf) - 1, stdin))
152 {
153 btl = bt_append(btl, buf);
154 }
155 EINA_LIST_FOREACH(btl, l, bt)
156 {
157 len = strlen(bt->bin_dir);
158 if (len > cols[0]) cols[0] = len;
159 len = strlen(bt->bin_name);
160 if (len > cols[1]) cols[1] = len;
161
162 len = strlen(bt->file_dir);
163 if (len > cols[2]) cols[2] = len;
164 len = strlen(bt->file_name);
165 if (len > cols[3]) cols[3] = len;
166
167 snprintf(buf, sizeof(buf), "%i", bt->line);
168 len = strlen(buf);
169 if (len > cols[4]) cols[4] = len;
170
171 len = strlen(bt->func_name);
172 if (len > cols[5]) cols[5] = len;
173 }
174 EINA_LIST_FOREACH(btl, l, bt)
175 {
176 len = strlen(bt->bin_dir);
177 for (i = 0; i < (cols[0] - len); i++) printf(" ");
178 printf("\033[34m%s\033[01m\033[36m/\033[37m%s\033[0m",
179 bt->bin_dir, bt->bin_name);
180 len = strlen(bt->bin_name);
181 for (i = 0; i < (cols[1] - len); i++) printf(" ");
182 printf(" | ");
183 len = strlen(bt->file_dir);
184 for (i = 0; i < (cols[2] - len); i++) printf(" ");
185 printf("\033[34m%s\033[01m\033[36m/\033[37m%s\033[0m",
186 bt->file_dir, bt->file_name);
187 len = strlen(bt->file_name);
188 for (i = 0; i < (cols[3] - len); i++) printf(" ");
189
190 printf(" : ");
191 snprintf(buf, sizeof(buf), "%i", bt->line);
192 len = strlen(buf);
193 for (i = 0; i < (cols[4] - len); i++) printf(" ");
194 printf("\033[01m\033[33m%s\033[0m @ \033[32m%s\033[36m()", buf, bt->func_name);
195 printf("\033[0m\n");
196 }
197 return 0;
198}
diff --git a/src/lib/eina/eina_debug.c b/src/lib/eina/eina_debug.c
new file mode 100644
index 0000000000..766ec7ac3c
--- /dev/null
+++ b/src/lib/eina/eina_debug.c
@@ -0,0 +1,69 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5extern pthread_t _eina_debug_thread_mainloop;
6extern pthread_t *_eina_debug_thread_active;
7extern int _eina_debug_thread_active_num;
8
9
10// yes - a global debug spinlock. i expect contention to be low for now, and
11// when needed we can split this up into mroe locks to reduce contention when
12// and if that day comes
13Eina_Spinlock _eina_debug_lock;
14
15static Eina_Bool _inited = EINA_FALSE;
16
17Eina_Bool
18eina_debug_init(void)
19{
20 pthread_t self;
21
22 if (_inited)
23 {
24 eina_spinlock_release(&_eina_debug_thread_lock);
25 return EINA_TRUE;
26 }
27 _inited = EINA_TRUE;
28 eina_spinlock_new(&_eina_debug_lock);
29 eina_spinlock_new(&_eina_debug_thread_lock);
30 eina_semaphore_new(&_eina_debug_monitor_return_sem, 0);
31 self = pthread_self();
32 _eina_debug_thread_mainloop_set(&self);
33#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
34 // if we are setuid - don't debug!
35 if (getuid() != geteuid()) return EINA_TRUE;
36#endif
37 if (getenv("EFL_NODEBUG")) return EINA_TRUE;
38 _eina_debug_monitor_service_connect();
39 if (_eina_debug_monitor_service_fd >= 0)
40 {
41 _eina_debug_monitor_service_greet();
42 _eina_debug_monitor_signal_init();
43 _eina_debug_monitor_thread_start();
44 }
45 return EINA_TRUE;
46}
47
48Eina_Bool
49eina_debug_shutdown(void)
50{
51 eina_spinlock_take(&_eina_debug_thread_lock);
52 // yes - we never free on shutdown - this is because the monitor thread
53 // never exits. this is not a leak - we intend to never free up any
54 // resources here because they are allocated once only ever.
55 return EINA_TRUE;
56}
57#else
58Eina_Bool
59eina_debug_init(void)
60{
61 return EINA_TRUE;
62}
63
64Eina_Bool
65eina_debug_shutdown(void)
66{
67 return EINA_TRUE;
68}
69#endif
diff --git a/src/lib/eina/eina_debug.h b/src/lib/eina/eina_debug.h
new file mode 100644
index 0000000000..f4494af741
--- /dev/null
+++ b/src/lib/eina/eina_debug.h
@@ -0,0 +1,86 @@
1#ifndef EINA_DEBUG_H_
2# define EINA_DEBUG_H_
3
4# ifdef HAVE_CONFIG_H
5# include "config.h"
6# endif
7
8# include <stdio.h>
9# include <string.h>
10# include <stdlib.h>
11# include <unistd.h>
12# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_DLADDR) && defined(HAVE_UNWIND)
13# include <execinfo.h>
14# ifndef _GNU_SOURCE
15# define _GNU_SOURCE 1
16# endif
17# include <errno.h>
18# include <stdio.h>
19# include <string.h>
20# include <unistd.h>
21# include <dlfcn.h>
22# include <sys/select.h>
23# include <sys/time.h>
24# include <sys/types.h>
25# include <sys/stat.h>
26# include <pthread.h>
27# include <signal.h>
28# include <time.h>
29# include <sys/types.h>
30# include <sys/stat.h>
31# include <sys/socket.h>
32# include <sys/un.h>
33# include <fcntl.h>
34# include <libunwind.h>
35
36# include "eina_config.h"
37# include "eina_private.h"
38# include "eina_inlist.h"
39# include "eina_lock.h"
40# include "eina_thread.h"
41# include "eina_convert.h"
42# include "eina_strbuf.h"
43# include "eina_safety_checks.h"
44# include "eina_log.h"
45# include "eina_inline_private.h"
46
47# define EINA_HAVE_DEBUG 1
48
49# define EINA_MAX_BT 256
50
51extern Eina_Spinlock _eina_debug_lock;
52extern Eina_Spinlock _eina_debug_thread_lock;
53extern Eina_Semaphore _eina_debug_monitor_return_sem;
54extern int _eina_debug_monitor_service_fd;
55
56void _eina_debug_thread_add(void *th);
57void _eina_debug_thread_del(void *th);
58void _eina_debug_thread_mainloop_set(void *th);
59
60void *_eina_debug_chunk_push(int size);
61void *_eina_debug_chunk_realloc(int size);
62char *_eina_debug_chunk_strdup(const char *str);
63void *_eina_debug_chunk_tmp_push(int size);
64void _eina_debug_chunk_tmp_reset(void);
65
66const char *_eina_debug_file_get(const char *fname);
67
68void _eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen);
69
70void _eina_debug_monitor_thread_start(void);
71void _eina_debug_monitor_signal_init(void);
72void _eina_debug_monitor_service_connect(void);
73
74void _eina_debug_monitor_service_greet(void);
75
76# define EINA_BT(file) \
77 do { \
78 void *bt[EINA_MAX_BT]; \
79 int btlen = backtrace((void **)bt, EINA_MAX_BT); \
80 _eina_debug_dump_fhandle_bt(file, bt, btlen); \
81 } while (0)
82# else
83# define EINA_BT(file) do { } while (0)
84# endif
85
86#endif
diff --git a/src/lib/eina/eina_debug_bt.c b/src/lib/eina/eina_debug_bt.c
new file mode 100644
index 0000000000..7f1beb1a61
--- /dev/null
+++ b/src/lib/eina/eina_debug_bt.c
@@ -0,0 +1,31 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5void
6_eina_debug_dump_fhandle_bt(FILE *f, void **bt, int btlen)
7{
8 int i;
9 Dl_info info;
10 const char *file;
11 unsigned long long offset, base;
12
13 for (i = 0; i < btlen; i++)
14 {
15 file = NULL;
16 offset = base = 0;
17 // we have little choice but to hgope/assume dladdr() doesn't alloc
18 // anything here
19 if ((dladdr(bt[i], &info)) && (info.dli_fname[0]))
20 {
21 offset = (unsigned long long)bt[i];
22 base = (unsigned long long)info.dli_fbase;
23 file = _eina_debug_file_get(info.dli_fname);
24 }
25 // rely on normal libc buffering for file ops to avoid syscalls.
26 // may or may not be a good idea. good enough for now.
27 if (file) fprintf(f, "%s\t 0x%llx 0x%llx\n", file, offset, base);
28 else fprintf(f, "??\t -\n");
29 }
30}
31#endif
diff --git a/src/lib/eina/eina_debug_bt_file.c b/src/lib/eina/eina_debug_bt_file.c
new file mode 100644
index 0000000000..9ed5a7cf8a
--- /dev/null
+++ b/src/lib/eina/eina_debug_bt_file.c
@@ -0,0 +1,145 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5static unsigned int _table_num = 0;
6static unsigned int _table_size = 0;
7static const char **_table = NULL;
8
9// a very simple "fast lookup" of a filename to a path. we expect this table
10// of lookups to remain very small as it most likely includes just the
11// application executable itself as this was run from $PATH or using
12// a relative path relative to cwd of shell at time of exec. this is really
13// the only likely content, but just in case, handle more. it is much faster
14// than going through systemcalls like realpath() every time (well this libc
15// function will at least be a system call or use system calls to do its work)
16static const char *
17_eina_debug_file_lookup(const char *fname)
18{
19 unsigned int n;
20
21 if (!_table) return NULL;
22 for (n = 0; _table[n]; n += 2)
23 {
24 if (!strcmp(_table[n], fname)) return _table[n + 1];
25 }
26 return NULL;
27}
28
29// record a new filename -> path entry in our table. the table really is just
30// odd/even strings like fname, path, fname2, path2, fnamr3, path3, ...
31// and we are unlikely to have much more than 1 entry here. see above
32static const char *
33_eina_debug_file_store(const char *fname, const char *file)
34{
35 static const char **table2;
36
37 _table_num += 2;
38 if (_table_num >= _table_size)
39 {
40 _table_size += 32;
41 table2 = _eina_debug_chunk_realloc(_table_size * sizeof(const char *));
42 if (!table2) return NULL;
43 _table = table2;
44 }
45 _table[_table_num - 2] = _eina_debug_chunk_strdup(fname);
46 _table[_table_num - 1] = _eina_debug_chunk_strdup(file);
47 return _table[_table_num - 1];
48}
49
50// do a "fast lookup" of a filename to a file path for debug output. this
51// relies on caching to avoid system calls and assumes that once we know
52// the full path of a given filename, we can know its full path reliably.
53// if we can't we'd be in trouble anyway as the filename and path lookup
54// failure is due maybe to a deleted file or renamed file and then we are
55// going to have a bad day either way.
56const char *
57_eina_debug_file_get(const char *fname)
58{
59 char buf[4096];
60 const char *file;
61 static const char **path = NULL;
62 static char *pathstrs = NULL;
63
64 // no filename provided
65 if ((!fname) || (!fname[0])) return NULL;
66 // it's a full path so return as-is
67 if (fname[0] == '/') return fname;
68 // first look in cache for filename -> full path lookup and if
69 // there, return that (yes - assuming filesystem paths doesn't change
70 // which is unlikely as they were set up at star most likely)
71 eina_spinlock_take(&_eina_debug_lock);
72 file = _eina_debug_file_lookup(fname);
73 eina_spinlock_release(&_eina_debug_lock);
74 if (file) return file;
75
76 // store PATH permanently - yes. if it changes runtime it will break.
77 // for speed reasons we need to assume it won't change. store path broken
78 // down into an array of ptrs to strings with NULL ptr at the end. this
79 // will only execute once as an "init" for a breoken up path so it should
80 // not matter speed-wise
81 eina_spinlock_take(&_eina_debug_lock);
82 if (!path)
83 {
84 unsigned int n;
85 char *p1, *p2;
86 const char *p;
87 const char *pathstr = getenv("PATH");
88
89 if (!pathstr) return NULL;
90 // dup the entire env as we will rpelace : with 0 bytes to break str
91 pathstrs = _eina_debug_chunk_strdup(pathstr);
92 for (n = 0, p = pathstr; *p;)
93 {
94 n++;
95 p = strchr(p, ':');
96 if (!p) break;
97 p++;
98 }
99 path = _eina_debug_chunk_push(sizeof(const char *) * (n + 1));
100 for (n = 0, p1 = pathstrs; *p1; n++)
101 {
102 path[n] = p1;
103 p2 = strchr(p1, ':');
104 if (!p2) break;
105 *p2 = 0;
106 p1 = p2 + 1;
107 }
108 path[n] = NULL;
109 }
110 eina_spinlock_release(&_eina_debug_lock);
111
112 // a relative path - resolve with realpath. due to the cache store above
113 // we shouldn't have to do this very often
114 if ((!strncmp(fname, "./", 2)) || (!strncmp(fname, "../", 3)))
115 { // relative path
116 if (realpath(fname, buf)) file = buf;
117 else file = NULL;
118 }
119 // search in $PATH for the file then - this should also be very rare as
120 // we will store and cache results permanently
121 else if (path)
122 {
123 struct stat st;
124 unsigned int n;
125
126 for (n = 0; path[n]; n++)
127 {
128 snprintf(buf, sizeof(buf), "%s/%s", path[n], fname);
129 if (stat(buf, &st) == 0)
130 {
131 file = buf;
132 break;
133 }
134 }
135 }
136 // if it's found - store it in cache for later
137 if (file)
138 {
139 eina_spinlock_take(&_eina_debug_lock);
140 file = _eina_debug_file_store(fname, file);
141 eina_spinlock_release(&_eina_debug_lock);
142 }
143 return file;
144}
145#endif
diff --git a/src/lib/eina/eina_debug_chunk.c b/src/lib/eina/eina_debug_chunk.c
new file mode 100644
index 0000000000..3351ce19a7
--- /dev/null
+++ b/src/lib/eina/eina_debug_chunk.c
@@ -0,0 +1,228 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5# ifdef HAVE_MMAP
6# include <sys/mman.h>
7# endif
8
9// custom memory allocators to avoid malloc/free during backtrace handling
10// just in case we're inside some signal handler due to mem corruption and
11// are inside a malloc/free lock and thus would deadlock ourselves if we
12// allocated memory, so implement scratch space just big enough for what we
13// need and then some via either a static 8k+4k buffer pair or via a growable
14// mmaped mem chunk pair
15# ifdef HAVE_MMAP
16// implement using mmap so we can grow if needed - unlikelt though
17static unsigned char *chunk1 = NULL;
18static unsigned char *chunk2 = NULL;
19static unsigned char *chunk3 = NULL;
20static int chunk1_size = 0;
21static int chunk1_num = 0;
22static int chunk2_size = 0;
23static int chunk2_num = 0;
24static int chunk3_size = 0;
25static int chunk3_num = 0;
26
27// get a new chunk of "anonymous mmaped memory"
28static void *
29_eina_debug_chunk_need(int size)
30{
31 void *ptr;
32
33 ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
34 MAP_PRIVATE | MAP_ANON, -1, 0);
35 if (ptr == MAP_FAILED) return NULL;
36 return ptr;
37}
38
39// release a chunk of this mmaped anon mem if we don't need it anymore
40static void
41_eina_debug_chunk_noneed(void *ptr, int size)
42{
43 munmap(ptr, size);
44}
45
46// push a new bit of mem on our growing stack of mem - given our workload,
47// we never free anything here, only ever grow new things on this stack
48void *
49_eina_debug_chunk_push(int size)
50{
51 void *ptr;
52
53 // no initial chunk1 block - allocate it
54 if (!chunk1)
55 {
56 chunk1 = _eina_debug_chunk_need(8 * 1024);
57 if (!chunk1) return NULL;
58 chunk1_size = 8 * 1024;
59 }
60 // round size up to the nearest pointer size for alignment
61 size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
62 // if our chunk is too small - grow it
63 if ((chunk1_num + size) > chunk1_size)
64 {
65 // get a new chunk twice as big
66 void *newchunk = _eina_debug_chunk_need(chunk1_size * 2);
67 if (!newchunk) return NULL;
68 // copy content over
69 memcpy(newchunk, chunk1, chunk1_num);
70 // release old chunk
71 _eina_debug_chunk_noneed(chunk1, chunk1_size);
72 // switch to our new 2x as big chunk
73 chunk1 = newchunk;
74 chunk1_size = chunk1_size * 2;
75 }
76 // get the mem at the top of this stack and return it, then move along
77 ptr = chunk1 + chunk1_num;
78 chunk1_num += size;
79 return ptr;
80}
81
82// grow a single existing chunk (we use this for the filename -> path lookup)
83void *
84_eina_debug_chunk_realloc(int size)
85{
86 // we have a null/empty second chunk - allocate one
87 if (!chunk2)
88 {
89 chunk2 = _eina_debug_chunk_need(4 * 1024);
90 if (!chunk2) return NULL;
91 chunk2_size = 4 * 1024;
92 }
93 // if our chunk is too small - grow it
94 if (size > chunk2_size)
95 {
96 // get a new chunk twice as big
97 void *newchunk = _eina_debug_chunk_need(chunk2_size * 2);
98 if (!newchunk) return NULL;
99 // copy content over
100 memcpy(newchunk, chunk2, chunk2_num);
101 // release old chunk
102 _eina_debug_chunk_noneed(chunk2, chunk2_size);
103 // switch to our new 2x as big chunk
104 chunk2 = newchunk;
105 chunk2_size = chunk2_size * 2;
106 }
107 // record new size and return chunk ptr as we just re-use it
108 chunk2_num = size;
109 return chunk2;
110}
111
112// grow a single existing chunk (we use this for the filename -> path lookup)
113void *
114_eina_debug_chunk_tmp_push(int size)
115{
116 void *ptr;
117
118 // no initial chunk1 block - allocate it
119 if (!chunk3)
120 {
121 chunk3 = _eina_debug_chunk_need(32 * 1024);
122 if (!chunk3) return NULL;
123 chunk3_size = 32 * 1024;
124 }
125 // round size up to the nearest pointer size for alignment
126 size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
127 // if our chunk is too small - grow it
128 if ((chunk3_num + size) > chunk3_size)
129 {
130 // get a new chunk twice as big
131 void *newchunk = _eina_debug_chunk_need(chunk3_size * 2);
132 if (!newchunk) return NULL;
133 // copy content over
134 memcpy(newchunk, chunk3, chunk3_num);
135 // release old chunk
136 _eina_debug_chunk_noneed(chunk3, chunk3_size);
137 // switch to our new 2x as big chunk
138 chunk3 = newchunk;
139 chunk3_size = chunk3_size * 2;
140 }
141 // get the mem at the top of this stack and return it, then move along
142 ptr = chunk3 + chunk3_num;
143 chunk3_num += size;
144 return ptr;
145}
146
147void
148_eina_debug_chunk_tmp_reset(void)
149{
150 chunk3_num = 0;
151}
152# else
153// implement with static buffers - once we exceed these we will fail. sorry
154// maybe one day find another solution, but these buffers should be enough
155// for now for thos eplatforms (like windows) where we can't do the mmap
156// tricks above.
157static unsigned char chunk1[8 * 1024];
158static unsigned char chunk2[4 * 1024];
159static unsigned char chunk3[128 * 1024];
160static int chunk1_size = sizeof(chunk1);
161static int chunk1_num = 0;
162static int chunk2_size = sizeof(chunk2);
163static int chunk2_num = 0;
164static int chunk3_size = sizeof(chunk3);
165static int chunk3_num = 0;
166
167// push a new bit of mem on our growing stack of mem - given our workload,
168// we never free anything here, only ever grow new things on this stack
169void *
170_eina_debug_chunk_push(int size)
171{
172 void *ptr;
173
174 // round size up to the nearest pointer size for alignment
175 size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
176 // if we ran out of space - fail
177 if ((chunk1_num + size) > chunk1_size) return NULL;
178 // get the mem at the top of this stack and return it, then move along
179 ptr = chunk1 + chunk1_num;
180 chunk1_num += size;
181 return ptr;
182}
183
184// grow a single existing chunk (we use this for the filename -> path lookup)
185void *
186_eina_debug_chunk_realloc(int size)
187{
188 // if we ran out of space - fail
189 if (size > chunk2_size) return NULL;
190 // record new size and return chunk ptr as we just re-use it
191 chunk2_num = size;
192 return chunk2;
193}
194
195// grow a single existing chunk (we use this for the filename -> path lookup)
196void *
197_eina_debug_chunk_tmp_push(int size)
198{
199 void *ptr;
200
201 // round size up to the nearest pointer size for alignment
202 size = sizeof(void *) * ((size + sizeof(void *) - 1) / sizeof(void *));
203 // if we ran out of space - fail
204 if ((chunk3_num + size) > chunk3_size) return NULL;
205 // get the mem at the top of this stack and return it, then move along
206 ptr = chunk3 + chunk1_num;
207 chunk3_num += size;
208 return ptr;
209}
210
211void
212_eina_debug_chunk_tmp_reset(void)
213{
214 chunk3_num = 0;
215}
216# endif
217
218// handy - duplicate a string on our growing stack - never expect to free it
219char *
220_eina_debug_chunk_strdup(const char *str)
221{
222 int len = strlen(str);
223 char *s = _eina_debug_chunk_push(len + 1);
224 if (!s) return NULL;
225 strcpy(s, str);
226 return s;
227}
228#endif
diff --git a/src/lib/eina/eina_debug_monitor.c b/src/lib/eina/eina_debug_monitor.c
new file mode 100644
index 0000000000..74833f3a87
--- /dev/null
+++ b/src/lib/eina/eina_debug_monitor.c
@@ -0,0 +1,320 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5#define DEBUG_SERVER ".ecore/efl_debug/0"
6
7extern pthread_t _eina_debug_thread_mainloop;
8extern volatile pthread_t *_eina_debug_thread_active;
9extern volatile int _eina_debug_thread_active_num;
10
11int _eina_debug_monitor_service_fd = -1;
12Eina_Semaphore _eina_debug_monitor_return_sem;
13
14static Eina_Bool _monitor_thread_runs = EINA_FALSE;
15static pthread_t _monitor_thread;
16
17// _bt_buf[0] is always for mainloop, 1 + is for extra threads
18static void ***_bt_buf;
19static int *_bt_buf_len;
20static struct timespec *_bt_ts;
21static int *_bt_cpu;
22
23static inline int
24_eina_debug_unwind_bt(void **bt, int max)
25{
26 unw_cursor_t cursor;
27 unw_context_t uc;
28 unw_word_t p;
29 int total;
30
31 unw_getcontext(&uc);
32 unw_init_local(&cursor, &uc);
33 for (total = 0; (unw_step(&cursor) > 0) && (total < max); total++)
34 {
35 unw_get_reg(&cursor, UNW_REG_IP, &p);
36 bt[total] = (void *)p;
37 }
38 return total;
39}
40
41static void
42_eina_debug_signal(int sig EINA_UNUSED,
43 siginfo_t *si EINA_UNUSED,
44 void *foo EINA_UNUSED)
45{
46 int i, slot = 0;
47 pthread_t self = pthread_self();
48 clockid_t cid;
49 // XXX: use pthread_getcpuclockid() to get cpu time used since last poll
50 //
51 // clockid_t cid;
52 // struct timespec ts ts;
53 // pthread_getcpuclockid(pthread_self(), &cid);
54 // clock_gettime(cid, &ts);
55 // printf("%4ld.%03ld\n", ts.tv_sec, ts.tv_nsec / 1000000);
56 //
57 // also get current cpu with:
58 // getcpu()
59 if (self != _eina_debug_thread_mainloop)
60 {
61 for (i = 0; i < _eina_debug_thread_active_num; i++)
62 {
63 if (self == _eina_debug_thread_active[i])
64 {
65 slot = i + 1;
66 goto found;
67 }
68 }
69 fprintf(stderr, "EINA DEBUG ERROR: can't find thread slot!\n");
70 eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
71 return;
72 }
73found:
74// printf("dump into slot %i for %p\n", slot, (void *)self);
75 _bt_cpu[slot] = sched_getcpu();
76 pthread_getcpuclockid(self, &cid);
77 clock_gettime(cid, &(_bt_ts[slot]));
78// _bt_buf_len[slot] = backtrace(_bt_buf[slot], EINA_MAX_BT);
79 _bt_buf_len[slot] = _eina_debug_unwind_bt(_bt_buf[slot], EINA_MAX_BT);
80 eina_semaphore_release(&_eina_debug_monitor_return_sem, 1);
81}
82
83#define SIG SIGPROF
84//#define SIG ((SIGRTMIN + SIGRTMAX) / 2)
85
86static inline double
87get_time(void)
88{
89 struct timeval timev;
90 gettimeofday(&timev, NULL);
91 return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
92}
93
94static void
95_eina_debug_collect_bt(pthread_t pth)
96{
97 // this async signals the thread to switch to the deebug signal handler
98 // and collect a backtrace and other info from inside the thread
99 pthread_kill(pth, SIG);
100}
101
102// this is a DEDICATED debug thread to monitor the application so it works
103// even if the mainloop is blocked or the app otherwise deadlocked in some
104// way. this is an alternative to using external debuggers so we can get
105// users or developers to get useful information about an app at all times
106static void *
107_eina_debug_monitor(void *data EINA_UNUSED)
108{
109 int bts = 0, ret, max_fd;
110 double t0, t;
111 fd_set rfds, wfds, exfds;
112 struct timeval tv = { 0 };
113 unsigned int poll_time = 1000;
114 Eina_Bool poll_on = EINA_FALSE;
115
116 t0 = get_time();
117 for (;;)
118 {
119 int i;
120
121 FD_ZERO(&rfds);
122 FD_ZERO(&wfds);
123 FD_ZERO(&exfds);
124 FD_SET(_eina_debug_monitor_service_fd, &rfds);
125 max_fd = _eina_debug_monitor_service_fd;
126 if (poll_on)
127 {
128 if ((tv.tv_sec == 0) && (tv.tv_usec == 0))
129 {
130 tv.tv_sec = 0;
131 tv.tv_usec = poll_time;
132 }
133 ret = select(max_fd + 1, &rfds, &wfds, &exfds, &tv);
134 }
135 else ret = select(max_fd + 1, &rfds, &wfds, &exfds, NULL);
136 if ((ret == 1) && (FD_ISSET(_eina_debug_monitor_service_fd, &rfds)))
137 {
138 unsigned int size;
139 int rret;
140
141 // XXX: handle protocol
142 rret = read(_eina_debug_monitor_service_fd, &size, 4);
143 if ((rret == 4) && (size > 0) && (size < 63356))
144 {
145 char *buf = alloca(size);
146
147 rret = read(_eina_debug_monitor_service_fd, buf, size);
148 if ((rret == (int)size) && (size >= 4))
149 {
150 if (!strncmp(buf, "PLON", 4))
151 {
152 if (size >= 8) memcpy(&poll_time, buf + 4, 4);
153 poll_on = EINA_TRUE;
154 }
155 else if (!strncmp(buf, "PLOF", 4))
156 {
157 poll_time = 1000;
158 poll_on = EINA_FALSE;
159 }
160 else
161 fprintf(stderr, "EINA DEBUG ERROR: Uunknown command\n");
162 }
163 else
164 {
165 if (rret <= 0)
166 {
167 fprintf(stderr, "EINA DEBUG ERROR: Lost debug daemon!\n");
168 goto fail;
169 }
170 else
171 {
172 }
173 }
174 }
175 else
176 {
177 if (rret <= 0)
178 {
179 fprintf(stderr, "EINA_DEBUG ERROR: Lost debug daemon!\n");
180 goto fail;
181 }
182 else
183 {
184 fprintf(stderr, "EINA DEBUG ERROR: Invalid message size %i\n", size);
185 goto fail;
186 }
187 }
188 }
189
190 if (poll_on)
191 {
192 // take a lock on grabbing thread debug info like backtraces
193 eina_spinlock_take(&_eina_debug_thread_lock);
194 // reset our "stack" of memory se use to dump thread info into
195 _eina_debug_chunk_tmp_reset();
196 // get an array of pointers for the backtrace array for main + th
197 _bt_buf = _eina_debug_chunk_tmp_push
198 ((1 + _eina_debug_thread_active_num) * sizeof(void *));
199 if (!_bt_buf) goto err;
200 // get an array of pointers for the timespec array for mainloop + th
201 _bt_ts = _eina_debug_chunk_tmp_push
202 ((1 + _eina_debug_thread_active_num) * sizeof(struct timespec));
203 if (!_bt_ts) goto err;
204 // get an array of pointers for the cpuid array for mainloop + th
205 _bt_cpu = _eina_debug_chunk_tmp_push
206 ((1 + _eina_debug_thread_active_num) * sizeof(int));
207 if (!_bt_cpu) goto err;
208 // now get an array of void pts for mainloop bt
209 _bt_buf[0] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
210 if (!_bt_buf[0]) goto err;
211 // get an array of void ptrs for each thread we know about for bt
212 for (i = 0; i < _eina_debug_thread_active_num; i++)
213 {
214 _bt_buf[i + 1] = _eina_debug_chunk_tmp_push(EINA_MAX_BT * sizeof(void *));
215 if (!_bt_buf[i + 1]) goto err;
216 }
217 // get an array of ints to stor the bt len for mainloop + threads
218 _bt_buf_len = _eina_debug_chunk_tmp_push
219 ((1 + _eina_debug_thread_active_num) * sizeof(int));
220 // collect bt from the mainloop - always there
221 _eina_debug_collect_bt(_eina_debug_thread_mainloop);
222 // now collect per thread
223 for (i = 0; i < _eina_debug_thread_active_num; i++)
224 _eina_debug_collect_bt(_eina_debug_thread_active[i]);
225 // we're done probing. now collec all the "i'm done" msgs on the
226 // semaphore for every thread + mainloop
227 for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
228 eina_semaphore_lock(&_eina_debug_monitor_return_sem);
229 // we now have gotten all the data from all threadd + mainloop.
230 // we can process it now as we see fit, so release thread lock
231// for (i = 0; i < (_eina_debug_thread_active_num + 1); i++)
232// {
233// _eina_debug_dump_fhandle_bt(stderr, _bt_buf[i], _bt_buf_len[i]);
234// }
235err:
236 eina_spinlock_release(&_eina_debug_thread_lock);
237 bts++;
238 if (bts >= 10000)
239 {
240 t = get_time();
241 fprintf(stderr, "%1.5f bt's per sec\n", (double)bts / (t - t0));
242 t0 = t;
243 bts = 0;
244 }
245 }
246 }
247fail:
248 close(_eina_debug_monitor_service_fd);
249 _eina_debug_monitor_service_fd = -1;
250 return NULL;
251}
252
253// start up the debug monitor if we haven't already
254void
255_eina_debug_monitor_thread_start(void)
256{
257 int err;
258
259 if (_monitor_thread_runs) return;
260 // XXX: set up socket conn to debug daemon and then have thread deal with
261 // it from there on
262 err = pthread_create(&_monitor_thread, NULL, _eina_debug_monitor, NULL);
263 if (err != 0)
264 {
265 fprintf(stderr, "EINA DEBUG ERROR: Can't create debug thread!\n");
266 abort();
267 }
268 else _monitor_thread_runs = EINA_TRUE;
269}
270
271void
272_eina_debug_monitor_signal_init(void)
273{
274 struct sigaction sa;
275
276 sa.sa_sigaction = _eina_debug_signal;
277 sa.sa_flags = SA_RESTART | SA_SIGINFO;
278 sigemptyset(&sa.sa_mask);
279 if (sigaction(SIG, &sa, NULL) != 0)
280 fprintf(stderr, "EINA DEBUG ERROR: Can't set up sig %i handler!\n", SIG);
281}
282
283static const char *
284_socket_home_get()
285{
286 const char *dir = getenv("XDG_RUNTIME_DIR");
287 if (!dir) dir = getenv("HOME");
288 if (!dir) dir = getenv("TMPDIR");
289 if (!dir) dir = "/tmp";
290 return dir;
291}
292
293void
294_eina_debug_monitor_service_connect(void)
295{
296 char buf[4096];
297 int fd, socket_unix_len, curstate = 0;
298 struct sockaddr_un socket_unix;
299
300 snprintf(buf, sizeof(buf), "%s/%s", _socket_home_get(), DEBUG_SERVER);
301 fd = socket(AF_UNIX, SOCK_STREAM, 0);
302 if (fd < 0) goto err;
303 if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) goto err;
304 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&curstate,
305 sizeof(curstate)) < 0)
306 goto err;
307 socket_unix.sun_family = AF_UNIX;
308 strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
309#define LENGTH_OF_SOCKADDR_UN(s) \
310 (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
311 socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
312 if (connect(fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
313 goto err;
314 _eina_debug_monitor_service_fd = fd;
315 return;
316err:
317 close(fd);
318 return;
319}
320#endif
diff --git a/src/lib/eina/eina_debug_proto.c b/src/lib/eina/eina_debug_proto.c
new file mode 100644
index 0000000000..44ab536664
--- /dev/null
+++ b/src/lib/eina/eina_debug_proto.c
@@ -0,0 +1,19 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5void
6_eina_debug_monitor_service_greet(void)
7{
8 const char *hello = "HELO";
9 unsigned int version = 1;
10 unsigned int msize = 4 + 4 + 4;
11 unsigned int pid = getpid();
12 unsigned char buf[16];
13 memcpy(buf + 0, &msize, 4);
14 memcpy(buf + 4, hello, 4);
15 memcpy(buf + 8, &version, 4);
16 memcpy(buf + 12, &pid, 4);
17 write(_eina_debug_monitor_service_fd, buf, sizeof(buf));
18}
19#endif
diff --git a/src/lib/eina/eina_debug_thread.c b/src/lib/eina/eina_debug_thread.c
new file mode 100644
index 0000000000..e3c8d7a389
--- /dev/null
+++ b/src/lib/eina/eina_debug_thread.c
@@ -0,0 +1,80 @@
1#include "eina_debug.h"
2
3#ifdef EINA_HAVE_DEBUG
4
5// a really simple store of currently known active threads. the mainloop is
6// special and inittied at debug init time - assuming eina inits in the
7// mainloop thread (whihc is expected). also a growable array of thread
8// id's for other threads is held here so we can loop over them and do things
9// like get them to stop and dump a backtrace for us
10Eina_Spinlock _eina_debug_thread_lock;
11
12pthread_t _eina_debug_thread_mainloop = 0;
13pthread_t *_eina_debug_thread_active = NULL;
14int _eina_debug_thread_active_num = 0;
15
16static int _thread_active_size = 0;
17
18// add a thread id to our tracking array - very simple. add to end, and
19// if array to small, reallocate it to be bigger by 16 slots AND double that
20// size (so grows should slow down FAST). we will never shrink this array
21void
22_eina_debug_thread_add(void *th)
23{
24 pthread_t *pth = th;
25 // take thread tracking lock
26 eina_spinlock_take(&_eina_debug_thread_lock);
27 // if we don't have enough space to store thread id's - make some more
28 if (_thread_active_size < (_eina_debug_thread_active_num + 1))
29 {
30 pthread_t *threads = realloc
31 (_eina_debug_thread_active,
32 ((_eina_debug_thread_active_num + 16) * 2) * sizeof(pthread_t *));
33 if (threads)
34 {
35 _eina_debug_thread_active = threads;
36 _thread_active_size = (_eina_debug_thread_active_num + 16) * 2;
37 }
38 }
39 // add new thread id to the end
40 _eina_debug_thread_active[_eina_debug_thread_active_num] = *pth;
41 _eina_debug_thread_active_num++;
42 // release our lock cleanly
43 eina_spinlock_release(&_eina_debug_thread_lock);
44}
45
46// remove a thread id from our tracking array - simply find and shuffle all
47// later elements down. this array should be small almsot all the time and
48// shouldn't bew changing THAT often for this to matter
49void
50_eina_debug_thread_del(void *th)
51{
52 pthread_t *pth = th;
53 int i;
54 // take a thread tracking lock
55 eina_spinlock_take(&_eina_debug_thread_lock);
56 // find the thread id to remove
57 for (i = 0; i < _eina_debug_thread_active_num; i++)
58 {
59 if (_eina_debug_thread_active[i] == *pth)
60 {
61 // found it - now shuffle down all further thread id's in array
62 for (; i < (_eina_debug_thread_active_num - 1); i++)
63 _eina_debug_thread_active[i] = _eina_debug_thread_active[i + 1];
64 // reduce our counter and get out of loop
65 _eina_debug_thread_active_num--;
66 break;
67 }
68 }
69 // release lock cleanly
70 eina_spinlock_release(&_eina_debug_thread_lock);
71}
72
73// register the thread that is the mainloop - always there
74void
75_eina_debug_thread_mainloop_set(void *th)
76{
77 pthread_t *pth = th;
78 _eina_debug_thread_mainloop = *pth;
79}
80#endif
diff --git a/src/lib/eina/eina_log.c b/src/lib/eina/eina_log.c
index c3174ff3a5..5f458f01d0 100644
--- a/src/lib/eina/eina_log.c
+++ b/src/lib/eina/eina_log.c
@@ -29,11 +29,6 @@
29#include <assert.h> 29#include <assert.h>
30#include <errno.h> 30#include <errno.h>
31 31
32#if defined HAVE_EXECINFO_H && defined HAVE_BACKTRACE && defined HAVE_BACKTRACE_SYMBOLS
33# include <execinfo.h>
34# define EINA_LOG_BACKTRACE
35#endif
36
37#ifdef HAVE_SYSTEMD 32#ifdef HAVE_SYSTEMD
38# include <systemd/sd-journal.h> 33# include <systemd/sd-journal.h>
39#endif 34#endif
@@ -42,6 +37,11 @@
42# include <Evil.h> 37# include <Evil.h>
43#endif 38#endif
44 39
40#include "eina_debug.h"
41#ifdef EINA_HAVE_DEBUG
42# define EINA_LOG_BACKTRACE
43#endif
44
45#include "eina_config.h" 45#include "eina_config.h"
46#include "eina_private.h" 46#include "eina_private.h"
47#include "eina_inlist.h" 47#include "eina_inlist.h"
@@ -121,7 +121,7 @@ static Eina_Bool _disable_timing = EINA_TRUE;
121static int _abort_level_on_critical = EINA_LOG_LEVEL_CRITICAL; 121static int _abort_level_on_critical = EINA_LOG_LEVEL_CRITICAL;
122 122
123#ifdef EINA_LOG_BACKTRACE 123#ifdef EINA_LOG_BACKTRACE
124static int _backtrace_level = -1; 124static int _backtrace_level = 999;
125#endif 125#endif
126 126
127static Eina_Bool _threads_enabled = EINA_FALSE; 127static Eina_Bool _threads_enabled = EINA_FALSE;
@@ -1849,21 +1849,11 @@ eina_log_domain_registered_level_set(int domain, int level)
1849} 1849}
1850 1850
1851#ifdef EINA_LOG_BACKTRACE 1851#ifdef EINA_LOG_BACKTRACE
1852# define DISPLAY_BACKTRACE(File, Level) \ 1852# define DISPLAY_BACKTRACE(File, Level) \
1853 if (EINA_UNLIKELY(Level < _backtrace_level)) \ 1853 if (EINA_UNLIKELY(Level < _backtrace_level)) { \
1854 { \ 1854 fprintf(File, "*** Backtrace ***\n"); \
1855 void *bt[256]; \ 1855 EINA_BT(File); \
1856 char **strings; \ 1856 }
1857 int btlen; \
1858 int i; \
1859 \
1860 btlen = backtrace((void **)bt, 256); \
1861 strings = backtrace_symbols((void **)bt, btlen); \
1862 fprintf(File, "*** Backtrace ***\n"); \
1863 for (i = 0; i < btlen; ++i) \
1864 fprintf(File, "%s\n", strings[i]); \
1865 free(strings); \
1866 }
1867#else 1857#else
1868# define DISPLAY_BACKTRACE(File, Level) 1858# define DISPLAY_BACKTRACE(File, Level)
1869#endif 1859#endif
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index ea85b30b80..358c3a4bfb 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -120,6 +120,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
120 */ 120 */
121#define S(x) extern Eina_Bool eina_ ## x ## _init(void); \ 121#define S(x) extern Eina_Bool eina_ ## x ## _init(void); \
122 extern Eina_Bool eina_ ## x ## _shutdown(void) 122 extern Eina_Bool eina_ ## x ## _shutdown(void)
123 S(debug);
123 S(log); 124 S(log);
124 S(error); 125 S(error);
125 S(safety_checks); 126 S(safety_checks);
@@ -165,6 +166,7 @@ struct eina_desc_setup
165static const struct eina_desc_setup _eina_desc_setup[] = { 166static const struct eina_desc_setup _eina_desc_setup[] = {
166#define S(x) {# x, eina_ ## x ## _init, eina_ ## x ## _shutdown} 167#define S(x) {# x, eina_ ## x ## _init, eina_ ## x ## _shutdown}
167 /* log is a special case as it needs printf */ 168 /* log is a special case as it needs printf */
169 S(debug),
168 S(stringshare), 170 S(stringshare),
169 S(error), 171 S(error),
170 S(safety_checks), 172 S(safety_checks),
diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c
index 2924fc7360..86cb8a932b 100644
--- a/src/lib/eina/eina_thread.c
+++ b/src/lib/eina/eina_thread.c
@@ -29,6 +29,8 @@
29/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ 29/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
30#include "eina_safety_checks.h" 30#include "eina_safety_checks.h"
31 31
32#include "eina_debug.h"
33
32# include <pthread.h> 34# include <pthread.h>
33# include <errno.h> 35# include <errno.h>
34 36
@@ -100,13 +102,20 @@ _eina_internal_call(void *context)
100{ 102{
101 Eina_Thread_Call *c = context; 103 Eina_Thread_Call *c = context;
102 void *r; 104 void *r;
105 pthread_t self;
103 106
104 if (c->prio == EINA_THREAD_BACKGROUND || 107 if (c->prio == EINA_THREAD_BACKGROUND ||
105 c->prio == EINA_THREAD_IDLE) 108 c->prio == EINA_THREAD_IDLE)
106 eina_sched_prio_drop(); 109 eina_sched_prio_drop();
107 110
108 /* FIXME: set priority and affinity */ 111 self = pthread_self();
112#ifdef EINA_HAVE_DEBUG
113 _eina_debug_thread_add(&self);
114#endif
109 r = c->func((void*) c->data, eina_thread_self()); 115 r = c->func((void*) c->data, eina_thread_self());
116#ifdef EINA_HAVE_DEBUG
117 _eina_debug_thread_del(&self);
118#endif
110 119
111 free(c); 120 free(c);
112 121