1 /* EINA - EFL data type library
2 * Copyright (C) 2015 Carsten Haitzler
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #ifdef HAVE_VALGRIND
24 # include <valgrind.h>
25 # include <memcheck.h>
26 #endif
27
28 #include "Eina.h"
29 #include "eina_evlog.h"
30 #include "eina_debug.h"
31
32 #if defined(__APPLE__) && defined(__MACH__)
33 # include <mach/mach_time.h>
34 #endif
35
36 #include <time.h>
37 #include <unistd.h>
38
39 #ifdef _WIN32
40 # include <evil_private.h> /* mmap */
41 #else
42 # include <sys/mman.h>
43 #endif
44
45 #if __BYTE_ORDER == __LITTLE_ENDIAN
46 #define SWAP_64(x) x
47 #define SWAP_32(x) x
48 #define SWAP_16(x) x
49 #define SWAP_DBL(x) x
50 #else
51 #define SWAP_64(x) eina_swap64(x)
52 #define SWAP_32(x) eina_swap32(x)
53 #define SWAP_16(x) eina_swap16(x)
54 #define SWAP_DBL(x) SWAP_64(x)
55 #endif
56
57 # define EVLOG_BUF_SIZE (4 * (1024 * 1024))
58
59 static Eina_Spinlock _evlog_lock;
60 static int _evlog_go = 0;
61
62 static Eina_Evlog_Buf *buf; // current event log we are writing events to
63 static Eina_Evlog_Buf buffers[2]; // double-buffer our event log buffers
64
65 #if defined (HAVE_CLOCK_GETTIME)
66 static clockid_t _eina_evlog_time_clock_id = -1;
67 #elif defined(__APPLE__) && defined(__MACH__)
68 static double _eina_evlog_time_clock_conversion = 1e-9;
69 #endif
70
71 static int _evlog_get_opcode = EINA_DEBUG_OPCODE_INVALID;
72
73 static inline double
get_time(void)74 get_time(void)
75 {
76 #if defined (HAVE_CLOCK_GETTIME)
77 struct timespec t;
78
79 if (EINA_UNLIKELY(clock_gettime(_eina_evlog_time_clock_id, &t)))
80 {
81 struct timeval timev;
82 gettimeofday(&timev, NULL);
83 return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
84 }
85 return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
86 #elif defined(_WIN32)
87 return evil_time_get();
88 #elif defined(__APPLE__) && defined(__MACH__)
89 return _eina_evlog_time_clock_conversion * (double)mach_absolute_time();
90 #else
91 struct timeval timev;
92 gettimeofday(&timev, NULL);
93 return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000.0);
94 #endif
95 }
96
97 static void
alloc_buf(Eina_Evlog_Buf * b,unsigned int size)98 alloc_buf(Eina_Evlog_Buf *b, unsigned int size)
99 {
100 if (b->buf) return;
101 b->size = size;
102 b->top = 0;
103 #ifdef HAVE_MMAP
104 # ifdef HAVE_VALGRIND
105 if (RUNNING_ON_VALGRIND) b->buf = malloc(size);
106 else
107 # endif
108 {
109 b->buf = mmap(NULL, size, PROT_READ | PROT_WRITE,
110 MAP_PRIVATE | MAP_ANON, -1, 0);
111 if (b->buf == MAP_FAILED) b->buf = NULL;
112 }
113 #else
114 b->buf = malloc(size);
115 #endif
116 b->overflow = 0;
117 }
118
119 static void
free_buf(Eina_Evlog_Buf * b)120 free_buf(Eina_Evlog_Buf *b)
121 {
122 if (!b->buf) return;
123 #ifdef HAVE_MMAP
124 # ifdef HAVE_VALGRIND
125 if (RUNNING_ON_VALGRIND) free(b->buf);
126 else
127 # endif
128 munmap(b->buf, b->size);
129 #else
130 free(b->buf);
131 #endif
132 b->buf = NULL;
133 b->size = 0;
134 b->top = 0;
135 }
136
137 static inline void *
push_buf(Eina_Evlog_Buf * b,unsigned int size)138 push_buf(Eina_Evlog_Buf *b, unsigned int size)
139 {
140 void *ptr;
141
142 if (b->size < size) abort();
143 if ((b->top + size) > (b->size))
144 {
145 b->overflow++;
146 b->top = 0;
147 }
148 ptr = (b->buf + b->top);
149 b->top += size;
150 return ptr;
151 }
152
153 EAPI void
eina_evlog(const char * event,void * obj,double srctime,const char * detail)154 eina_evlog(const char *event, void *obj, double srctime, const char *detail)
155 {
156 Eina_Evlog_Item *item;
157 char *strings;
158 double now;
159 int size;
160 unsigned short detail_offset, event_size;
161
162 if (!_evlog_go) return;
163 now = get_time();
164 event_size = strlen(event) + 1;
165 size = sizeof(Eina_Evlog_Item) + event_size;
166 detail_offset = 0;
167 if (detail)
168 {
169 detail_offset = size;
170 size += strlen(detail) + 1;
171 }
172 size = sizeof(double) * ((size + sizeof(double) - 1)
173 / sizeof(double));
174 eina_spinlock_take(&_evlog_lock);
175 strings = push_buf(buf, size);
176 item = (Eina_Evlog_Item *)strings;
177 item->tim = SWAP_DBL(now);
178 item->srctim = SWAP_DBL(srctime);
179 item->thread = SWAP_64((unsigned long long)(uintptr_t)pthread_self());
180 item->obj = SWAP_64((unsigned long long)(uintptr_t)obj);
181 item->event_offset = SWAP_16(sizeof(Eina_Evlog_Item));
182 item->detail_offset = SWAP_16(detail_offset);
183 item->event_next = SWAP_16(size);
184 strcpy(strings + sizeof(Eina_Evlog_Item), event);
185 if (detail_offset > 0) strcpy(strings + detail_offset, detail);
186 eina_spinlock_release(&_evlog_lock);
187 }
188
189 EAPI Eina_Evlog_Buf *
eina_evlog_steal(void)190 eina_evlog_steal(void)
191 {
192 Eina_Evlog_Buf *stolen = NULL;
193
194 eina_spinlock_take(&_evlog_lock);
195 if (buf == &(buffers[0]))
196 {
197 buf = &(buffers[1]);
198 buf->top = 0;
199 buf->overflow = 0;
200 stolen = &(buffers[0]);
201 }
202 else
203 {
204 buf = &(buffers[0]);
205 buf->top = 0;
206 buf->overflow = 0;
207 stolen = &(buffers[1]);
208 }
209 eina_spinlock_release(&_evlog_lock);
210 return stolen;
211 }
212
213 EAPI void
eina_evlog_start(void)214 eina_evlog_start(void)
215 {
216 eina_spinlock_take(&_evlog_lock);
217 _evlog_go++;
218 if (_evlog_go == 1)
219 {
220 // alloc 2 buffers for spinning around in
221 alloc_buf(&(buffers[0]), EVLOG_BUF_SIZE);
222 alloc_buf(&(buffers[1]), EVLOG_BUF_SIZE);
223 }
224 eina_spinlock_release(&_evlog_lock);
225 }
226
227 EAPI void
eina_evlog_stop(void)228 eina_evlog_stop(void)
229 {
230 eina_spinlock_take(&_evlog_lock);
231 _evlog_go--;
232 if (_evlog_go == 0)
233 {
234 free_buf(&(buffers[0]));
235 free_buf(&(buffers[1]));
236 }
237 eina_spinlock_release(&_evlog_lock);
238 }
239
240 // get evlog
241 static Eina_Bool
_get_cb(Eina_Debug_Session * session EINA_UNUSED,int cid EINA_UNUSED,void * buffer EINA_UNUSED,int size EINA_UNUSED)242 _get_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
243 {
244 Eina_Evlog_Buf *evlog = eina_evlog_steal();
245 int resp_size = 0;
246 unsigned char *resp_buf = NULL;
247
248 if ((evlog) && (evlog->buf))
249 {
250 int ovf = SWAP_32(evlog->overflow);
251 resp_size = evlog->top + sizeof(evlog->overflow);
252 resp_buf = malloc(resp_size);
253 memcpy(resp_buf, &ovf, sizeof(ovf));
254 memcpy(resp_buf + sizeof(evlog->overflow), evlog->buf, evlog->top);
255 }
256 printf("send evlog size %d\n", resp_size);
257 eina_debug_session_send(session, cid, _evlog_get_opcode, resp_buf, resp_size);
258 free(resp_buf);
259
260 return EINA_TRUE;
261 }
262
263 // enable evlog
264 static Eina_Bool
_start_cb(Eina_Debug_Session * session EINA_UNUSED,int cid EINA_UNUSED,void * buffer EINA_UNUSED,int size EINA_UNUSED)265 _start_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
266 {
267 eina_evlog_start();
268 return EINA_TRUE;
269 }
270
271 // stop evlog
272 static Eina_Bool
_stop_cb(Eina_Debug_Session * session EINA_UNUSED,int cid EINA_UNUSED,void * buffer EINA_UNUSED,int size EINA_UNUSED)273 _stop_cb(Eina_Debug_Session *session EINA_UNUSED, int cid EINA_UNUSED, void *buffer EINA_UNUSED, int size EINA_UNUSED)
274 {
275 eina_evlog_stop();
276 return EINA_TRUE;
277 }
278
279 EINA_DEBUG_OPCODES_ARRAY_DEFINE(_EINA_DEBUG_EVLOG_OPS,
280 {"EvLog/on", NULL, &_start_cb},
281 {"EvLog/off", NULL, &_stop_cb},
282 {"EvLog/get", &_evlog_get_opcode, &_get_cb},
283 {NULL, NULL, NULL}
284 );
285
286 Eina_Bool
eina_evlog_init(void)287 eina_evlog_init(void)
288 {
289 eina_spinlock_new(&_evlog_lock);
290 buf = &(buffers[0]);
291 #if defined (HAVE_CLOCK_GETTIME)
292 {
293 struct timespec t;
294
295 if (!clock_gettime(CLOCK_MONOTONIC, &t))
296 _eina_evlog_time_clock_id = CLOCK_MONOTONIC;
297 else
298 _eina_evlog_time_clock_id = CLOCK_REALTIME;
299 }
300 #endif
301 eina_evlog("+eina_init", NULL, 0.0, NULL);
302 eina_debug_opcodes_register(NULL, _EINA_DEBUG_EVLOG_OPS(), NULL, NULL);
303 return EINA_TRUE;
304 }
305
306 Eina_Bool
eina_evlog_shutdown(void)307 eina_evlog_shutdown(void)
308 {
309 // yes - we don't free tyhe evlog buffers. they may be in used by debug th
310 eina_spinlock_free(&_evlog_lock);
311 return EINA_TRUE;
312 }
313