-/*\r
- * DA probe\r
- *\r
- * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.\r
- *\r
- * Contact: \r
- *\r
- * Jaewon Lim <jaewon81.lim@samsung.com>\r
- * Woojin Jung <woojin2.jung@samsung.com>\r
- * Juyoung Kim <j0.kim@samsung.com>\r
- * \r
- * This library is free software; you can redistribute it and/or modify it under\r
- * the terms of the GNU Lesser General Public License as published by the\r
- * Free Software Foundation; either version 2.1 of the License, or (at your option)\r
- * any later version.\r
- * \r
- * This library is distributed in the hope that it will be useful, but WITHOUT ANY\r
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public\r
- * License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public License\r
- * along with this library; if not, write to the Free Software Foundation, Inc., 51\r
- * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
- *\r
- * Contributors:\r
- * - S-Core Co., Ltd\r
- * \r
- */\r
-\r
-#include <pthread.h>\r
-#include <signal.h> // for signal\r
-#include <unistd.h> // for ualarm, sleep\r
-#include <sys/timerfd.h> // for timerfd\r
-#include <stdio.h>\r
-#include <errno.h> // for errno\r
-#include <string.h> // for memset\r
-#include <stdint.h> // for uint64_t\r
-\r
-#include "daprobe.h"\r
-#include "dahelper.h"\r
-#include "probeinfo.h"\r
-#define _USE_DA_\r
-#include "da_chart.h"\r
-\r
-#include "binproto.h"\r
-\r
-#define ERR_THREAD_CREATE_FAIL -2001 // thread creation fail\r
-\r
-#define MAX_TITLE_LENGTH 16\r
-#define MAX_CHART_HANDLE 10 // cannot be exceeded 10\r
-#define MAX_SERIES_PER_CHART 4 // cannot be exceeded 10\r
-// chart handle is from 1 to 9\r
-// series handle is from 11 to 99\r
-// series handle 32 means that the series is 2nd series in 3rd chart.\r
-// zero value handle means error\r
-\r
-// =====================================================================\r
-// global variable\r
-// =====================================================================\r
-\r
-typedef struct _chart_interval_callback\r
-{\r
- da_handle chart_handle;\r
- da_handle series_handle;\r
- da_user_data_2_chart_data callback;\r
- void* user_data;\r
- struct _chart_interval_callback* next;\r
-} chart_interval_callback;\r
-\r
-typedef struct\r
-{\r
- int timerfd;\r
- pthread_t thread_handle;\r
- chart_interval_callback* callback_list;\r
- pthread_mutex_t list_mutex;\r
-} interval_manager;\r
-\r
-typedef struct\r
-{\r
- int chart_handle_index;\r
- int series_handle_index[MAX_CHART_HANDLE];\r
- int interval_for_series[MAX_CHART_HANDLE][MAX_SERIES_PER_CHART];\r
- interval_manager interval_10ms;\r
- interval_manager interval_100ms;\r
- interval_manager interval_1s;\r
-} chart_handle_maintainer;\r
-\r
-chart_handle_maintainer chm;\r
-\r
-__thread pid_t cur_thread = -1;\r
-\r
-\r
-// =====================================================================\r
-// internal macro definition\r
-// =====================================================================\r
-\r
-#define DECLARE_CHART_VARIABLE \\r
- da_handle __attribute__((unused)) ret = 0; \\r
- probeInfo_t probeInfo; \\r
- log_t log\r
-\r
-#define APPEND_LOG_CHART_RESULT(RESULT) \\r
- log.length += sprintf(log.data + log.length, "`,%d`,0x%p`,0`,2", \\r
- RESULT, CALLER_ADDRESS)\r
-\r
-#define APPEND_LOG_CHART(HANDLE, TYPE, TEXT, COLOR, VALUE) \\r
- log.length += sprintf(log.data + log.length, "`,`,%d`,%d`,%s`,%d`,%.3f", \\r
- HANDLE, TYPE, TEXT, COLOR, VALUE)\r
-\r
-// =====================================================================\r
-// timer thread routine for callback\r
-// =====================================================================\r
-\r
-void* _chart_timerThread(void* data)\r
-{\r
- DECLARE_CHART_VARIABLE;\r
-\r
- uint64_t exp;\r
- ssize_t readsize;\r
- chart_interval_callback* cur;\r
- float value;\r
- sigset_t profsigmask;\r
- interval_manager* pmanager = (interval_manager*)data;\r
-\r
- probeBlockStart();\r
-\r
- sigemptyset(&profsigmask);\r
- sigaddset(&profsigmask, SIGPROF);\r
- pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);\r
-\r
- while((readsize = read(pmanager->timerfd, &exp, sizeof(uint64_t))) > 0)\r
- {\r
- pthread_mutex_lock(&pmanager->list_mutex);\r
-\r
- cur = pmanager->callback_list;\r
- while(cur != NULL)\r
- {\r
- value = cur->callback(cur->user_data);\r
-\r
- INIT_LOG;\r
- setProbePoint(&probeInfo);\r
- log.length = sprintf(log.data, "%d`,%d`,%s`,%lu`,%d`,%d`,`,`,0x%p`,0`,2`,`,%d`,0`,`,0`,%.3f",\r
- LC_CUSTOM, probeInfo.eventIndex, __func__, probeInfo.currentTime, probeInfo.pID,\r
- probeInfo.tID, CALLER_ADDRESS, cur->series_handle, value);\r
- printLog(&log, MSG_LOG);\r
-\r
- PREPARE_LOCAL_BUF();\r
- PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "", 0);\r
- PACK_COMMON_END(0, 0, 2);\r
- PACK_CUSTOM(cur->series_handle, 0, "", 0, value);\r
- FLUSH_LOCAL_BUF();\r
-\r
- cur = cur->next;\r
- }\r
-\r
- pthread_mutex_unlock(&pmanager->list_mutex);\r
-\r
- sleep(0);\r
- }\r
-\r
- probeBlockEnd();\r
-\r
- return NULL;\r
-}\r
-\r
-static int start_callback_thread(chart_interval interval)\r
-{\r
- int* timerfd = NULL;\r
- pthread_t* thread_handle = NULL;\r
- interval_manager* pman = NULL;\r
- struct itimerspec timevalue;\r
-\r
- if(interval == CHART_INTERVAL_10MSEC)\r
- {\r
- timerfd = &(chm.interval_10ms.timerfd);\r
- thread_handle = &(chm.interval_10ms.thread_handle);\r
- pman = &(chm.interval_10ms);\r
- timevalue.it_value.tv_sec = 0;\r
- timevalue.it_value.tv_nsec = 10000000L;\r
- timevalue.it_interval.tv_sec = 0;\r
- timevalue.it_interval.tv_nsec = 10000000L;\r
- }\r
- else if(interval == CHART_INTERVAL_100MSEC)\r
- {\r
- timerfd = &(chm.interval_100ms.timerfd);\r
- thread_handle = &(chm.interval_100ms.thread_handle);\r
- pman = &(chm.interval_100ms);\r
- timevalue.it_value.tv_sec = 0;\r
- timevalue.it_value.tv_nsec = 100000000L;\r
- timevalue.it_interval.tv_sec = 0;\r
- timevalue.it_interval.tv_nsec = 100000000L;\r
- }\r
- else if(interval == CHART_INTERVAL_1SEC)\r
- {\r
- timerfd = &(chm.interval_1s.timerfd);\r
- thread_handle = &(chm.interval_1s.thread_handle);\r
- pman = &(chm.interval_1s);\r
- timevalue.it_value.tv_sec = 1;\r
- timevalue.it_value.tv_nsec = 0;\r
- timevalue.it_interval.tv_sec = 1;\r
- timevalue.it_interval.tv_nsec = 0;\r
- }\r
- else\r
- {\r
- return ERR_WRONG_PARAMETER;\r
- }\r
-\r
- if(*timerfd != -1 || *thread_handle != -1)\r
- return 0; // already thread exist\r
-\r
- *timerfd = timerfd_create(CLOCK_REALTIME, 0);\r
- if(*timerfd == -1)\r
- return errno;\r
-\r
- if(timerfd_settime(*timerfd, 0, &timevalue, NULL) == -1)\r
- return errno;\r
-\r
- if(pthread_create(thread_handle, NULL, _chart_timerThread, pman) < 0)\r
- return ERR_THREAD_CREATE_FAIL;\r
-\r
- return 0;\r
-}\r
-\r
-\r
-// =====================================================================\r
-// internal utility function\r
-// =====================================================================\r
-\r
-static void add_to_callback_list(chart_interval interval, da_handle charthandle, da_handle series_handle,\r
- da_user_data_2_chart_data callback, void* user_data)\r
-{\r
- chart_interval_callback* newelem;\r
- \r
- newelem = (chart_interval_callback*)malloc(sizeof(chart_interval_callback));\r
- newelem->chart_handle = charthandle;\r
- newelem->series_handle = series_handle;\r
- newelem->callback = callback;\r
- newelem->user_data = user_data;\r
-\r
- chm.interval_for_series[charthandle][series_handle % 10] = interval;\r
- \r
- switch(interval)\r
- {\r
- case CHART_INTERVAL_10MSEC:\r
- pthread_mutex_lock(&chm.interval_10ms.list_mutex);\r
- newelem->next = chm.interval_10ms.callback_list;\r
- chm.interval_10ms.callback_list = newelem;\r
- pthread_mutex_unlock(&chm.interval_10ms.list_mutex);\r
- __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
- break;\r
- case CHART_INTERVAL_100MSEC:\r
- pthread_mutex_lock(&chm.interval_100ms.list_mutex);\r
- newelem->next = chm.interval_100ms.callback_list;\r
- chm.interval_100ms.callback_list = newelem;\r
- pthread_mutex_unlock(&chm.interval_100ms.list_mutex);\r
- __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
- break;\r
- case CHART_INTERVAL_1SEC:\r
- pthread_mutex_lock(&chm.interval_1s.list_mutex);\r
- newelem->next = chm.interval_1s.callback_list;\r
- chm.interval_1s.callback_list = newelem;\r
- pthread_mutex_unlock(&chm.interval_1s.list_mutex);\r
- __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
- break;\r
- default:\r
- free(newelem);\r
- break;\r
- }\r
-}\r
-\r
-static void remove_all_callback_list()\r
-{\r
- chart_interval_callback* cur;\r
- \r
- pthread_mutex_lock(&chm.interval_10ms.list_mutex);\r
- while(chm.interval_10ms.callback_list != NULL)\r
- {\r
- cur = chm.interval_10ms.callback_list;\r
- chm.interval_10ms.callback_list = cur->next;\r
- free(cur);\r
- }\r
- pthread_mutex_unlock(&chm.interval_10ms.list_mutex);\r
-\r
- pthread_mutex_lock(&chm.interval_100ms.list_mutex);\r
- while(chm.interval_100ms.callback_list != NULL)\r
- {\r
- cur = chm.interval_100ms.callback_list;\r
- chm.interval_100ms.callback_list = cur->next;\r
- free(cur);\r
- }\r
- pthread_mutex_unlock(&chm.interval_100ms.list_mutex);\r
-\r
- pthread_mutex_lock(&chm.interval_1s.list_mutex);\r
- while(chm.interval_1s.callback_list != NULL)\r
- {\r
- cur = chm.interval_1s.callback_list;\r
- chm.interval_1s.callback_list = cur->next;\r
- free(cur);\r
- }\r
- pthread_mutex_unlock(&chm.interval_1s.list_mutex);\r
-\r
- memset(&chm.interval_for_series, 0, sizeof(chm.interval_for_series));\r
- __sync_and_and_fetch(&(gTraceInfo.custom_chart_callback_count), 0);\r
-}\r
-\r
-static void remove_from_callback_list(da_handle charthandle, da_handle series_handle)\r
-{\r
- chart_interval_callback *prev, *cur;\r
- chart_interval interval;\r
-\r
- interval = chm.interval_for_series[charthandle][series_handle % 10];\r
- chm.interval_for_series[charthandle][series_handle % 10] = 0;\r
-\r
- switch(interval)\r
- {\r
- case CHART_INTERVAL_10MSEC:\r
- pthread_mutex_lock(&chm.interval_10ms.list_mutex);\r
-\r
- prev = NULL;\r
- cur = chm.interval_10ms.callback_list;\r
- while(cur != NULL)\r
- {\r
- if(cur->chart_handle == charthandle && cur->series_handle == series_handle)\r
- {\r
- if(prev)\r
- prev->next = cur->next;\r
- else\r
- chm.interval_10ms.callback_list = cur->next;\r
-\r
- __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
- free(cur);\r
- break;\r
- }\r
-\r
- prev = cur;\r
- cur = cur->next;\r
- }\r
-\r
- pthread_mutex_unlock(&chm.interval_10ms.list_mutex);\r
- break;\r
- case CHART_INTERVAL_100MSEC:\r
- pthread_mutex_lock(&chm.interval_100ms.list_mutex);\r
-\r
- prev = NULL;\r
- cur = chm.interval_100ms.callback_list;\r
- while(cur != NULL)\r
- {\r
- if(cur->chart_handle == charthandle && cur->series_handle == series_handle)\r
- {\r
- if(prev)\r
- prev->next = cur->next;\r
- else\r
- chm.interval_100ms.callback_list = cur->next;\r
-\r
- __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
- free(cur);\r
- break;\r
- }\r
-\r
- prev = cur;\r
- cur = cur->next;\r
- }\r
-\r
- pthread_mutex_unlock(&chm.interval_100ms.list_mutex);\r
- break;\r
- case CHART_INTERVAL_1SEC:\r
- pthread_mutex_lock(&chm.interval_1s.list_mutex);\r
-\r
- prev = NULL;\r
- cur = chm.interval_1s.callback_list;\r
- while(cur != NULL)\r
- {\r
- if(cur->chart_handle == charthandle && cur->series_handle == series_handle)\r
- {\r
- if(prev)\r
- prev->next = cur->next;\r
- else\r
- chm.interval_1s.callback_list = cur->next;\r
-\r
- __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
- free(cur);\r
- break;\r
- }\r
-\r
- prev = cur;\r
- cur = cur->next;\r
- }\r
-\r
- pthread_mutex_unlock(&chm.interval_1s.list_mutex);\r
- break;\r
- default:\r
- break;\r
- }\r
-}\r
-\r
-\r
-// =====================================================================\r
-// constructor and destructor functions\r
-// =====================================================================\r
-\r
-void __attribute__((constructor)) _init_lib()\r
-{\r
- probeBlockStart();\r
-\r
- memset(&chm, 0, sizeof(chm));\r
- chm.interval_10ms.timerfd = -1;\r
- chm.interval_100ms.timerfd = -1;\r
- chm.interval_1s.timerfd = -1;\r
- chm.interval_10ms.thread_handle = -1;\r
- chm.interval_100ms.thread_handle = -1;\r
- chm.interval_1s.thread_handle = -1;\r
- pthread_mutex_init(&chm.interval_10ms.list_mutex, NULL);\r
- pthread_mutex_init(&chm.interval_100ms.list_mutex, NULL);\r
- pthread_mutex_init(&chm.interval_1s.list_mutex, NULL);\r
-\r
- probeBlockEnd();\r
-}\r
-\r
-void __attribute__((destructor)) _fini_lib()\r
-{\r
- probeBlockStart();\r
-\r
- remove_all_callback_list();\r
- if(chm.interval_10ms.timerfd != -1)\r
- close(chm.interval_10ms.timerfd);\r
- if(chm.interval_100ms.timerfd != -1)\r
- close(chm.interval_100ms.timerfd);\r
- if(chm.interval_1s.timerfd != -1)\r
- close(chm.interval_1s.timerfd);\r
-\r
- if(chm.interval_10ms.thread_handle != -1)\r
- {\r
- pthread_join(chm.interval_10ms.thread_handle, NULL);\r
- }\r
- if(chm.interval_100ms.thread_handle != -1)\r
- {\r
- pthread_join(chm.interval_100ms.thread_handle, NULL);\r
- }\r
- if(chm.interval_1s.thread_handle != -1)\r
- {\r
- pthread_join(chm.interval_1s.thread_handle, NULL);\r
- }\r
-\r
- probeBlockEnd();\r
-}\r
-\r
-\r
-// =====================================================================\r
-// api definition\r
-// =====================================================================\r
-\r
-void da_mark(chart_color color, char* mark_text)\r
-{\r
- DECLARE_CHART_VARIABLE;\r
- \r
- probeBlockStart();\r
- \r
- INIT_LOG;\r
- setProbePoint(&probeInfo);\r
- APPEND_LOG_BASIC(LC_CUSTOM);\r
- APPEND_LOG_INPUT("%s", "");\r
- APPEND_LOG_CHART_RESULT(0);\r
- APPEND_LOG_CHART(0, 0, mark_text, color, 0.0f);\r
- printLog(&log, MSG_LOG);\r
-\r
- PREPARE_LOCAL_BUF();\r
- PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dp", color, mark_text);\r
- PACK_COMMON_END(0, 0, 2);\r
- PACK_CUSTOM(0, 0, mark_text, color, 0.0f);\r
- FLUSH_LOCAL_BUF();\r
-\r
- probeBlockEnd();\r
-}\r
-\r
-da_handle da_create_chart(char* chart_name)\r
-{\r
- DECLARE_CHART_VARIABLE;\r
-\r
- // check if there is available chart handle slot\r
- if(chm.chart_handle_index + 1 >= MAX_CHART_HANDLE)\r
- return ERR_MAX_CHART_NUMBER;\r
- \r
- // check if chart_name is null\r
- if(chart_name == NULL)\r
- return ERR_WRONG_PARAMETER;\r
- \r
- probeBlockStart();\r
- ret = ++(chm.chart_handle_index);\r
-\r
- INIT_LOG;\r
- setProbePoint(&probeInfo);\r
- APPEND_LOG_BASIC(LC_CUSTOM);\r
- APPEND_LOG_INPUT("%s", "");\r
- APPEND_LOG_CHART_RESULT(ret);\r
- APPEND_LOG_CHART(0, 0, chart_name, 0, 0.0f);\r
- printLog(&log, MSG_LOG);\r
-\r
- PREPARE_LOCAL_BUF();\r
- PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "p", chart_name);\r
- PACK_COMMON_END(ret, 0, 2);\r
- PACK_CUSTOM(0, 0, chart_name, 0, 0.0f);\r
- FLUSH_LOCAL_BUF();\r
- \r
- probeBlockEnd();\r
- \r
- return ret;\r
-}\r
-\r
-da_handle da_create_series(da_handle charthandle, char* seriesname,\r
- series_type type, chart_color color)\r
-{\r
- DECLARE_CHART_VARIABLE;\r
- \r
- // check if charthandle is valid handle or not\r
- if(charthandle <= 0 || charthandle > chm.chart_handle_index)\r
- return ERR_WRONG_HANDLE;\r
- \r
- // chech if extra parameter is valid\r
- if(seriesname == NULL)\r
- return ERR_WRONG_PARAMETER;\r
-\r
- // check if there is available series spot\r
- if(chm.series_handle_index[(int)charthandle] + 1 >= MAX_SERIES_PER_CHART)\r
- return ERR_MAX_CHART_NUMBER;\r
- \r
- probeBlockStart();\r
- ret = ++(chm.series_handle_index[charthandle]);\r
- ret += (10 * charthandle);\r
- \r
- INIT_LOG;\r
- setProbePoint(&probeInfo);\r
- APPEND_LOG_BASIC(LC_CUSTOM);\r
- APPEND_LOG_INPUT("%s", "");\r
- APPEND_LOG_CHART_RESULT(ret);\r
- APPEND_LOG_CHART(charthandle, type, seriesname, color, 0.0f);\r
- printLog(&log, MSG_LOG);\r
-\r
- PREPARE_LOCAL_BUF();\r
- PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dpdd", charthandle, seriesname, type, color);\r
- PACK_COMMON_END(ret, 0, 2);\r
- PACK_CUSTOM(charthandle, type, seriesname, color, 0.0f);\r
- FLUSH_LOCAL_BUF();\r
-\r
- probeBlockEnd();\r
- \r
- return ret;\r
-}\r
- \r
-da_handle da_create_default_series(da_handle charthandle, char* seriesname)\r
-{\r
- return da_create_series(charthandle, seriesname,\r
- CHART_TYPE_AUTO, CHART_COLOR_AUTO);\r
-}\r
-\r
-int da_set_callback(da_handle series_handle, da_user_data_2_chart_data callback,\r
- void* data_addr, chart_interval interval)\r
-{\r
- int cindex, sindex;\r
- cindex = (int)(series_handle / 10);\r
- sindex = series_handle % 10;\r
- \r
- // check series handle\r
- if(cindex <= 0 || cindex > chm.chart_handle_index)\r
- return ERR_WRONG_HANDLE;\r
- \r
- if(sindex > chm.series_handle_index[(int)cindex])\r
- return ERR_WRONG_HANDLE;\r
- \r
- // check rest parameters\r
- if(interval == CHART_NO_CYCLE && callback != NULL)\r
- return ERR_WRONG_PARAMETER;\r
-\r
- probeBlockStart();\r
-\r
- // remove previously registered callback\r
- remove_from_callback_list(cindex, series_handle);\r
-\r
- // register new callback\r
- if(callback != NULL)\r
- {\r
- add_to_callback_list(interval, cindex, series_handle, callback, data_addr);\r
- start_callback_thread(interval);\r
- }\r
- probeBlockEnd();\r
-\r
- return 0;\r
-}\r
-\r
-void da_log(da_handle series_handle, float uservalue)\r
-{\r
- DECLARE_CHART_VARIABLE;\r
- \r
- // chech if series handle is valid\r
- int cindex, sindex;\r
- cindex = (int)(series_handle / 10);\r
- sindex = series_handle % 10;\r
- \r
- if(cindex <= 0 || cindex > chm.chart_handle_index)\r
- return;\r
- \r
- if(sindex > chm.series_handle_index[(int)cindex])\r
- return;\r
- \r
- probeBlockStart();\r
- \r
- INIT_LOG;\r
- setProbePoint(&probeInfo);\r
- APPEND_LOG_BASIC(LC_CUSTOM);\r
- APPEND_LOG_INPUT("%s", "");\r
- APPEND_LOG_CHART_RESULT(0);\r
- APPEND_LOG_CHART(series_handle, 0, "", 0, uservalue);\r
- printLog(&log, MSG_LOG);\r
-\r
- PREPARE_LOCAL_BUF();\r
- PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dw", series_handle, uservalue);\r
- PACK_COMMON_END(0, 0, 2);\r
- PACK_CUSTOM(series_handle, 0, "", 0, uservalue);\r
- FLUSH_LOCAL_BUF();\r
- \r
- probeBlockEnd();\r
-}\r
+/*
+ * DA probe
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact:
+ *
+ * Jaewon Lim <jaewon81.lim@samsung.com>
+ * Woojin Jung <woojin2.jung@samsung.com>
+ * Juyoung Kim <j0.kim@samsung.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your option)
+ * any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ */
+
+#include <pthread.h>
+#include <signal.h> // for signal
+#include <unistd.h> // for ualarm, sleep
+#include <sys/timerfd.h> // for timerfd
+#include <stdio.h>
+#include <errno.h> // for errno
+#include <string.h> // for memset
+#include <stdint.h> // for uint64_t
+
+#include "daprobe.h"
+#include "dahelper.h"
+#include "probeinfo.h"
+#define _USE_DA_
+#include "da_chart.h"
+
+#include "binproto.h"
+
+#define ERR_THREAD_CREATE_FAIL -2001 // thread creation fail
+
+#define MAX_TITLE_LENGTH 16
+#define MAX_CHART_HANDLE 10 // cannot be exceeded 10
+#define MAX_SERIES_PER_CHART 4 // cannot be exceeded 10
+// chart handle is from 1 to 9
+// series handle is from 11 to 99
+// series handle 32 means that the series is 2nd series in 3rd chart.
+// zero value handle means error
+
+// =====================================================================
+// global variable
+// =====================================================================
+
+typedef struct _chart_interval_callback
+{
+ da_handle chart_handle;
+ da_handle series_handle;
+ da_user_data_2_chart_data callback;
+ void* user_data;
+ struct _chart_interval_callback* next;
+} chart_interval_callback;
+
+typedef struct
+{
+ int timerfd;
+ pthread_t thread_handle;
+ chart_interval_callback* callback_list;
+ pthread_mutex_t list_mutex;
+} interval_manager;
+
+typedef struct
+{
+ int chart_handle_index;
+ int series_handle_index[MAX_CHART_HANDLE];
+ int interval_for_series[MAX_CHART_HANDLE][MAX_SERIES_PER_CHART];
+ interval_manager interval_10ms;
+ interval_manager interval_100ms;
+ interval_manager interval_1s;
+} chart_handle_maintainer;
+
+chart_handle_maintainer chm;
+
+__thread pid_t cur_thread = -1;
+
+
+// =====================================================================
+// internal macro definition
+// =====================================================================
+
+#define DECLARE_CHART_VARIABLE \
+ da_handle __attribute__((unused)) ret = 0; \
+ probeInfo_t probeInfo; \
+ log_t log
+
+#define APPEND_LOG_CHART_RESULT(RESULT) \
+ log.length += sprintf(log.data + log.length, "`,%d`,0x%p`,0`,2", \
+ RESULT, CALLER_ADDRESS)
+
+#define APPEND_LOG_CHART(HANDLE, TYPE, TEXT, COLOR, VALUE) \
+ log.length += sprintf(log.data + log.length, "`,`,%d`,%d`,%s`,%d`,%.3f", \
+ HANDLE, TYPE, TEXT, COLOR, VALUE)
+
+// =====================================================================
+// timer thread routine for callback
+// =====================================================================
+
+void* _chart_timerThread(void* data)
+{
+ DECLARE_CHART_VARIABLE;
+
+ uint64_t exp;
+ ssize_t readsize;
+ chart_interval_callback* cur;
+ float value;
+ sigset_t profsigmask;
+ interval_manager* pmanager = (interval_manager*)data;
+
+ probeBlockStart();
+
+ sigemptyset(&profsigmask);
+ sigaddset(&profsigmask, SIGPROF);
+ pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);
+
+ while((readsize = read(pmanager->timerfd, &exp, sizeof(uint64_t))) > 0)
+ {
+ pthread_mutex_lock(&pmanager->list_mutex);
+
+ cur = pmanager->callback_list;
+ while(cur != NULL)
+ {
+ value = cur->callback(cur->user_data);
+
+ INIT_LOG;
+ setProbePoint(&probeInfo);
+ log.length = sprintf(log.data, "%d`,%d`,%s`,%lu`,%d`,%d`,`,`,0x%p`,0`,2`,`,%d`,0`,`,0`,%.3f",
+ LC_CUSTOM, probeInfo.eventIndex, __func__, probeInfo.currentTime, probeInfo.pID,
+ probeInfo.tID, CALLER_ADDRESS, cur->series_handle, value);
+ printLog(&log, MSG_LOG);
+
+ PREPARE_LOCAL_BUF();
+ PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "", 0);
+ PACK_COMMON_END(0, 0, 2);
+ PACK_CUSTOM(cur->series_handle, 0, "", 0, value);
+ FLUSH_LOCAL_BUF();
+
+ cur = cur->next;
+ }
+
+ pthread_mutex_unlock(&pmanager->list_mutex);
+
+ sleep(0);
+ }
+
+ probeBlockEnd();
+
+ return NULL;
+}
+
+static int start_callback_thread(chart_interval interval)
+{
+ int* timerfd = NULL;
+ pthread_t* thread_handle = NULL;
+ interval_manager* pman = NULL;
+ struct itimerspec timevalue;
+
+ if(interval == CHART_INTERVAL_10MSEC)
+ {
+ timerfd = &(chm.interval_10ms.timerfd);
+ thread_handle = &(chm.interval_10ms.thread_handle);
+ pman = &(chm.interval_10ms);
+ timevalue.it_value.tv_sec = 0;
+ timevalue.it_value.tv_nsec = 10000000L;
+ timevalue.it_interval.tv_sec = 0;
+ timevalue.it_interval.tv_nsec = 10000000L;
+ }
+ else if(interval == CHART_INTERVAL_100MSEC)
+ {
+ timerfd = &(chm.interval_100ms.timerfd);
+ thread_handle = &(chm.interval_100ms.thread_handle);
+ pman = &(chm.interval_100ms);
+ timevalue.it_value.tv_sec = 0;
+ timevalue.it_value.tv_nsec = 100000000L;
+ timevalue.it_interval.tv_sec = 0;
+ timevalue.it_interval.tv_nsec = 100000000L;
+ }
+ else if(interval == CHART_INTERVAL_1SEC)
+ {
+ timerfd = &(chm.interval_1s.timerfd);
+ thread_handle = &(chm.interval_1s.thread_handle);
+ pman = &(chm.interval_1s);
+ timevalue.it_value.tv_sec = 1;
+ timevalue.it_value.tv_nsec = 0;
+ timevalue.it_interval.tv_sec = 1;
+ timevalue.it_interval.tv_nsec = 0;
+ }
+ else
+ {
+ return ERR_WRONG_PARAMETER;
+ }
+
+ if(*timerfd != -1 || *thread_handle != -1)
+ return 0; // already thread exist
+
+ *timerfd = timerfd_create(CLOCK_REALTIME, 0);
+ if(*timerfd == -1)
+ return errno;
+
+ if(timerfd_settime(*timerfd, 0, &timevalue, NULL) == -1)
+ return errno;
+
+ if(pthread_create(thread_handle, NULL, _chart_timerThread, pman) < 0)
+ return ERR_THREAD_CREATE_FAIL;
+
+ return 0;
+}
+
+
+// =====================================================================
+// internal utility function
+// =====================================================================
+
+static void add_to_callback_list(chart_interval interval, da_handle charthandle, da_handle series_handle,
+ da_user_data_2_chart_data callback, void* user_data)
+{
+ chart_interval_callback* newelem;
+
+ newelem = (chart_interval_callback*)malloc(sizeof(chart_interval_callback));
+ newelem->chart_handle = charthandle;
+ newelem->series_handle = series_handle;
+ newelem->callback = callback;
+ newelem->user_data = user_data;
+
+ chm.interval_for_series[charthandle][series_handle % 10] = interval;
+
+ switch(interval)
+ {
+ case CHART_INTERVAL_10MSEC:
+ pthread_mutex_lock(&chm.interval_10ms.list_mutex);
+ newelem->next = chm.interval_10ms.callback_list;
+ chm.interval_10ms.callback_list = newelem;
+ pthread_mutex_unlock(&chm.interval_10ms.list_mutex);
+ __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);
+ break;
+ case CHART_INTERVAL_100MSEC:
+ pthread_mutex_lock(&chm.interval_100ms.list_mutex);
+ newelem->next = chm.interval_100ms.callback_list;
+ chm.interval_100ms.callback_list = newelem;
+ pthread_mutex_unlock(&chm.interval_100ms.list_mutex);
+ __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);
+ break;
+ case CHART_INTERVAL_1SEC:
+ pthread_mutex_lock(&chm.interval_1s.list_mutex);
+ newelem->next = chm.interval_1s.callback_list;
+ chm.interval_1s.callback_list = newelem;
+ pthread_mutex_unlock(&chm.interval_1s.list_mutex);
+ __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);
+ break;
+ default:
+ free(newelem);
+ break;
+ }
+}
+
+static void remove_all_callback_list()
+{
+ chart_interval_callback* cur;
+
+ pthread_mutex_lock(&chm.interval_10ms.list_mutex);
+ while(chm.interval_10ms.callback_list != NULL)
+ {
+ cur = chm.interval_10ms.callback_list;
+ chm.interval_10ms.callback_list = cur->next;
+ free(cur);
+ }
+ pthread_mutex_unlock(&chm.interval_10ms.list_mutex);
+
+ pthread_mutex_lock(&chm.interval_100ms.list_mutex);
+ while(chm.interval_100ms.callback_list != NULL)
+ {
+ cur = chm.interval_100ms.callback_list;
+ chm.interval_100ms.callback_list = cur->next;
+ free(cur);
+ }
+ pthread_mutex_unlock(&chm.interval_100ms.list_mutex);
+
+ pthread_mutex_lock(&chm.interval_1s.list_mutex);
+ while(chm.interval_1s.callback_list != NULL)
+ {
+ cur = chm.interval_1s.callback_list;
+ chm.interval_1s.callback_list = cur->next;
+ free(cur);
+ }
+ pthread_mutex_unlock(&chm.interval_1s.list_mutex);
+
+ memset(&chm.interval_for_series, 0, sizeof(chm.interval_for_series));
+ __sync_and_and_fetch(&(gTraceInfo.custom_chart_callback_count), 0);
+}
+
+static void remove_from_callback_list(da_handle charthandle, da_handle series_handle)
+{
+ chart_interval_callback *prev, *cur;
+ chart_interval interval;
+
+ interval = chm.interval_for_series[charthandle][series_handle % 10];
+ chm.interval_for_series[charthandle][series_handle % 10] = 0;
+
+ switch(interval)
+ {
+ case CHART_INTERVAL_10MSEC:
+ pthread_mutex_lock(&chm.interval_10ms.list_mutex);
+
+ prev = NULL;
+ cur = chm.interval_10ms.callback_list;
+ while(cur != NULL)
+ {
+ if(cur->chart_handle == charthandle && cur->series_handle == series_handle)
+ {
+ if(prev)
+ prev->next = cur->next;
+ else
+ chm.interval_10ms.callback_list = cur->next;
+
+ __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);
+ free(cur);
+ break;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+
+ pthread_mutex_unlock(&chm.interval_10ms.list_mutex);
+ break;
+ case CHART_INTERVAL_100MSEC:
+ pthread_mutex_lock(&chm.interval_100ms.list_mutex);
+
+ prev = NULL;
+ cur = chm.interval_100ms.callback_list;
+ while(cur != NULL)
+ {
+ if(cur->chart_handle == charthandle && cur->series_handle == series_handle)
+ {
+ if(prev)
+ prev->next = cur->next;
+ else
+ chm.interval_100ms.callback_list = cur->next;
+
+ __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);
+ free(cur);
+ break;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+
+ pthread_mutex_unlock(&chm.interval_100ms.list_mutex);
+ break;
+ case CHART_INTERVAL_1SEC:
+ pthread_mutex_lock(&chm.interval_1s.list_mutex);
+
+ prev = NULL;
+ cur = chm.interval_1s.callback_list;
+ while(cur != NULL)
+ {
+ if(cur->chart_handle == charthandle && cur->series_handle == series_handle)
+ {
+ if(prev)
+ prev->next = cur->next;
+ else
+ chm.interval_1s.callback_list = cur->next;
+
+ __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);
+ free(cur);
+ break;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+
+ pthread_mutex_unlock(&chm.interval_1s.list_mutex);
+ break;
+ default:
+ break;
+ }
+}
+
+
+// =====================================================================
+// constructor and destructor functions
+// =====================================================================
+
+void __attribute__((constructor)) _init_lib()
+{
+ probeBlockStart();
+
+ memset(&chm, 0, sizeof(chm));
+ chm.interval_10ms.timerfd = -1;
+ chm.interval_100ms.timerfd = -1;
+ chm.interval_1s.timerfd = -1;
+ chm.interval_10ms.thread_handle = -1;
+ chm.interval_100ms.thread_handle = -1;
+ chm.interval_1s.thread_handle = -1;
+ pthread_mutex_init(&chm.interval_10ms.list_mutex, NULL);
+ pthread_mutex_init(&chm.interval_100ms.list_mutex, NULL);
+ pthread_mutex_init(&chm.interval_1s.list_mutex, NULL);
+
+ probeBlockEnd();
+}
+
+void __attribute__((destructor)) _fini_lib()
+{
+ probeBlockStart();
+
+ remove_all_callback_list();
+ if(chm.interval_10ms.timerfd != -1)
+ close(chm.interval_10ms.timerfd);
+ if(chm.interval_100ms.timerfd != -1)
+ close(chm.interval_100ms.timerfd);
+ if(chm.interval_1s.timerfd != -1)
+ close(chm.interval_1s.timerfd);
+
+ if(chm.interval_10ms.thread_handle != -1)
+ {
+ pthread_join(chm.interval_10ms.thread_handle, NULL);
+ }
+ if(chm.interval_100ms.thread_handle != -1)
+ {
+ pthread_join(chm.interval_100ms.thread_handle, NULL);
+ }
+ if(chm.interval_1s.thread_handle != -1)
+ {
+ pthread_join(chm.interval_1s.thread_handle, NULL);
+ }
+
+ probeBlockEnd();
+}
+
+
+// =====================================================================
+// api definition
+// =====================================================================
+
+void da_mark(chart_color color, char* mark_text)
+{
+ DECLARE_CHART_VARIABLE;
+
+ probeBlockStart();
+
+ INIT_LOG;
+ setProbePoint(&probeInfo);
+ APPEND_LOG_BASIC(LC_CUSTOM);
+ APPEND_LOG_INPUT("%s", "");
+ APPEND_LOG_CHART_RESULT(0);
+ APPEND_LOG_CHART(0, 0, mark_text, color, 0.0f);
+ printLog(&log, MSG_LOG);
+
+ PREPARE_LOCAL_BUF();
+ PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dp", color, mark_text);
+ PACK_COMMON_END(0, 0, 2);
+ PACK_CUSTOM(0, 0, mark_text, color, 0.0f);
+ FLUSH_LOCAL_BUF();
+
+ probeBlockEnd();
+}
+
+da_handle da_create_chart(char* chart_name)
+{
+ DECLARE_CHART_VARIABLE;
+
+ // check if there is available chart handle slot
+ if(chm.chart_handle_index + 1 >= MAX_CHART_HANDLE)
+ return ERR_MAX_CHART_NUMBER;
+
+ // check if chart_name is null
+ if(chart_name == NULL)
+ return ERR_WRONG_PARAMETER;
+
+ probeBlockStart();
+ ret = ++(chm.chart_handle_index);
+
+ INIT_LOG;
+ setProbePoint(&probeInfo);
+ APPEND_LOG_BASIC(LC_CUSTOM);
+ APPEND_LOG_INPUT("%s", "");
+ APPEND_LOG_CHART_RESULT(ret);
+ APPEND_LOG_CHART(0, 0, chart_name, 0, 0.0f);
+ printLog(&log, MSG_LOG);
+
+ PREPARE_LOCAL_BUF();
+ PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "p", chart_name);
+ PACK_COMMON_END(ret, 0, 2);
+ PACK_CUSTOM(0, 0, chart_name, 0, 0.0f);
+ FLUSH_LOCAL_BUF();
+
+ probeBlockEnd();
+
+ return ret;
+}
+
+da_handle da_create_series(da_handle charthandle, char* seriesname,
+ series_type type, chart_color color)
+{
+ DECLARE_CHART_VARIABLE;
+
+ // check if charthandle is valid handle or not
+ if(charthandle <= 0 || charthandle > chm.chart_handle_index)
+ return ERR_WRONG_HANDLE;
+
+ // chech if extra parameter is valid
+ if(seriesname == NULL)
+ return ERR_WRONG_PARAMETER;
+
+ // check if there is available series spot
+ if(chm.series_handle_index[(int)charthandle] + 1 >= MAX_SERIES_PER_CHART)
+ return ERR_MAX_CHART_NUMBER;
+
+ probeBlockStart();
+ ret = ++(chm.series_handle_index[charthandle]);
+ ret += (10 * charthandle);
+
+ INIT_LOG;
+ setProbePoint(&probeInfo);
+ APPEND_LOG_BASIC(LC_CUSTOM);
+ APPEND_LOG_INPUT("%s", "");
+ APPEND_LOG_CHART_RESULT(ret);
+ APPEND_LOG_CHART(charthandle, type, seriesname, color, 0.0f);
+ printLog(&log, MSG_LOG);
+
+ PREPARE_LOCAL_BUF();
+ PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dpdd", charthandle, seriesname, type, color);
+ PACK_COMMON_END(ret, 0, 2);
+ PACK_CUSTOM(charthandle, type, seriesname, color, 0.0f);
+ FLUSH_LOCAL_BUF();
+
+ probeBlockEnd();
+
+ return ret;
+}
+
+da_handle da_create_default_series(da_handle charthandle, char* seriesname)
+{
+ return da_create_series(charthandle, seriesname,
+ CHART_TYPE_AUTO, CHART_COLOR_AUTO);
+}
+
+int da_set_callback(da_handle series_handle, da_user_data_2_chart_data callback,
+ void* data_addr, chart_interval interval)
+{
+ int cindex, sindex;
+ cindex = (int)(series_handle / 10);
+ sindex = series_handle % 10;
+
+ // check series handle
+ if(cindex <= 0 || cindex > chm.chart_handle_index)
+ return ERR_WRONG_HANDLE;
+
+ if(sindex > chm.series_handle_index[(int)cindex])
+ return ERR_WRONG_HANDLE;
+
+ // check rest parameters
+ if(interval == CHART_NO_CYCLE && callback != NULL)
+ return ERR_WRONG_PARAMETER;
+
+ probeBlockStart();
+
+ // remove previously registered callback
+ remove_from_callback_list(cindex, series_handle);
+
+ // register new callback
+ if(callback != NULL)
+ {
+ add_to_callback_list(interval, cindex, series_handle, callback, data_addr);
+ start_callback_thread(interval);
+ }
+ probeBlockEnd();
+
+ return 0;
+}
+
+void da_log(da_handle series_handle, float uservalue)
+{
+ DECLARE_CHART_VARIABLE;
+
+ // chech if series handle is valid
+ int cindex, sindex;
+ cindex = (int)(series_handle / 10);
+ sindex = series_handle % 10;
+
+ if(cindex <= 0 || cindex > chm.chart_handle_index)
+ return;
+
+ if(sindex > chm.series_handle_index[(int)cindex])
+ return;
+
+ probeBlockStart();
+
+ INIT_LOG;
+ setProbePoint(&probeInfo);
+ APPEND_LOG_BASIC(LC_CUSTOM);
+ APPEND_LOG_INPUT("%s", "");
+ APPEND_LOG_CHART_RESULT(0);
+ APPEND_LOG_CHART(series_handle, 0, "", 0, uservalue);
+ printLog(&log, MSG_LOG);
+
+ PREPARE_LOCAL_BUF();
+ PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dw", series_handle, uservalue);
+ PACK_COMMON_END(0, 0, 2);
+ PACK_CUSTOM(series_handle, 0, "", 0, uservalue);
+ FLUSH_LOCAL_BUF();
+
+ probeBlockEnd();
+}