4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
8 * Jaewon Lim <jaewon81.lim@samsung.com>
9 * Woojin Jung <woojin2.jung@samsung.com>
10 * Juyoung Kim <j0.kim@samsung.com>
12 * This library is free software; you can redistribute it and/or modify it under
13 * the terms of the GNU Lesser General Public License as published by the
14 * Free Software Foundation; either version 2.1 of the License, or (at your option)
17 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
18 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
20 * License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this library; if not, write to the Free Software Foundation, Inc., 51
24 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <stdio.h> // for sprintf
32 #include <stdlib.h> // for getenv
33 #include <string.h> // for strstr
34 #include <stdbool.h> // for bool
35 #include <stdint.h> // fot uint32_t,uint64_t
36 #include <stdarg.h> // for va_list, va_arg(__appendTypeLog)
37 #include <execinfo.h> // for backtrace, backtrace_symbols
38 #include <unistd.h> // for write, alarm function, syscall
39 #include <pthread.h> // for pthread_mutex_lock
42 #include <sys/syscall.h> // for syscall
43 #include <sys/time.h> // for gettimeofday
44 #include <sys/socket.h> // for socket, connect
45 #include <sys/un.h> // for sockaddr_un
46 #include <sys/timerfd.h> // for timerfd
49 #include "probeinfo.h"
52 #include "dacollection.h"
54 #define APP_INSTALL_PATH "/opt/apps"
55 #define OSP_APP_POSTFIX ".exe"
56 #define UDS_NAME "/tmp/da.socket"
57 #define TIMERFD_INTERVAL 100000000 // 0.1 sec
59 __thread int gProbeBlockCount = 0;
60 __thread int gProbeDepth = 0;
61 __thread pid_t gTid = -1;
64 long g_total_alloc_size = 0;
65 pthread_t g_recvthread_id;
67 int getExecutableMappingAddress();
69 /******************************************************************************
71 (this means that these functions do not need to set enter/exit flag)
72 ******************************************************************************/
74 // runtime configure the probe option
75 static void _configure(char* configstr)
78 gTraceInfo.optionflag = atoi(configstr);
80 sprintf(buf, "configure in probe : %s, %lx\n", configstr, gTraceInfo.optionflag);
83 if(isOptionEnabled(OPT_FUNC))
93 // create socket to daemon and connect
94 static int createSocket(void)
97 int clientLen, ret = 0;
98 struct sockaddr_un clientAddr;
102 if((gTraceInfo.socket.daemonSock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) != -1)
104 bzero(&clientAddr, sizeof(clientAddr));
105 clientAddr.sun_family = AF_UNIX;
106 sprintf(clientAddr.sun_path, "%s", UDS_NAME);
108 clientLen = sizeof(clientAddr);
109 if(connect(gTraceInfo.socket.daemonSock, (struct sockaddr *)&clientAddr, clientLen) >= 0)
111 // recv initial configuration value
112 recvlen = recv(gTraceInfo.socket.daemonSock, &log,
113 sizeof(log.type) + sizeof(log.length), MSG_WAITALL);
115 if(recvlen > 0) // recv succeed
119 if(log.length >= DA_LOG_MAX)
120 log.length = DA_LOG_MAX - 1;
121 recvlen = recv(gTraceInfo.socket.daemonSock, log.data,
122 log.length, MSG_WAITALL);
129 log.data[log.length] = '\0';
131 if(log.type == MSG_CONFIG)
133 _configure(log.data);
143 sprintf(buf, "recv failed in socket creation with error(%d)\n", recvlen);
146 else // closed by other peer
150 sprintf(buf, "%d|%u", getpid(), gTraceInfo.app.startTime);
151 printLogStr(buf, MSG_PID);
152 PRINTMSG("createSocket connect() success\n");
156 close(gTraceInfo.socket.daemonSock);
157 gTraceInfo.socket.daemonSock = -1;
170 // parse backtrace string and find out the caller of probed api function
171 // return 0 if caller is user binary, otherwise return 1
172 static int determineCaller(char* tracestring)
176 // determine whether saveptr (caller binary name) is user binary or not
177 substr = strstr(tracestring, APP_INSTALL_PATH);
179 if(substr == NULL) // not user binary
186 substr = strstr(tracestring, OSP_APP_POSTFIX);
194 // return current thread id
195 static pid_t _gettid()
198 gTid = syscall(__NR_gettid); // syscall is very expensive
202 static void* recvThread(void* data)
204 fd_set readfds, workfds;
209 sigset_t profsigmask;
211 if(gTraceInfo.socket.daemonSock == -1)
216 sigemptyset(&profsigmask);
217 sigaddset(&profsigmask, SIGPROF);
218 pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);
224 FD_SET(g_timerfd, &readfds);
226 if(maxfd < gTraceInfo.socket.daemonSock)
227 maxfd = gTraceInfo.socket.daemonSock;
228 FD_SET(gTraceInfo.socket.daemonSock, &readfds);
233 rc = select(maxfd + 1, &workfds, NULL, NULL, NULL);
239 if(g_timerfd > 0 && FD_ISSET(g_timerfd, &workfds))
241 recvlen = read(g_timerfd, &xtime, sizeof(xtime));
244 log.length = sprintf(log.data, "%ld", g_total_alloc_size);
245 printLog(&log, MSG_ALLOC);
253 else if(FD_ISSET(gTraceInfo.socket.daemonSock, &workfds))
255 recvlen = recv(gTraceInfo.socket.daemonSock, &log,
256 sizeof(log.type) + sizeof(log.length), MSG_WAITALL);
258 if(recvlen > 0) // recv succeed
262 if(log.length >= DA_LOG_MAX)
263 log.length = DA_LOG_MAX - 1;
264 recvlen = recv(gTraceInfo.socket.daemonSock, log.data,
265 log.length, MSG_WAITALL);
272 log.data[log.length] = '\0';
274 if(log.type == MSG_CONFIG)
276 _configure(log.data);
278 else if(log.type == MSG_STOP)
285 sprintf(buf, "recv unknown message(%d)\n", log.type);
290 else if(recvlen == 0) // closed by other peer
292 close(gTraceInfo.socket.daemonSock);
293 gTraceInfo.socket.daemonSock = -1;
299 sprintf(buf, "recv failed in recv thread with error(%d)\n", recvlen);
306 PRINTMSG("unknown fd in recvThread\n");
315 /*****************************************************************************
316 * initialize / finalize function
317 *****************************************************************************/
319 void __attribute__((constructor)) _init_probe()
321 struct timeval ttime;
322 struct itimerspec ctime;
326 initialize_hash_table();
328 initialize_screencapture();
332 getExecutableMappingAddress();
334 // get app start time
335 gettimeofday(&ttime, NULL);
336 gTraceInfo.app.startTime = ((ttime.tv_sec * 10000 + (ttime.tv_usec/100)));
338 // create socket for communication with da_daemon
339 if(createSocket() == 0)
342 g_timerfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
345 ctime.it_value.tv_sec = 0;
346 ctime.it_value.tv_nsec = TIMERFD_INTERVAL;
347 ctime.it_interval.tv_sec = 0;
348 ctime.it_interval.tv_nsec = TIMERFD_INTERVAL;
349 if(0 > timerfd_settime(g_timerfd, 0, &ctime, NULL))
351 PRINTMSG("failed to set timerfd\n");
358 PRINTMSG("failed to create timerdf\n");
361 // create recv Thread
362 if(pthread_create(&g_recvthread_id, NULL, recvThread, NULL) < 0) // thread creation failed
364 PRINTMSG("failed to crate recv thread\n");
366 update_heap_memory_size(true, 0);
373 PRINTMSG("dynamic analyzer probe helper so loading...\n");
375 gTraceInfo.init_complete = 1;
379 void __attribute__((destructor)) _fini_probe()
384 gTraceInfo.init_complete = -1;
385 PRINTMSG("dynamic analyzer probe helper so unloading...\n");
394 if(gTraceInfo.socket.daemonSock != -1)
396 printLogStr(NULL, MSG_TERMINATE);
397 close(gTraceInfo.socket.daemonSock);
398 gTraceInfo.socket.daemonSock = -1;
403 finalize_screencapture();
405 finalize_hash_table();
407 for(i = 0; i < NUM_ORIGINAL_LIBRARY; i++)
409 if(lib_handle[i] != NULL)
411 dlclose(lib_handle[i]);
419 /**************************************************************************
421 **************************************************************************/
423 /************************************************************************
424 * manipulate and print log functions
425 ************************************************************************/
427 bool printLog(log_t* log, int msgType)
430 if(unlikely(gTraceInfo.socket.daemonSock == -1))
433 if(unlikely(log == NULL))
438 pthread_mutex_lock(&(gTraceInfo.socket.sockMutex));
439 res = send(gTraceInfo.socket.daemonSock, log, sizeof(log->type) + sizeof(log->length) + log->length, 0);
440 pthread_mutex_unlock(&(gTraceInfo.socket.sockMutex));
446 bool printLogStr(const char* str, int msgType)
451 if(unlikely(gTraceInfo.socket.daemonSock == -1))
459 sprintf(log.data, "%s", str);
460 log.length = strlen(str);
467 pthread_mutex_lock(&(gTraceInfo.socket.sockMutex));
468 res = send(gTraceInfo.socket.daemonSock, &log, sizeof(log.type) + sizeof(log.length) + log.length, MSG_DONTWAIT);
469 pthread_mutex_unlock(&(gTraceInfo.socket.sockMutex));
476 // return 0 for successful case
477 // return non-zero for error
478 // if token is NULL then use DEFAULT TOKEN "`,"
479 // if token is not NULL then insert DEFAULT TOKEN before append input
480 int __appendTypeLog(log_t* log, int nInput, char* token, ...)
482 static char* default_token = DEFAULT_TOKEN;
485 char* seperator = default_token;
487 if(nInput <= 0 || log == NULL)
492 va_start(p_arg, token);
497 for(i = 0; i < nInput; i++)
499 type = va_arg(p_arg, int);
501 if(likely(log->length > 0)) // append token or default token
504 log->length += sprintf(log->data + log->length, "%s", default_token);
506 log->length += sprintf(log->data + log->length, "%s", seperator);
512 log->length += sprintf(log->data + log->length, "%d", va_arg(p_arg, int));
515 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, unsigned int));
518 log->length += sprintf(log->data + log->length, "%ld", va_arg(p_arg, long));
521 log->length += sprintf(log->data + log->length, "%lu", va_arg(p_arg, unsigned long));
524 log->length += sprintf(log->data + log->length, "%s", va_arg(p_arg, char*));
526 case VT_CHAR: // 'char' is promoted to 'int' when passed through '...'
527 log->length += sprintf(log->data + log->length, "%c", va_arg(p_arg, int));
530 log->length += sprintf(log->data + log->length, "%p", va_arg(p_arg, void*));
533 va_arg(p_arg, unsigned int);
536 log->length += sprintf(log->data + log->length, "%ld", va_arg(p_arg, off_t));
539 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, size_t));
542 log->length += sprintf(log->data + log->length, "%d", va_arg(p_arg, ssize_t));
545 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, socklen_t));
547 case VT_UINT16_T: // 'uint16_t' is promoted to 'int' when passed through '...'
548 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, int));
551 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, uint32_t));
554 log->length += sprintf(log->data + log->length, "%llu", va_arg(p_arg, uint64_t));
557 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, mode_t));
560 log->length += sprintf(log->data + log->length, "%lu", va_arg(p_arg, dev_t));
563 log->length += sprintf(log->data + log->length, "%lu", va_arg(p_arg, nfds_t));
578 // get backtrace string
579 // return stack depth if succeed, otherwise return 0
580 // parameter 'log' cannot be null
581 int getBacktraceString(log_t* log, int bufsize)
583 void* array[MAX_STACK_DEPTH];
584 char** strings = NULL;
594 initsize = log->length;
595 log->data[log->length] = '\0'; // is this necessary ?
596 size = backtrace(array, MAX_STACK_DEPTH);
597 if(likely(size > TRIM_STACK_DEPTH))
599 strings = BACKTRACE_SYMBOLS(array + TRIM_STACK_DEPTH, size - TRIM_STACK_DEPTH);
601 if(likely(strings != NULL))
603 for(i = TRIM_STACK_DEPTH; i < size; i++)
605 stringlen = strlen(strings[i - TRIM_STACK_DEPTH]) + 14;
606 if(log->length + stringlen >= bufsize + initsize)
609 log->length += sprintf(log->data + log->length, "%010u`,%s`,", (unsigned int)(array[i]), strings[i - TRIM_STACK_DEPTH]);
611 log->data[log->length-2] = '\0';
615 else // failed to get backtrace symbols
617 // just print trace address
618 for(i = TRIM_STACK_DEPTH; i < size; i++)
621 if(log->length + stringlen >= bufsize + initsize)
624 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,", (unsigned int)(array[i]));
626 log->data[log->length-2] = '\0';
631 return (int)(size - TRIM_STACK_DEPTH);
640 /*************************************************************************
641 * probe block control functions
642 *************************************************************************/
643 int preBlockBegin(void* caller, bool bFiltering, enum DaOptions option)
649 if(gProbeBlockCount != 0 || gProbeDepth != 0)
652 if(gTraceInfo.init_complete <= 0)
655 if((gTraceInfo.optionflag & option) == 0)
660 if(gTraceInfo.exec_map.map_start != NULL)
662 // address comparison
663 if(caller >= gTraceInfo.exec_map.map_start &&
664 caller <= gTraceInfo.exec_map.map_end)
675 // backtrace for filtering
677 strings = BACKTRACE_SYMBOLS(tarray, 1);
680 if((determineCaller(strings[0]) == 0))
693 return 2; // user call
700 return 0; // not probing
705 return 1; // internal call
710 int postBlockBegin(int preresult)
731 /*************************************************************************
732 * helper info getter functions
733 *************************************************************************/
734 // return current time in 1/10000 sec unit
735 unsigned long getCurrentTime()
737 struct timeval cTime;
739 gettimeofday(&cTime, NULL);
741 return (unsigned long)((cTime.tv_sec * 10000 + (cTime.tv_usec/100)));
744 unsigned int getCurrentEventIndex()
746 return gTraceInfo.index.eventIndex;
749 /************************************************************************
751 ************************************************************************/
752 bool setProbePoint(probeInfo_t* iProbe)
754 if(unlikely(iProbe == NULL))
761 // atomic operaion(gcc builtin) is more expensive then pthread_mutex
762 pthread_mutex_lock(&(gTraceInfo.index.eventMutex));
763 iProbe->eventIndex = gTraceInfo.index.eventIndex++;
764 pthread_mutex_unlock(&(gTraceInfo.index.eventMutex));
766 iProbe->currentTime = getCurrentTime();
767 iProbe->pID = getpid();
768 iProbe->tID = _gettid();
769 iProbe->callDepth = gProbeDepth;
775 // update heap memory size through socket
776 // return 0 if size is updated through socket
777 // return 1 if size is updated into global variable
778 int update_heap_memory_size(bool isAdd, size_t size)
783 tmp = __sync_add_and_fetch(&g_total_alloc_size, (long)size);
787 tmp = __sync_sub_and_fetch(&g_total_alloc_size, (long)size);