[FIX] cherry-pick d89d303d37d714ced2d1a9945ae2ea054c5824c0 (#8:3,4,5) + changes
[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 int profil_option = 0;
72 volatile int profil_turned_on = 0;
73 volatile int profil_thread_on = 0;
74 u_long low_pc, high_pc;
75
76 typedef struct elapsed_time_t {
77         struct timeval startTime;
78         struct timeval endTime;
79         struct timeval resultTime;
80         void *self;
81 } elapsed_time;
82
83 __thread elapsed_time elapsed_time_array[USERFUNCTION_MAX_DEPTH];
84 __thread int elapsed_time_index;
85
86 pthread_t profil_log_thread;
87 pthread_mutex_t profil_log_mutex;
88
89 typedef struct sample_info_t {
90         unsigned long time;
91         void *pc;
92         int bt_size;
93         void *bt_array[MAX_STACK_DEPTH];
94 } sample_info;
95
96 int sample_seq = 0;
97 int sample_read_index = 0;
98 int sample_write_index = 0;
99 sample_info sample_info_array[SAMPLE_MAX_IN_INTERVAL];
100
101 int __profile_frequency();
102
103 /*
104 static unsigned long long getElapsedTime(struct timeval eTime)
105 {
106         return ((unsigned long long) eTime.tv_sec) * 1000000 + eTime.tv_usec;
107 }
108 */
109
110 static unsigned long getTime()
111 {
112         struct timespec ts;
113         clock_gettime(CLOCK_REALTIME, &ts);
114
115         return (unsigned long)(ts.tv_sec * 10000 + (ts.tv_nsec/100000));
116 }
117
118 #ifdef USING_BACKTRACE
119 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
120 {
121         char **strings = NULL;
122         size_t i;
123         int initsize;
124         int stringlen;
125
126         if(log == NULL)
127                 return 0;
128
129         initsize = log->length;
130         log->data[log->length] = '\0';  // is this necessary ?
131         if(likely(sample_info_array[index].bt_size > PROFIL_TRIM_STACK_DEPTH))
132         {
133                 strings = BACKTRACE_SYMBOLS(sample_info_array[index].bt_array + PROFIL_TRIM_STACK_DEPTH,
134                                 sample_info_array[index].bt_size - PROFIL_TRIM_STACK_DEPTH);
135
136                 if(likely(strings != NULL))
137                 {
138                         for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
139                         {
140                                 stringlen = strlen(strings[i - PROFIL_TRIM_STACK_DEPTH]) + 14;
141                                 if(log->length + stringlen >= bufsize + initsize)       
142                                         break;
143
144                                 log->length += sprintf(log->data + log->length, "%010u`,%s`,",
145                                                 (unsigned int)(sample_info_array[index].bt_array[i]),
146                                                 strings[i - PROFIL_TRIM_STACK_DEPTH]);
147                         }
148                         log->data[log->length-2] = '\0';
149                         log->length -= 2;
150                         free(strings);
151                 }
152                 else
153                 {
154                         for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
155                         {
156                                 stringlen = 23;
157                                 if(log->length + stringlen >= bufsize + initsize)
158                                         break;
159
160                                 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
161                                                 (unsigned int)(sample_info_array[index].bt_array[i]));
162                         }
163                         log->data[log->length-2] = '\0';
164                         log->length -= 2;
165                 }
166                 return (int)(sample_info_array[index].bt_size - PROFIL_TRIM_STACK_DEPTH);
167         }
168         else
169         {
170                 return 0;
171         }
172 }
173 #else
174 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
175 {
176         char **strings = NULL;
177         size_t i;
178         int initsize;
179         int stringlen;
180
181         if(log == NULL)
182                 return 0;
183
184         initsize = log->length;
185         log->data[log->length] = '\0';  // is this necessary ?
186         strings = BACKTRACE_SYMBOLS(sample_info_array[index].bt_array,
187                         sample_info_array[index].bt_size);
188
189         if(likely(strings != NULL))
190         {
191                 for(i = 0; i < sample_info_array[index].bt_size; i++)
192                 {
193                         stringlen = strlen(strings[i]) + 14;
194                         if(log->length + stringlen >= bufsize + initsize)       
195                                 break;
196
197                         log->length += sprintf(log->data + log->length, "%010u`,%s`,",
198                                         (unsigned int)(sample_info_array[index].bt_array[i]),
199                                         strings[i]);
200                 }
201                 log->data[log->length-2] = '\0';
202                 log->length -= 2;
203                 free(strings);
204         }
205         else
206         {
207                 for(i = 0; i < sample_info_array[index].bt_size; i++)
208                 {
209                         stringlen = 23;
210                         if(log->length + stringlen >= bufsize + initsize)
211                                 break;
212
213                         log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
214                                         (unsigned int)(sample_info_array[index].bt_array[i]));
215                 }
216                 log->data[log->length-2] = '\0';
217                 log->length -= 2;
218         }
219         return (int)(sample_info_array[index].bt_size);
220 }
221 #endif
222
223 void *profil_log_func(void *data)
224 {
225         TRACE_STATE_SET(TS_PROFIL_THREAD);
226         
227         sigset_t profsigmask;
228         sigemptyset(&profsigmask);
229         sigaddset(&profsigmask, SIGPROF);
230         pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);
231
232         while(profil_thread_on)
233         {
234                 while(!IS_EMPTY_SAMPLE_ARRAY)
235                 {
236                         sample_read_index = (sample_read_index + 1) % SAMPLE_MAX_IN_INTERVAL;
237                 }
238                 usleep(PROFIL_LOG_SENDING_INTERVAL_USEC);
239         }
240         TRACE_STATE_UNSET(TS_PROFIL_THREAD);
241
242         return NULL;
243 }
244
245 void __cyg_profile_func_enter(void *this, void *callsite)
246 {
247         probeInfo_t probeInfo;
248
249         sigset_t profsigmask, oldsigmask;
250         sigemptyset(&profsigmask);
251         sigaddset(&profsigmask, SIGPROF);
252         pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
253
254         probeBlockStart();
255         do {
256                 // remove custom chart callback function
257                 if(gTraceInfo.custom_chart_callback_count > 0)
258                 {
259                         if(custom_cb_addr == (void*)-1)
260                         {
261                                 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
262                                 if(likely(strings != NULL))
263                                 {
264                                         if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
265                                         {
266                                                 custom_cb_addr = callsite;
267                                                 free(strings);
268                                                 break;
269                                         }
270                                         free(strings);
271                                 }
272                         }
273                         else
274                         {
275                                 if(callsite == custom_cb_addr)
276                                         break;
277                         }
278                 }
279
280                 setProbePoint(&probeInfo);
281
282                 gettimeofday(&(elapsed_time_array[elapsed_time_index].startTime), NULL);
283                 elapsed_time_array[elapsed_time_index].self = this;
284                 elapsed_time_index++;
285
286         } while(0); 
287         probeBlockEnd();
288
289         pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
290         return;
291 }
292
293 void __cyg_profile_func_exit(void *this, void *callsite)
294 {
295         probeInfo_t probeInfo;
296
297         sigset_t profsigmask, oldsigmask;
298         sigemptyset(&profsigmask);
299         sigaddset(&profsigmask, SIGPROF);
300         pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
301
302         probeBlockStart();
303         do {
304                 // remove custom chart callback function
305                 if(gTraceInfo.custom_chart_callback_count > 0)
306                 {
307                         if(custom_cb_addr == (void*)-1)
308                         {
309                                 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
310                                 if(likely(strings != NULL))
311                                 {
312                                         if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
313                                         {
314                                                 custom_cb_addr = callsite;
315                                                 free(strings);
316                                                 break;
317                                         }
318                                         free(strings);
319                                 }
320                         }
321                         else
322                         {
323                                 if(callsite == custom_cb_addr)
324                                         break;
325                         }
326                 }
327
328                 elapsed_time_index--;
329
330                 if(this != elapsed_time_array[elapsed_time_index].self)
331                 { // this should never happen
332                         ;// function exit: enter/exit function matching failed!!
333                 }
334
335                 gettimeofday(&(elapsed_time_array[elapsed_time_index].endTime), NULL);
336                 timersub(&(elapsed_time_array[elapsed_time_index].endTime),
337                                 &(elapsed_time_array[elapsed_time_index].startTime),
338                                 &(elapsed_time_array[elapsed_time_index].resultTime));
339
340                 setProbePoint(&probeInfo);
341         } while(0);
342         probeBlockEnd();
343
344         pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
345         return;
346 }
347
348 #if defined(__i386__)
349 void mcount(void)
350 {
351         return;
352 }
353
354 #elif defined(__arm__)
355 void mcount_internal(u_long frompc, u_long selfpc)
356 {
357         return;
358 }
359
360 void __attribute__((__naked__)) __gnu_mcount_nc(void)
361 {
362         asm(
363                         "push {r0, r1, r2, r3, lr}\n\t"
364                         "bic r1, lr, #1\n\t"
365                         "ldr r0, [sp, #20]\n\t"
366                         "bl mcount_internal\n\t"
367                         "pop {r0, r1, r2, r3, ip, lr}\n\t"
368                         "bx ip");
369 }
370 #endif
371
372 static inline void profil_count(void *pc)
373 {
374 #if defined(__arm__)
375         /* This is a hack to prevent profil_count function from being called
376            inside of __gnu_mcount_nc. DO NOT put any other function between
377            __gnu_mcount_nc and profil_count. */
378         if((pc >= (void*)(&profil_count - 0x18)) && (pc <= (void*)(&profil_count)))
379                 return;
380 #endif
381         if(gSTrace != 0)
382                 return;
383
384         TRACE_STATE_SET(TS_PROFIL_COUNT);
385
386         real_pthread_mutex_lock(&profil_log_mutex);
387         do {
388                 if(IS_FULL_SAMPLE_ARRAY)
389                 {       // when this happens, array size should be increased
390                         // profil log: sample info array is full
391                         break;
392                 }
393                 sample_info_array[sample_write_index].time = getTime();
394                 sample_info_array[sample_write_index].pc = pc;
395 #ifdef USING_BACKTRACE
396                 sample_info_array[sample_write_index].bt_size
397                         = backtrace(sample_info_array[sample_write_index].bt_array, MAX_STACK_DEPTH);
398 //#if defined(__i386__)
399 // restore frame clobbered by signal handler
400                 sample_info_array[sample_write_index].bt_array[2] = pc;
401 //#endif
402 #else
403                 sample_info_array[sample_write_index].bt_array[0] = pc;
404                 {
405                         int i;
406                         for(i = 0; i < elapsed_time_index; i++)
407                         {
408                                 sample_info_array[sample_write_index].bt_array[i + 1] 
409                                         = elapsed_time_array[i].self;
410                         }
411                         sample_info_array[sample_write_index].bt_size = elapsed_time_index + 1;
412                 }
413 #endif
414                 sample_write_index = (sample_write_index + 1) % SAMPLE_MAX_IN_INTERVAL;
415         } while(0);
416         real_pthread_mutex_unlock(&profil_log_mutex);
417
418         TRACE_STATE_UNSET(TS_PROFIL_COUNT);
419 }
420
421 #if defined(__i386__)
422 static void profil_counter(int signo, const struct sigcontext scp)
423 {
424         profil_count((void *) GET_PC(scp));
425
426         /* This is a hack to prevent the compiler from implementing the
427            above function call as a sibcall. The sibcall would overwrite
428            the signal context */
429         asm volatile("");
430 }
431 #elif defined(__arm__)
432 static void profil_counter(int signr, siginfo_t *si, struct ucontext *uctx)
433 {
434         profil_count((void *) GET_PC(uctx));
435
436         /* This is a hack to prevent the compiler from implementing the
437            above function call as a sibcall. The sibcall would overwrite
438            the signal context */
439         asm volatile("");
440 }
441 #endif
442
443 #if 0
444 // this function can cause floating point exception.
445 int __profile_frequency(void)
446 {
447         /*
448          * Discover the tick frequency of the machine if something goes wrong,
449          * we return 0, an impossible hertz.
450          */
451         struct itimerval tim;
452
453         tim.it_interval.tv_sec = 0;
454         tim.it_interval.tv_usec = 1;
455         tim.it_value.tv_sec = 0;
456         tim.it_value.tv_usec = 0;
457         setitimer(ITIMER_REAL, &tim, 0);
458         setitimer(ITIMER_REAL, 0, &tim);
459         if (tim.it_interval.tv_usec < 2)
460                 return 0;
461         return (1000000 / tim.it_interval.tv_usec);
462 }
463 #endif
464
465 int __profil(int mode)
466 {
467         struct sigaction act;
468         struct itimerval timer;
469
470         static struct sigaction oact;
471         static struct itimerval otimer;
472
473         if(profil_thread_on != 1)
474         {
475                 profil_option = mode;
476                 return 0;
477         }
478
479         if(mode == 0)
480         {
481                 if(profil_turned_on == 0)
482                 {
483                         return 0;
484                 }
485
486                 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0)
487                         return -1;
488                 profil_turned_on = 0;
489                 return sigaction(SIGPROF, &oact, NULL);
490         }
491
492         if(profil_turned_on == 1)
493         {
494                 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0
495                                 || sigaction(SIGPROF, &oact, NULL) < 0)
496                 {
497                         return -1;
498                 }
499         }
500         profil_turned_on = 1;
501
502         act.sa_handler = (sighandler_t) &profil_counter;
503 #if defined(__i386__)
504         act.sa_flags = SA_RESTART;
505 #elif defined(__arm__)
506         act.sa_flags = SA_RESTART | SA_SIGINFO;
507 #endif
508         sigfillset(&act.sa_mask);
509         if(sigaction(SIGPROF, &act, &oact) < 0)
510                 return -1;
511
512         timer.it_value.tv_sec = 0;
513         timer.it_value.tv_usec = 1000000 / __profile_frequency();
514         timer.it_interval = timer.it_value;
515         return setitimer(ITIMER_PROF, &timer, &otimer);
516 }
517
518 void __monstartup(u_long lowpc, u_long highpc)
519 {
520         low_pc = lowpc;
521         high_pc = highpc;
522
523         pthread_mutex_init(&profil_log_mutex, NULL);
524         probeBlockStart();
525         profil_thread_on = 1;
526         __profil(profil_option);
527         if(pthread_create(&profil_log_thread, NULL, &profil_log_func, NULL) < 0)
528         {
529                 perror("Fail to create profil_log thread");
530         }
531         probeBlockEnd();
532         return;
533 }
534
535 void _mcleanup(void)
536 {
537         __profil(0);
538         profil_thread_on = 0;
539         return;
540 }
541