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