summaryrefslogtreecommitdiff
path: root/src/lib/eina
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 /src/lib/eina
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
Diffstat (limited to 'src/lib/eina')
-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
11 files changed, 1001 insertions, 22 deletions
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