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
41 #include <sys/syscall.h> // for syscall
42 #include <sys/time.h> // for gettimeofday
43 #include <sys/socket.h> // for socket, connect
44 #include <sys/un.h> // for sockaddr_un
45 #include <sys/timerfd.h> // for timerfd
47 #include "probeinfo.h"
50 #include "dacollection.h"
52 #define APP_INSTALL_PATH "/opt/apps"
53 #define UDS_NAME "/tmp/da.socket"
54 #define TIMERFD_INTERVAL 100000000 // 0.1 sec
56 __thread int gProbeDepth = 0;
57 __thread unsigned int gProbeBlockCount = 0;
58 __thread pid_t gTid = -1;
61 long g_total_alloc_size = 0;
62 pthread_t g_recvthread_id;
64 int getExecutableMappingAddress();
66 /******************************************************************************
68 (this means that these functions do not need to set enter/exit flag)
69 ******************************************************************************/
71 // runtime configure the probe option
72 static void _configure(char* configstr)
75 gTraceInfo.optionflag = atoi(configstr);
77 sprintf(buf, "configure in probe : %s, %lx\n", configstr, gTraceInfo.optionflag);
80 if(isOptionEnabled(OPT_FUNC))
90 // create sokcet to daemon and connect
91 static int createSocket(void)
94 int clientLen, ret = 0;
95 struct sockaddr_un clientAddr;
99 if((gTraceInfo.socket.daemonSock = socket(AF_UNIX, SOCK_STREAM,0)) != -1)
101 bzero(&clientAddr, sizeof(clientAddr));
102 clientAddr.sun_family = AF_UNIX;
103 sprintf(clientAddr.sun_path, "%s", UDS_NAME);
105 clientLen = sizeof(clientAddr);
106 if(connect(gTraceInfo.socket.daemonSock, (struct sockaddr *)&clientAddr, clientLen) >= 0)
108 // recv initial configuration value
109 recvlen = recv(gTraceInfo.socket.daemonSock, &log,
110 sizeof(log.type) + sizeof(log.length), MSG_WAITALL);
112 if(recvlen > 0) // recv succeed
116 recvlen = recv(gTraceInfo.socket.daemonSock, log.data,
117 log.length, MSG_WAITALL);
119 log.data[log.length] = '\0';
121 if(log.type == MSG_CONFIG)
123 _configure(log.data);
133 sprintf(buf, "recv failed in socket creation with error(%d)\n", recvlen);
136 else // closed by other peer
140 sprintf(buf, "%d|%u", getpid(), gTraceInfo.app.startTime);
141 printLogStr(buf, MSG_PID);
142 PRINTMSG("createSocket connect() success\n");
146 close(gTraceInfo.socket.daemonSock);
147 gTraceInfo.socket.daemonSock = -1;
160 // parse backtrace string and find out the caller of probed api function
161 // return 0 if caller is user binary, otherwise return 1
162 static int determineCaller(char* tracestring)
166 // determine whether saveptr (caller binary name) is user binary or not
167 substr = strstr(tracestring, APP_INSTALL_PATH);
169 if(substr == NULL) // not user binary
175 // return current thread id
176 static pid_t _gettid()
179 gTid = syscall(__NR_gettid); // syscall is very expensive
183 static void* recvThread(void* data)
185 fd_set readfds, workfds;
191 if(gTraceInfo.socket.daemonSock == -1)
198 FD_SET(g_timerfd, &readfds);
200 if(maxfd < gTraceInfo.socket.daemonSock)
201 maxfd = gTraceInfo.socket.daemonSock;
202 FD_SET(gTraceInfo.socket.daemonSock, &readfds);
207 rc = select(maxfd + 1, &workfds, NULL, NULL, NULL);
213 if(g_timerfd > 0 && FD_ISSET(g_timerfd, &workfds))
215 recvlen = read(g_timerfd, &xtime, sizeof(xtime));
218 log.length = sprintf(log.data, "%ld", g_total_alloc_size);
219 printLog(&log, MSG_ALLOC);
227 else if(FD_ISSET(gTraceInfo.socket.daemonSock, &workfds))
229 recvlen = recv(gTraceInfo.socket.daemonSock, &log,
230 sizeof(log.type) + sizeof(log.length), MSG_WAITALL);
232 if(recvlen > 0) // recv succeed
236 recvlen = recv(gTraceInfo.socket.daemonSock, log.data,
237 log.length, MSG_WAITALL);
239 log.data[log.length] = '\0';
241 if(log.type == MSG_CONFIG)
243 _configure(log.data);
245 else if(log.type == MSG_STOP)
252 sprintf(buf, "recv unknown message(%d)\n", log.type);
257 else if(recvlen == 0) // closed by other peer
259 close(gTraceInfo.socket.daemonSock);
260 gTraceInfo.socket.daemonSock = -1;
266 sprintf(buf, "recv failed in recv thread with error(%d)\n", recvlen);
273 PRINTMSG("unknown fd in recvThread\n");
281 /*****************************************************************************
282 * initialize / finalize function
283 *****************************************************************************/
285 void __attribute__((constructor)) _init_probe()
287 struct timeval ttime;
288 struct itimerspec ctime;
290 TRACE_STATE_SET(TS_INIT);
292 initialize_hash_table();
294 initialize_screencapture();
298 getExecutableMappingAddress();
300 // get app start time
301 gettimeofday(&ttime, NULL);
302 gTraceInfo.app.startTime = ((ttime.tv_sec * 10000 + (ttime.tv_usec/100)));
304 // create socket for communication with da_daemon
305 if(createSocket() == 0)
308 g_timerfd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC);
311 ctime.it_value.tv_sec = 0;
312 ctime.it_value.tv_nsec = TIMERFD_INTERVAL;
313 ctime.it_interval.tv_sec = 0;
314 ctime.it_interval.tv_nsec = TIMERFD_INTERVAL;
315 if(0 > timerfd_settime(g_timerfd, 0, &ctime, NULL))
317 PRINTMSG("failed to set timerfd\n");
324 PRINTMSG("failed to create timerdf\n");
327 // create recv Thread
328 if(pthread_create(&g_recvthread_id, NULL, recvThread, NULL) < 0) // thread creation failed
330 PRINTMSG("failed to crate recv thread\n");
332 update_heap_memory_size(true, 0);
339 PRINTMSG("dynamic analyzer probe helper so loading...\n");
341 gTraceInfo.init_complete = 1;
342 TRACE_STATE_UNSET(TS_INIT);
345 void __attribute__((destructor)) _fini_probe()
348 TRACE_STATE_SET(TS_FINIT);
350 gTraceInfo.init_complete = -1;
351 PRINTMSG("dynamic analyzer probe helper so unloading...\n");
360 if(gTraceInfo.socket.daemonSock != -1)
362 printLogStr(NULL, MSG_TERMINATE);
363 close(gTraceInfo.socket.daemonSock);
364 gTraceInfo.socket.daemonSock = -1;
369 finalize_screencapture();
371 finalize_hash_table();
373 for(i = 0; i < NUM_ORIGINAL_LIBRARY; i++)
375 if(lib_handle[i] != NULL)
377 dlclose(lib_handle[i]);
381 TRACE_STATE_UNSET(TS_FINIT);
385 /**************************************************************************
387 **************************************************************************/
389 /************************************************************************
390 * manipulate and print log functions
391 ************************************************************************/
393 bool printLog(log_t* log, int msgType)
396 if(unlikely(gTraceInfo.socket.daemonSock == -1))
399 if(unlikely(log == NULL))
402 TRACE_STATE_SET(TS_PRINT_LOG);
404 pthread_mutex_lock(&(gTraceInfo.socket.sockMutex));
405 res = send(gTraceInfo.socket.daemonSock, log, sizeof(log->type) + sizeof(log->length) + log->length, 0);
406 pthread_mutex_unlock(&(gTraceInfo.socket.sockMutex));
407 TRACE_STATE_UNSET(TS_PRINT_LOG);
412 bool printLogStr(const char* str, int msgType)
417 if(unlikely(gTraceInfo.socket.daemonSock == -1))
420 TRACE_STATE_SET(TS_PRINT_LOG);
425 sprintf(log.data, "%s", str);
426 log.length = strlen(str);
433 pthread_mutex_lock(&(gTraceInfo.socket.sockMutex));
434 res = send(gTraceInfo.socket.daemonSock, &log, sizeof(log.type) + sizeof(log.length) + log.length, MSG_DONTWAIT);
435 pthread_mutex_unlock(&(gTraceInfo.socket.sockMutex));
437 TRACE_STATE_UNSET(TS_PRINT_LOG);
442 // return 0 for successful case
443 // return non-zero for error
444 // if token is NULL then use DEFAULT TOKEN "`,"
445 // if token is not NULL then insert DEFAULT TOKEN before append input
446 int __appendTypeLog(log_t* log, int nInput, char* token, ...)
448 static char* default_token = DEFAULT_TOKEN;
451 char* seperator = default_token;
453 if(nInput <= 0 || log == NULL)
456 TRACE_STATE_SET(TS_APPEND_TYPE_LOG);
458 va_start(p_arg, token);
463 for(i = 0; i < nInput; i++)
465 type = va_arg(p_arg, int);
467 if(likely(log->length > 0)) // append token or default token
470 log->length += sprintf(log->data + log->length, "%s", default_token);
472 log->length += sprintf(log->data + log->length, "%s", seperator);
478 log->length += sprintf(log->data + log->length, "%d", va_arg(p_arg, int));
481 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, unsigned int));
484 log->length += sprintf(log->data + log->length, "%ld", va_arg(p_arg, long));
487 log->length += sprintf(log->data + log->length, "%lu", va_arg(p_arg, unsigned long));
490 log->length += sprintf(log->data + log->length, "%s", va_arg(p_arg, char*));
492 case VT_CHAR: // 'char' is promoted to 'int' when passed through '...'
493 log->length += sprintf(log->data + log->length, "%c", va_arg(p_arg, int));
496 log->length += sprintf(log->data + log->length, "%p", va_arg(p_arg, void*));
499 va_arg(p_arg, unsigned int);
502 log->length += sprintf(log->data + log->length, "%ld", va_arg(p_arg, off_t));
505 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, size_t));
508 log->length += sprintf(log->data + log->length, "%d", va_arg(p_arg, ssize_t));
511 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, socklen_t));
513 case VT_UINT16_T: // 'uint16_t' is promoted to 'int' when passed through '...'
514 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, int));
517 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, uint32_t));
520 log->length += sprintf(log->data + log->length, "%llu", va_arg(p_arg, uint64_t));
523 log->length += sprintf(log->data + log->length, "%u", va_arg(p_arg, mode_t));
526 log->length += sprintf(log->data + log->length, "%lu", va_arg(p_arg, dev_t));
529 log->length += sprintf(log->data + log->length, "%lu", va_arg(p_arg, nfds_t));
533 TRACE_STATE_UNSET(TS_APPEND_TYPE_LOG);
540 TRACE_STATE_UNSET(TS_APPEND_TYPE_LOG);
544 // get backtrace string
545 // return stack depth if succeed, otherwise return 0
546 // parameter 'log' cannot be null
547 int getBacktraceString(log_t* log, int bufsize)
549 void* array[MAX_STACK_DEPTH];
550 char** strings = NULL;
558 TRACE_STATE_SET(TS_BACKTRACE);
560 initsize = log->length;
561 log->data[log->length] = '\0'; // is this necessary ?
562 size = backtrace(array, MAX_STACK_DEPTH);
563 if(likely(size > TRIM_STACK_DEPTH))
565 strings = BACKTRACE_SYMBOLS(array + TRIM_STACK_DEPTH, size - TRIM_STACK_DEPTH);
567 if(likely(strings != NULL))
569 for(i = TRIM_STACK_DEPTH; i < size; i++)
571 stringlen = strlen(strings[i - TRIM_STACK_DEPTH]) + 14;
572 if(log->length + stringlen >= bufsize + initsize)
575 log->length += sprintf(log->data + log->length, "%010u`,%s`,", (unsigned int)(array[i]), strings[i - TRIM_STACK_DEPTH]);
577 log->data[log->length-2] = '\0';
581 else // failed to get backtrace symbols
583 // just print trace address
584 for(i = TRIM_STACK_DEPTH; i < size; i++)
587 if(log->length + stringlen >= bufsize + initsize)
590 log->length += sprintf(log->data + log->length, "%010u`,(unknown)`,", (unsigned int)(array[i]));
592 log->data[log->length-2] = '\0';
596 TRACE_STATE_UNSET(TS_BACKTRACE);
597 return (int)(size - TRIM_STACK_DEPTH);
601 TRACE_STATE_UNSET(TS_BACKTRACE);
606 /*************************************************************************
607 * probe block control functions
608 *************************************************************************/
609 int preBlockBegin(void* caller, bool bFiltering, enum DaOptions option)
615 if(gSTrace != 0 || gProbeDepth != 0)
618 if(gTraceInfo.init_complete <= 0)
621 if((gTraceInfo.optionflag & option) == 0)
624 TRACE_STATE_SET(TS_ENTER_PROBE_BLOCK);
626 if(gTraceInfo.exec_map.map_start != NULL)
628 // address comparison
629 if(caller >= gTraceInfo.exec_map.map_start &&
630 caller <= gTraceInfo.exec_map.map_end)
641 // backtrace for filtering
643 strings = BACKTRACE_SYMBOLS(tarray, 1);
646 if((determineCaller(strings[0]) == 0))
656 if((!user) && bFiltering)
658 TRACE_STATE_UNSET(TS_ENTER_PROBE_BLOCK);
669 return 2; // user call
671 return 1; // internal call
674 int postBlockBegin(int preresult)
678 TRACE_STATE_SET(TS_ENTER_PROBE_BLOCK);
686 TRACE_STATE_UNSET(TS_ENTER_PROBE_BLOCK);
692 TRACE_STATE_UNSET(TS_ENTER_PROBE_BLOCK);
695 // for block that have to be run
696 void probeBlockStart()
699 if(gProbeBlockCount == 1)
700 TRACE_STATE_SET(TS_PROBE);
705 if(gProbeBlockCount == 1)
706 TRACE_STATE_UNSET(TS_PROBE);
710 /*************************************************************************
711 * helper info getter functions
712 *************************************************************************/
713 // return current time in 1/10000 sec unit
714 unsigned long getCurrentTime()
716 struct timeval cTime;
718 gettimeofday(&cTime, NULL);
720 return (unsigned long)((cTime.tv_sec * 10000 + (cTime.tv_usec/100)));
723 unsigned int getCurrentEventIndex()
725 return gTraceInfo.index.eventIndex;
728 unsigned int getCallDepth()
733 unsigned long getTraceState()
738 /******************************************************************
739 * screen capture and event related functions
740 ******************************************************************/
743 // TRACE_STATE_SET is not necessary because this function is called in probe block only
744 int registeScreenChange(int renderID)
746 if(gTraceInfo.snapshot.isTouchDown == 0)
751 if(gTraceInfo.snapshot.snapshotAPICallStartTime == 0){
752 gTraceInfo.snapshot.snapshotAPICallStartTime = getCurrentTime();
753 gTraceInfo.snapshot.snapshotAPICallCount = 1;
756 gTraceInfo.snapshot.snapshotAPICallCount++;
758 gTraceInfo.snapshot.renderID = renderID;
763 void detectTouchEvent(int touchID)
765 gTraceInfo.stateTouch = touchID;
767 TRACE_STATE_SET(TS_DETECT_TOUCH);
768 if(isOptionEnabled(OPT_SNAPSHOT))
770 if(touchID == EVENT_TYPE_UP)
772 if(gTraceInfo.snapshot.isTouchDown == 1)
774 gTraceInfo.snapshot.snapshotAPICallStartTime = getCurrentTime();
777 gTraceInfo.snapshot.isTouchDown = 1;
780 TRACE_STATE_UNSET(TS_DETECT_TOUCH);
785 return gTraceInfo.stateTouch;
788 // TRACE_STATE_SET is not necessary because this function is called in probe block only
789 int isPossibleCapture()
791 if(gTraceInfo.snapshot.isTouchDown == 0)
796 gTraceInfo.snapshot.snapshotAPICallCount--;
798 if(((getCurrentTime() - gTraceInfo.snapshot.snapshotAPICallStartTime) < SNAPSHOT_WAIT_TIME_MAX) && (gTraceInfo.snapshot.snapshotAPICallCount > 0))
803 gTraceInfo.snapshot.isTouchDown = 0;
804 gTraceInfo.snapshot.snapshotAPICallStartTime = 0;
805 gTraceInfo.snapshot.snapshotAPICallCount = 0;
806 gTraceInfo.snapshot.renderID = -1;
812 /************************************************************************
814 ************************************************************************/
815 bool setProbePoint(probeInfo_t* iProbe)
817 if(unlikely(iProbe == NULL))
822 TRACE_STATE_SET(TS_SET_PROBE_POINT);
824 // atomic operaion(gcc builtin) is more expensive then pthread_mutex
825 pthread_mutex_lock(&(gTraceInfo.index.eventMutex));
826 iProbe->eventIndex = gTraceInfo.index.eventIndex++;
827 pthread_mutex_unlock(&(gTraceInfo.index.eventMutex));
829 iProbe->currentTime = getCurrentTime();
830 iProbe->pID = getpid();
831 iProbe->tID = _gettid();
832 iProbe->callDepth = gProbeDepth;
834 TRACE_STATE_UNSET(TS_SET_PROBE_POINT);
838 // update heap memory size through socket
839 // return 0 if size is updated through socket
840 // return 1 if size is updated into global variable
841 int update_heap_memory_size(bool isAdd, size_t size)
846 tmp = __sync_add_and_fetch(&g_total_alloc_size, (long)size);
850 tmp = __sync_sub_and_fetch(&g_total_alloc_size, (long)size);