4 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd. All rights reserved.
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>
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)
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.
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
29 * - Samsung RnD Institute Russia
35 #include <sys/types.h>
43 #include "probeinfo.h"
48 //#define USING_BACKTRACE
50 #define RETURN_ADDRESS(nr) \
51 __builtin_extract_return_addr (__builtin_return_address(nr))
54 #define GET_PC(ctx) ((void *) ctx.eip)
55 #elif defined (__arm__)
56 #define GET_PC(ctx) ((void *) (ctx)->uc_mcontext.arm_pc)
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
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)
67 #define CUSTOM_CB_FUNC_NAME "_chart_timerThread"
69 void *custom_cb_addr = (void*)-1;
71 extern __thread unsigned long gSTrace;
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;
78 typedef struct elapsed_time_t {
79 struct timeval startTime;
80 struct timeval endTime;
81 struct timeval resultTime;
85 __thread elapsed_time elapsed_time_array[USERFUNCTION_MAX_DEPTH];
86 __thread int elapsed_time_index;
88 pthread_t profil_log_thread;
89 pthread_mutex_t profil_log_mutex;
91 typedef struct sample_info_t {
95 void *bt_array[MAX_STACK_DEPTH];
99 int sample_read_index = 0;
100 int sample_write_index = 0;
101 sample_info sample_info_array[SAMPLE_MAX_IN_INTERVAL];
103 int __profile_frequency();
106 static unsigned long long getElapsedTime(struct timeval eTime)
108 return ((unsigned long long) eTime.tv_sec) * 1000000 + eTime.tv_usec;
112 static unsigned long getTime()
115 clock_gettime(CLOCK_REALTIME, &ts);
117 return (unsigned long)(ts.tv_sec * 10000 + (ts.tv_nsec/100000));
120 #ifdef USING_BACKTRACE
121 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
123 char **strings = NULL;
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))
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);
138 if(likely(strings != NULL))
140 for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
142 stringlen = strlen(strings[i - PROFIL_TRIM_STACK_DEPTH]) + 14;
143 if(log->length + stringlen >= bufsize + initsize)
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]);
150 log->data[log->length-2] = '\0';
156 for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
159 if(log->length + stringlen >= bufsize + initsize)
162 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
163 (unsigned int)(sample_info_array[index].bt_array[i]));
165 log->data[log->length-2] = '\0';
168 return (int)(sample_info_array[index].bt_size - PROFIL_TRIM_STACK_DEPTH);
176 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
178 char **strings = NULL;
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);
191 if(likely(strings != NULL))
193 for(i = 0; i < sample_info_array[index].bt_size; i++)
195 stringlen = strlen(strings[i]) + 14;
196 if(log->length + stringlen >= bufsize + initsize)
199 log->length += sprintf(log->data + log->length, "%010u`,%s`,",
200 (unsigned int)(sample_info_array[index].bt_array[i]),
203 log->data[log->length-2] = '\0';
209 for(i = 0; i < sample_info_array[index].bt_size; i++)
212 if(log->length + stringlen >= bufsize + initsize)
215 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
216 (unsigned int)(sample_info_array[index].bt_array[i]));
218 log->data[log->length-2] = '\0';
221 return (int)(sample_info_array[index].bt_size);
225 void *profil_log_func(void *data)
227 TRACE_STATE_SET(TS_PROFIL_THREAD);
229 sigset_t profsigmask;
230 sigemptyset(&profsigmask);
231 sigaddset(&profsigmask, SIGPROF);
232 pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);
234 while(profil_thread_on)
236 while(!IS_EMPTY_SAMPLE_ARRAY)
238 sample_read_index = (sample_read_index + 1) % SAMPLE_MAX_IN_INTERVAL;
240 usleep(PROFIL_LOG_SENDING_INTERVAL_USEC);
242 TRACE_STATE_UNSET(TS_PROFIL_THREAD);
247 void __cyg_profile_func_enter(void *this, void *callsite)
249 probeInfo_t probeInfo;
251 sigset_t profsigmask, oldsigmask;
252 sigemptyset(&profsigmask);
253 sigaddset(&profsigmask, SIGPROF);
254 pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
258 // remove custom chart callback function
259 if(gTraceInfo.custom_chart_callback_count > 0)
261 if(custom_cb_addr == (void*)-1)
263 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
264 if(likely(strings != NULL))
266 if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
268 custom_cb_addr = callsite;
277 if(callsite == custom_cb_addr)
282 setProbePoint(&probeInfo);
284 gettimeofday(&(elapsed_time_array[elapsed_time_index].startTime), NULL);
285 elapsed_time_array[elapsed_time_index].self = this;
286 elapsed_time_index++;
291 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
295 void __cyg_profile_func_exit(void *this, void *callsite)
297 probeInfo_t probeInfo;
299 sigset_t profsigmask, oldsigmask;
300 sigemptyset(&profsigmask);
301 sigaddset(&profsigmask, SIGPROF);
302 pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
306 // remove custom chart callback function
307 if(gTraceInfo.custom_chart_callback_count > 0)
309 if(custom_cb_addr == (void*)-1)
311 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
312 if(likely(strings != NULL))
314 if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
316 custom_cb_addr = callsite;
325 if(callsite == custom_cb_addr)
330 elapsed_time_index--;
332 if(this != elapsed_time_array[elapsed_time_index].self)
333 { // this should never happen
334 ;// function exit: enter/exit function matching failed!!
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));
342 setProbePoint(&probeInfo);
346 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
350 #if defined(__i386__)
356 #elif defined(__arm__)
357 void mcount_internal(u_long frompc, u_long selfpc)
362 void __attribute__((__naked__)) __gnu_mcount_nc(void)
365 "push {r0, r1, r2, r3, lr}\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"
374 static inline void profil_count(void *pc)
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)))
386 TRACE_STATE_SET(TS_PROFIL_COUNT);
388 real_pthread_mutex_lock(&profil_log_mutex);
390 if(IS_FULL_SAMPLE_ARRAY)
391 { // when this happens, array size should be increased
392 // profil log: sample info array is full
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;
405 sample_info_array[sample_write_index].bt_array[0] = pc;
408 for(i = 0; i < elapsed_time_index; i++)
410 sample_info_array[sample_write_index].bt_array[i + 1]
411 = elapsed_time_array[i].self;
413 sample_info_array[sample_write_index].bt_size = elapsed_time_index + 1;
416 sample_write_index = (sample_write_index + 1) % SAMPLE_MAX_IN_INTERVAL;
418 real_pthread_mutex_unlock(&profil_log_mutex);
420 TRACE_STATE_UNSET(TS_PROFIL_COUNT);
423 #if defined(__i386__)
424 static void profil_counter(int signo, const struct sigcontext scp)
426 profil_count((void *) GET_PC(scp));
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 */
433 #elif defined(__arm__)
434 static void profil_counter(int signr, siginfo_t *si, struct ucontext *uctx)
436 profil_count((void *) GET_PC(uctx));
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 */
446 // this function can cause floating point exception.
447 int __profile_frequency(void)
450 * Discover the tick frequency of the machine if something goes wrong,
451 * we return 0, an impossible hertz.
453 struct itimerval tim;
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)
463 return (1000000 / tim.it_interval.tv_usec);
467 int __profil(int mode)
469 struct sigaction act;
470 struct itimerval timer;
472 static struct sigaction oact;
473 static struct itimerval otimer;
475 if(profil_thread_on != 1)
477 profil_option = mode;
483 if(profil_turned_on == 0)
488 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0)
490 profil_turned_on = 0;
491 return sigaction(SIGPROF, &oact, NULL);
494 if(profil_turned_on == 1)
496 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0
497 || sigaction(SIGPROF, &oact, NULL) < 0)
502 profil_turned_on = 1;
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;
510 sigfillset(&act.sa_mask);
511 if(sigaction(SIGPROF, &act, &oact) < 0)
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);
520 void __monstartup(u_long lowpc, u_long highpc)
525 pthread_mutex_init(&profil_log_mutex, NULL);
527 profil_thread_on = 1;
528 __profil(profil_option);
529 if(pthread_create(&profil_log_thread, NULL, &profil_log_func, NULL) < 0)
531 perror("Fail to create profil_log thread");
540 profil_thread_on = 0;