4a1b236286252237b9aead19d1db0c4930495852
[platform/core/system/swap-probe.git] / probe_userfunc / libdauserfunc.c
1 /*
2  *  DA probe
3  *
4  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: 
7  *
8  * Woojin Jung <woojin2.jung@samsung.com>
9  * Jaewon Lim <jaewon81.lim@samsung.com>
10  * Juyoung Kim <j0.kim@samsung.com>
11  * Anastasia Lyupa <a.lyupa@samsung.com>
12  * 
13  * This library is free software; you can redistribute it and/or modify it under
14  * the terms of the GNU Lesser General Public License as published by the
15  * Free Software Foundation; either version 2.1 of the License, or (at your option)
16  * any later version.
17  * 
18  * This library is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
21  * License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this library; if not, write to the Free Software Foundation, Inc., 51
25  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26  *
27  * Contributors:
28  * - S-Core Co., Ltd
29  * - Samsung RnD Institute Russia
30  * 
31  */
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <sys/time.h>
38 #include <time.h>
39 #include <execinfo.h>
40 #include <unistd.h>
41 #include <pthread.h>
42 #include "daprobe.h"
43 #include "probeinfo.h"
44 #include "dautil.h"
45 #include "dahelper.h"
46 #include "da_sync.h"
47
48 //#define USING_BACKTRACE
49
50 #define RETURN_ADDRESS(nr) \
51         __builtin_extract_return_addr (__builtin_return_address(nr))
52
53 #if defined(__i386__)
54 #define GET_PC(ctx)     ((void *) ctx.eip)
55 #elif defined (__arm__)
56 #define GET_PC(ctx)     ((void *) (ctx)->uc_mcontext.arm_pc)
57 #endif
58
59 #define USERFUNCTION_MAX_DEPTH                          32
60 #define PROFIL_LOG_SENDING_INTERVAL_USEC        100000 // 100 milliseconds
61 #define SAMPLE_MAX_IN_INTERVAL                          50
62 #define PROFIL_TRIM_STACK_DEPTH                         2
63
64 #define IS_FULL_SAMPLE_ARRAY    ((((sample_write_index + 1) % SAMPLE_MAX_IN_INTERVAL) == sample_read_index) ? 1 : 0)
65 #define IS_EMPTY_SAMPLE_ARRAY   ((sample_write_index == sample_read_index) ? 1 : 0)
66
67 #define CUSTOM_CB_FUNC_NAME             "_chart_timerThread"
68
69 void *custom_cb_addr = (void*)-1;
70
71 extern __thread unsigned long gSTrace;
72
73 int profil_option = 0;
74 volatile int profil_turned_on = 0;
75 volatile int profil_thread_on = 0;
76 u_long low_pc, high_pc;
77
78 typedef struct elapsed_time_t {
79         struct timeval startTime;
80         struct timeval endTime;
81         struct timeval resultTime;
82         void *self;
83 } elapsed_time;
84
85 __thread elapsed_time elapsed_time_array[USERFUNCTION_MAX_DEPTH];
86 __thread int elapsed_time_index;
87
88 pthread_t profil_log_thread;
89 pthread_mutex_t profil_log_mutex;
90
91 typedef struct sample_info_t {
92         unsigned long time;
93         void *pc;
94         int bt_size;
95         void *bt_array[MAX_STACK_DEPTH];
96 } sample_info;
97
98 int sample_seq = 0;
99 int sample_read_index = 0;
100 int sample_write_index = 0;
101 sample_info sample_info_array[SAMPLE_MAX_IN_INTERVAL];
102
103 int __profile_frequency();
104
105 /*
106 static unsigned long long getElapsedTime(struct timeval eTime)
107 {
108         return ((unsigned long long) eTime.tv_sec) * 1000000 + eTime.tv_usec;
109 }
110 */
111
112 static unsigned long getTime()
113 {
114         struct timespec ts;
115         clock_gettime(CLOCK_REALTIME, &ts);
116
117         return (unsigned long)(ts.tv_sec * 10000 + (ts.tv_nsec/100000));
118 }
119
120 #ifdef USING_BACKTRACE
121 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
122 {
123         char **strings = NULL;
124         size_t i;
125         int initsize;
126         int stringlen;
127
128         if(log == NULL)
129                 return 0;
130
131         initsize = log->length;
132         log->data[log->length] = '\0';  // is this necessary ?
133         if(likely(sample_info_array[index].bt_size > PROFIL_TRIM_STACK_DEPTH))
134         {
135                 strings = BACKTRACE_SYMBOLS(sample_info_array[index].bt_array + PROFIL_TRIM_STACK_DEPTH,
136                                 sample_info_array[index].bt_size - PROFIL_TRIM_STACK_DEPTH);
137
138                 if(likely(strings != NULL))
139                 {
140                         for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
141                         {
142                                 stringlen = strlen(strings[i - PROFIL_TRIM_STACK_DEPTH]) + 14;
143                                 if(log->length + stringlen >= bufsize + initsize)       
144                                         break;
145
146                                 log->length += sprintf(log->data + log->length, "%010u`,%s`,",
147                                                 (unsigned int)(sample_info_array[index].bt_array[i]),
148                                                 strings[i - PROFIL_TRIM_STACK_DEPTH]);
149                         }
150                         log->data[log->length-2] = '\0';
151                         log->length -= 2;
152                         free(strings);
153                 }
154                 else
155                 {
156                         for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
157                         {
158                                 stringlen = 23;
159                                 if(log->length + stringlen >= bufsize + initsize)
160                                         break;
161
162                                 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
163                                                 (unsigned int)(sample_info_array[index].bt_array[i]));
164                         }
165                         log->data[log->length-2] = '\0';
166                         log->length -= 2;
167                 }
168                 return (int)(sample_info_array[index].bt_size - PROFIL_TRIM_STACK_DEPTH);
169         }
170         else
171         {
172                 return 0;
173         }
174 }
175 #else
176 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
177 {
178         char **strings = NULL;
179         size_t i;
180         int initsize;
181         int stringlen;
182
183         if(log == NULL)
184                 return 0;
185
186         initsize = log->length;
187         log->data[log->length] = '\0';  // is this necessary ?
188         strings = BACKTRACE_SYMBOLS(sample_info_array[index].bt_array,
189                         sample_info_array[index].bt_size);
190
191         if(likely(strings != NULL))
192         {
193                 for(i = 0; i < sample_info_array[index].bt_size; i++)
194                 {
195                         stringlen = strlen(strings[i]) + 14;
196                         if(log->length + stringlen >= bufsize + initsize)       
197                                 break;
198
199                         log->length += sprintf(log->data + log->length, "%010u`,%s`,",
200                                         (unsigned int)(sample_info_array[index].bt_array[i]),
201                                         strings[i]);
202                 }
203                 log->data[log->length-2] = '\0';
204                 log->length -= 2;
205                 free(strings);
206         }
207         else
208         {
209                 for(i = 0; i < sample_info_array[index].bt_size; i++)
210                 {
211                         stringlen = 23;
212                         if(log->length + stringlen >= bufsize + initsize)
213                                 break;
214
215                         log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
216                                         (unsigned int)(sample_info_array[index].bt_array[i]));
217                 }
218                 log->data[log->length-2] = '\0';
219                 log->length -= 2;
220         }
221         return (int)(sample_info_array[index].bt_size);
222 }
223 #endif
224
225 void *profil_log_func(void *data)
226 {
227         TRACE_STATE_SET(TS_PROFIL_THREAD);
228         
229         sigset_t profsigmask;
230         sigemptyset(&profsigmask);
231         sigaddset(&profsigmask, SIGPROF);
232         pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);
233
234         while(profil_thread_on)
235         {
236                 while(!IS_EMPTY_SAMPLE_ARRAY)
237                 {
238                         sample_read_index = (sample_read_index + 1) % SAMPLE_MAX_IN_INTERVAL;
239                 }
240                 usleep(PROFIL_LOG_SENDING_INTERVAL_USEC);
241         }
242         TRACE_STATE_UNSET(TS_PROFIL_THREAD);
243
244         return NULL;
245 }
246
247 void __cyg_profile_func_enter(void *this, void *callsite)
248 {
249         probeInfo_t probeInfo;
250
251         sigset_t profsigmask, oldsigmask;
252         sigemptyset(&profsigmask);
253         sigaddset(&profsigmask, SIGPROF);
254         pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
255
256         probeBlockStart();
257         do {
258                 // remove custom chart callback function
259                 if(gTraceInfo.custom_chart_callback_count > 0)
260                 {
261                         if(custom_cb_addr == (void*)-1)
262                         {
263                                 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
264                                 if(likely(strings != NULL))
265                                 {
266                                         if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
267                                         {
268                                                 custom_cb_addr = callsite;
269                                                 free(strings);
270                                                 break;
271                                         }
272                                         free(strings);
273                                 }
274                         }
275                         else
276                         {
277                                 if(callsite == custom_cb_addr)
278                                         break;
279                         }
280                 }
281
282                 setProbePoint(&probeInfo);
283
284                 gettimeofday(&(elapsed_time_array[elapsed_time_index].startTime), NULL);
285                 elapsed_time_array[elapsed_time_index].self = this;
286                 elapsed_time_index++;
287
288         } while(0); 
289         probeBlockEnd();
290
291         pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
292         return;
293 }
294
295 void __cyg_profile_func_exit(void *this, void *callsite)
296 {
297         probeInfo_t probeInfo;
298
299         sigset_t profsigmask, oldsigmask;
300         sigemptyset(&profsigmask);
301         sigaddset(&profsigmask, SIGPROF);
302         pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
303
304         probeBlockStart();
305         do {
306                 // remove custom chart callback function
307                 if(gTraceInfo.custom_chart_callback_count > 0)
308                 {
309                         if(custom_cb_addr == (void*)-1)
310                         {
311                                 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
312                                 if(likely(strings != NULL))
313                                 {
314                                         if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
315                                         {
316                                                 custom_cb_addr = callsite;
317                                                 free(strings);
318                                                 break;
319                                         }
320                                         free(strings);
321                                 }
322                         }
323                         else
324                         {
325                                 if(callsite == custom_cb_addr)
326                                         break;
327                         }
328                 }
329
330                 elapsed_time_index--;
331
332                 if(this != elapsed_time_array[elapsed_time_index].self)
333                 { // this should never happen
334                         ;// function exit: enter/exit function matching failed!!
335                 }
336
337                 gettimeofday(&(elapsed_time_array[elapsed_time_index].endTime), NULL);
338                 timersub(&(elapsed_time_array[elapsed_time_index].endTime),
339                                 &(elapsed_time_array[elapsed_time_index].startTime),
340                                 &(elapsed_time_array[elapsed_time_index].resultTime));
341
342                 setProbePoint(&probeInfo);
343         } while(0);
344         probeBlockEnd();
345
346         pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
347         return;
348 }
349
350 #if defined(__i386__)
351 void mcount(void)
352 {
353         return;
354 }
355
356 #elif defined(__arm__)
357 void mcount_internal(u_long frompc, u_long selfpc)
358 {
359         return;
360 }
361
362 void __attribute__((__naked__)) __gnu_mcount_nc(void)
363 {
364         asm(
365                         "push {r0, r1, r2, r3, lr}\n\t"
366                         "bic r1, lr, #1\n\t"
367                         "ldr r0, [sp, #20]\n\t"
368                         "bl mcount_internal\n\t"
369                         "pop {r0, r1, r2, r3, ip, lr}\n\t"
370                         "bx ip");
371 }
372 #endif
373
374 static inline void profil_count(void *pc)
375 {
376 #if defined(__arm__)
377         /* This is a hack to prevent profil_count function from being called
378            inside of __gnu_mcount_nc. DO NOT put any other function between
379            __gnu_mcount_nc and profil_count. */
380         if((pc >= (void*)(&profil_count - 0x18)) && (pc <= (void*)(&profil_count)))
381                 return;
382 #endif
383         if(gSTrace != 0)
384                 return;
385
386         TRACE_STATE_SET(TS_PROFIL_COUNT);
387
388         real_pthread_mutex_lock(&profil_log_mutex);
389         do {
390                 if(IS_FULL_SAMPLE_ARRAY)
391                 {       // when this happens, array size should be increased
392                         // profil log: sample info array is full
393                         break;
394                 }
395                 sample_info_array[sample_write_index].time = getTime();
396                 sample_info_array[sample_write_index].pc = pc;
397 #ifdef USING_BACKTRACE
398                 sample_info_array[sample_write_index].bt_size
399                         = backtrace(sample_info_array[sample_write_index].bt_array, MAX_STACK_DEPTH);
400 //#if defined(__i386__)
401 // restore frame clobbered by signal handler
402                 sample_info_array[sample_write_index].bt_array[2] = pc;
403 //#endif
404 #else
405                 sample_info_array[sample_write_index].bt_array[0] = pc;
406                 {
407                         int i;
408                         for(i = 0; i < elapsed_time_index; i++)
409                         {
410                                 sample_info_array[sample_write_index].bt_array[i + 1] 
411                                         = elapsed_time_array[i].self;
412                         }
413                         sample_info_array[sample_write_index].bt_size = elapsed_time_index + 1;
414                 }
415 #endif
416                 sample_write_index = (sample_write_index + 1) % SAMPLE_MAX_IN_INTERVAL;
417         } while(0);
418         real_pthread_mutex_unlock(&profil_log_mutex);
419
420         TRACE_STATE_UNSET(TS_PROFIL_COUNT);
421 }
422
423 #if defined(__i386__)
424 static void profil_counter(int signo, const struct sigcontext scp)
425 {
426         profil_count((void *) GET_PC(scp));
427
428         /* This is a hack to prevent the compiler from implementing the
429            above function call as a sibcall. The sibcall would overwrite
430            the signal context */
431         asm volatile("");
432 }
433 #elif defined(__arm__)
434 static void profil_counter(int signr, siginfo_t *si, struct ucontext *uctx)
435 {
436         profil_count((void *) GET_PC(uctx));
437
438         /* This is a hack to prevent the compiler from implementing the
439            above function call as a sibcall. The sibcall would overwrite
440            the signal context */
441         asm volatile("");
442 }
443 #endif
444
445 #if 0
446 // this function can cause floating point exception.
447 int __profile_frequency(void)
448 {
449         /*
450          * Discover the tick frequency of the machine if something goes wrong,
451          * we return 0, an impossible hertz.
452          */
453         struct itimerval tim;
454
455         tim.it_interval.tv_sec = 0;
456         tim.it_interval.tv_usec = 1;
457         tim.it_value.tv_sec = 0;
458         tim.it_value.tv_usec = 0;
459         setitimer(ITIMER_REAL, &tim, 0);
460         setitimer(ITIMER_REAL, 0, &tim);
461         if (tim.it_interval.tv_usec < 2)
462                 return 0;
463         return (1000000 / tim.it_interval.tv_usec);
464 }
465 #endif
466
467 int __profil(int mode)
468 {
469         struct sigaction act;
470         struct itimerval timer;
471
472         static struct sigaction oact;
473         static struct itimerval otimer;
474
475         if(profil_thread_on != 1)
476         {
477                 profil_option = mode;
478                 return 0;
479         }
480
481         if(mode == 0)
482         {
483                 if(profil_turned_on == 0)
484                 {
485                         return 0;
486                 }
487
488                 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0)
489                         return -1;
490                 profil_turned_on = 0;
491                 return sigaction(SIGPROF, &oact, NULL);
492         }
493
494         if(profil_turned_on == 1)
495         {
496                 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0
497                                 || sigaction(SIGPROF, &oact, NULL) < 0)
498                 {
499                         return -1;
500                 }
501         }
502         profil_turned_on = 1;
503
504         act.sa_handler = (sighandler_t) &profil_counter;
505 #if defined(__i386__)
506         act.sa_flags = SA_RESTART;
507 #elif defined(__arm__)
508         act.sa_flags = SA_RESTART | SA_SIGINFO;
509 #endif
510         sigfillset(&act.sa_mask);
511         if(sigaction(SIGPROF, &act, &oact) < 0)
512                 return -1;
513
514         timer.it_value.tv_sec = 0;
515         timer.it_value.tv_usec = 1000000 / __profile_frequency();
516         timer.it_interval = timer.it_value;
517         return setitimer(ITIMER_PROF, &timer, &otimer);
518 }
519
520 void __monstartup(u_long lowpc, u_long highpc)
521 {
522         low_pc = lowpc;
523         high_pc = highpc;
524
525         pthread_mutex_init(&profil_log_mutex, NULL);
526         probeBlockStart();
527         profil_thread_on = 1;
528         __profil(profil_option);
529         if(pthread_create(&profil_log_thread, NULL, &profil_log_func, NULL) < 0)
530         {
531                 perror("Fail to create profil_log thread");
532         }
533         probeBlockEnd();
534         return;
535 }
536
537 void _mcleanup(void)
538 {
539         __profil(0);
540         profil_thread_on = 0;
541         return;
542 }
543