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 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;
76 typedef struct elapsed_time_t {
77 struct timeval startTime;
78 struct timeval endTime;
79 struct timeval resultTime;
83 __thread elapsed_time elapsed_time_array[USERFUNCTION_MAX_DEPTH];
84 __thread int elapsed_time_index;
86 pthread_t profil_log_thread;
87 pthread_mutex_t profil_log_mutex;
89 typedef struct sample_info_t {
93 void *bt_array[MAX_STACK_DEPTH];
97 int sample_read_index = 0;
98 int sample_write_index = 0;
99 sample_info sample_info_array[SAMPLE_MAX_IN_INTERVAL];
101 int __profile_frequency();
104 static unsigned long long getElapsedTime(struct timeval eTime)
106 return ((unsigned long long) eTime.tv_sec) * 1000000 + eTime.tv_usec;
110 static unsigned long getTime()
113 clock_gettime(CLOCK_REALTIME, &ts);
115 return (unsigned long)(ts.tv_sec * 10000 + (ts.tv_nsec/100000));
118 #ifdef USING_BACKTRACE
119 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
121 char **strings = NULL;
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))
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);
136 if(likely(strings != NULL))
138 for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
140 stringlen = strlen(strings[i - PROFIL_TRIM_STACK_DEPTH]) + 14;
141 if(log->length + stringlen >= bufsize + initsize)
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]);
148 log->data[log->length-2] = '\0';
154 for(i = PROFIL_TRIM_STACK_DEPTH; i < sample_info_array[index].bt_size; i++)
157 if(log->length + stringlen >= bufsize + initsize)
160 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
161 (unsigned int)(sample_info_array[index].bt_array[i]));
163 log->data[log->length-2] = '\0';
166 return (int)(sample_info_array[index].bt_size - PROFIL_TRIM_STACK_DEPTH);
174 int profil_backtrace_symbols(log_t *log, int bufsize, int index)
176 char **strings = NULL;
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);
189 if(likely(strings != NULL))
191 for(i = 0; i < sample_info_array[index].bt_size; i++)
193 stringlen = strlen(strings[i]) + 14;
194 if(log->length + stringlen >= bufsize + initsize)
197 log->length += sprintf(log->data + log->length, "%010u`,%s`,",
198 (unsigned int)(sample_info_array[index].bt_array[i]),
201 log->data[log->length-2] = '\0';
207 for(i = 0; i < sample_info_array[index].bt_size; i++)
210 if(log->length + stringlen >= bufsize + initsize)
213 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,",
214 (unsigned int)(sample_info_array[index].bt_array[i]));
216 log->data[log->length-2] = '\0';
219 return (int)(sample_info_array[index].bt_size);
223 void *profil_log_func(void *data)
225 TRACE_STATE_SET(TS_PROFIL_THREAD);
227 sigset_t profsigmask;
228 sigemptyset(&profsigmask);
229 sigaddset(&profsigmask, SIGPROF);
230 pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);
232 while(profil_thread_on)
234 while(!IS_EMPTY_SAMPLE_ARRAY)
236 sample_read_index = (sample_read_index + 1) % SAMPLE_MAX_IN_INTERVAL;
238 usleep(PROFIL_LOG_SENDING_INTERVAL_USEC);
240 TRACE_STATE_UNSET(TS_PROFIL_THREAD);
245 void __cyg_profile_func_enter(void *this, void *callsite)
247 probeInfo_t probeInfo;
249 sigset_t profsigmask, oldsigmask;
250 sigemptyset(&profsigmask);
251 sigaddset(&profsigmask, SIGPROF);
252 pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
256 // remove custom chart callback function
257 if(gTraceInfo.custom_chart_callback_count > 0)
259 if(custom_cb_addr == (void*)-1)
261 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
262 if(likely(strings != NULL))
264 if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
266 custom_cb_addr = callsite;
275 if(callsite == custom_cb_addr)
280 setProbePoint(&probeInfo);
282 gettimeofday(&(elapsed_time_array[elapsed_time_index].startTime), NULL);
283 elapsed_time_array[elapsed_time_index].self = this;
284 elapsed_time_index++;
289 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
293 void __cyg_profile_func_exit(void *this, void *callsite)
295 probeInfo_t probeInfo;
297 sigset_t profsigmask, oldsigmask;
298 sigemptyset(&profsigmask);
299 sigaddset(&profsigmask, SIGPROF);
300 pthread_sigmask(SIG_BLOCK, &profsigmask, &oldsigmask);
304 // remove custom chart callback function
305 if(gTraceInfo.custom_chart_callback_count > 0)
307 if(custom_cb_addr == (void*)-1)
309 char **strings = BACKTRACE_SYMBOLS(&callsite, 1);
310 if(likely(strings != NULL))
312 if(strstr(strings[0], CUSTOM_CB_FUNC_NAME) != NULL)
314 custom_cb_addr = callsite;
323 if(callsite == custom_cb_addr)
328 elapsed_time_index--;
330 if(this != elapsed_time_array[elapsed_time_index].self)
331 { // this should never happen
332 ;// function exit: enter/exit function matching failed!!
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));
340 setProbePoint(&probeInfo);
344 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
348 #if defined(__i386__)
354 #elif defined(__arm__)
355 void mcount_internal(u_long frompc, u_long selfpc)
360 void __attribute__((__naked__)) __gnu_mcount_nc(void)
363 "push {r0, r1, r2, r3, lr}\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"
372 static inline void profil_count(void *pc)
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)))
384 TRACE_STATE_SET(TS_PROFIL_COUNT);
386 real_pthread_mutex_lock(&profil_log_mutex);
388 if(IS_FULL_SAMPLE_ARRAY)
389 { // when this happens, array size should be increased
390 // profil log: sample info array is full
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;
403 sample_info_array[sample_write_index].bt_array[0] = pc;
406 for(i = 0; i < elapsed_time_index; i++)
408 sample_info_array[sample_write_index].bt_array[i + 1]
409 = elapsed_time_array[i].self;
411 sample_info_array[sample_write_index].bt_size = elapsed_time_index + 1;
414 sample_write_index = (sample_write_index + 1) % SAMPLE_MAX_IN_INTERVAL;
416 real_pthread_mutex_unlock(&profil_log_mutex);
418 TRACE_STATE_UNSET(TS_PROFIL_COUNT);
421 #if defined(__i386__)
422 static void profil_counter(int signo, const struct sigcontext scp)
424 profil_count((void *) GET_PC(scp));
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 */
431 #elif defined(__arm__)
432 static void profil_counter(int signr, siginfo_t *si, struct ucontext *uctx)
434 profil_count((void *) GET_PC(uctx));
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 */
444 // this function can cause floating point exception.
445 int __profile_frequency(void)
448 * Discover the tick frequency of the machine if something goes wrong,
449 * we return 0, an impossible hertz.
451 struct itimerval tim;
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)
461 return (1000000 / tim.it_interval.tv_usec);
465 int __profil(int mode)
467 struct sigaction act;
468 struct itimerval timer;
470 static struct sigaction oact;
471 static struct itimerval otimer;
473 if(profil_thread_on != 1)
475 profil_option = mode;
481 if(profil_turned_on == 0)
486 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0)
488 profil_turned_on = 0;
489 return sigaction(SIGPROF, &oact, NULL);
492 if(profil_turned_on == 1)
494 if(setitimer(ITIMER_PROF, &otimer, NULL) < 0
495 || sigaction(SIGPROF, &oact, NULL) < 0)
500 profil_turned_on = 1;
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;
508 sigfillset(&act.sa_mask);
509 if(sigaction(SIGPROF, &act, &oact) < 0)
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);
518 void __monstartup(u_long lowpc, u_long highpc)
523 pthread_mutex_init(&profil_log_mutex, NULL);
525 profil_thread_on = 1;
526 __profil(profil_option);
527 if(pthread_create(&profil_log_thread, NULL, &profil_log_func, NULL) < 0)
529 perror("Fail to create profil_log thread");
538 profil_thread_on = 0;