be393208d7de5dc3f98b4bbdccbff8e2edc9fc30
[platform/core/system/swap-probe.git] / custom_chart / da_chart.c
1 /*\r
2  *  DA probe\r
3  *\r
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.\r
5  *\r
6  * Contact: \r
7  *\r
8  * Jaewon Lim <jaewon81.lim@samsung.com>\r
9  * Woojin Jung <woojin2.jung@samsung.com>\r
10  * Juyoung Kim <j0.kim@samsung.com>\r
11  * \r
12  * This library is free software; you can redistribute it and/or modify it under\r
13  * the terms of the GNU Lesser General Public License as published by the\r
14  * Free Software Foundation; either version 2.1 of the License, or (at your option)\r
15  * any later version.\r
16  * \r
17  * This library is distributed in the hope that it will be useful, but WITHOUT ANY\r
18  * WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
19  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public\r
20  * License for more details.\r
21  *\r
22  * You should have received a copy of the GNU Lesser General Public License\r
23  * along with this library; if not, write to the Free Software Foundation, Inc., 51\r
24  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
25  *\r
26  * Contributors:\r
27  * - S-Core Co., Ltd\r
28  * \r
29  */\r
30 \r
31 #include <pthread.h>\r
32 #include <signal.h>                     // for signal\r
33 #include <unistd.h>                     // for ualarm, sleep\r
34 #include <sys/timerfd.h>        // for timerfd\r
35 #include <stdio.h>\r
36 #include <errno.h>                      // for errno\r
37 #include <string.h>                     // for memset\r
38 #include <stdint.h>                     // for uint64_t\r
39 \r
40 #include "daprobe.h"\r
41 #include "dahelper.h"\r
42 #include "probeinfo.h"\r
43 #define _USE_DA_\r
44 #include "da_chart.h"\r
45 \r
46 #include "binproto.h"\r
47 \r
48 #define ERR_THREAD_CREATE_FAIL  -2001   // thread creation fail\r
49 \r
50 #define MAX_TITLE_LENGTH                16\r
51 #define MAX_CHART_HANDLE                10              // cannot be exceeded 10\r
52 #define MAX_SERIES_PER_CHART    4               // cannot be exceeded 10\r
53 // chart handle is  from 1 to 9\r
54 // series handle is from 11 to 99\r
55 // series handle 32 means that the series is 2nd series in 3rd chart.\r
56 // zero value handle means error\r
57 \r
58 // =====================================================================\r
59 // global variable\r
60 // =====================================================================\r
61 \r
62 typedef struct _chart_interval_callback\r
63 {\r
64         da_handle chart_handle;\r
65         da_handle series_handle;\r
66         da_user_data_2_chart_data callback;\r
67         void* user_data;\r
68         struct _chart_interval_callback* next;\r
69 } chart_interval_callback;\r
70 \r
71 typedef struct\r
72 {\r
73         int                     timerfd;\r
74         pthread_t       thread_handle;\r
75         chart_interval_callback*        callback_list;\r
76         pthread_mutex_t         list_mutex;\r
77 } interval_manager;\r
78 \r
79 typedef struct\r
80 {\r
81         int                     chart_handle_index;\r
82         int                     series_handle_index[MAX_CHART_HANDLE];\r
83         int                     interval_for_series[MAX_CHART_HANDLE][MAX_SERIES_PER_CHART];\r
84         interval_manager        interval_10ms;\r
85         interval_manager        interval_100ms;\r
86         interval_manager        interval_1s;\r
87 } chart_handle_maintainer;\r
88 \r
89 chart_handle_maintainer chm;\r
90 \r
91 __thread pid_t cur_thread = -1;\r
92 \r
93 \r
94 // =====================================================================\r
95 // internal macro definition\r
96 // =====================================================================\r
97 \r
98 #define DECLARE_CHART_VARIABLE                                  \\r
99         da_handle __attribute__((unused)) ret = 0;      \\r
100         probeInfo_t probeInfo;                                          \\r
101         log_t log\r
102 \r
103 #define APPEND_LOG_CHART_RESULT(RESULT)                                                                 \\r
104         log.length += sprintf(log.data + log.length, "`,%d`,0x%p`,0`,2",        \\r
105                         RESULT, CALLER_ADDRESS)\r
106 \r
107 #define APPEND_LOG_CHART(HANDLE, TYPE, TEXT, COLOR, VALUE)                                      \\r
108         log.length += sprintf(log.data + log.length, "`,`,%d`,%d`,%s`,%d`,%.3f",        \\r
109                         HANDLE, TYPE, TEXT, COLOR, VALUE)\r
110 \r
111 // =====================================================================\r
112 // timer thread routine for callback\r
113 // =====================================================================\r
114 \r
115 void* _chart_timerThread(void* data)\r
116 {\r
117         DECLARE_CHART_VARIABLE;\r
118 \r
119         uint64_t exp;\r
120         ssize_t readsize;\r
121         chart_interval_callback* cur;\r
122         float value;\r
123         sigset_t profsigmask;\r
124         interval_manager* pmanager = (interval_manager*)data;\r
125 \r
126         probeBlockStart();\r
127 \r
128         sigemptyset(&profsigmask);\r
129         sigaddset(&profsigmask, SIGPROF);\r
130         pthread_sigmask(SIG_BLOCK, &profsigmask, NULL);\r
131 \r
132         while((readsize = read(pmanager->timerfd, &exp, sizeof(uint64_t))) > 0)\r
133         {\r
134                 pthread_mutex_lock(&pmanager->list_mutex);\r
135 \r
136                 cur = pmanager->callback_list;\r
137                 while(cur != NULL)\r
138                 {\r
139                         value = cur->callback(cur->user_data);\r
140 \r
141                         INIT_LOG;\r
142                         setProbePoint(&probeInfo);\r
143                         log.length = sprintf(log.data, "%d`,%d`,%s`,%lu`,%d`,%d`,`,`,0x%p`,0`,2`,`,%d`,0`,`,0`,%.3f",\r
144                                         LC_CUSTOM, probeInfo.eventIndex, __func__, probeInfo.currentTime, probeInfo.pID,\r
145                                         probeInfo.tID, CALLER_ADDRESS, cur->series_handle, value);\r
146                         printLog(&log, MSG_LOG);\r
147 \r
148                         PREPARE_LOCAL_BUF();\r
149                         PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "", 0);\r
150                         PACK_COMMON_END(0, 0, 2);\r
151                         PACK_CUSTOM(cur->series_handle, 0, "", 0, value);\r
152                         FLUSH_LOCAL_BUF();\r
153 \r
154                         cur = cur->next;\r
155                 }\r
156 \r
157                 pthread_mutex_unlock(&pmanager->list_mutex);\r
158 \r
159                 sleep(0);\r
160         }\r
161 \r
162         probeBlockEnd();\r
163 \r
164         return NULL;\r
165 }\r
166 \r
167 static int start_callback_thread(chart_interval interval)\r
168 {\r
169         int* timerfd = NULL;\r
170         pthread_t* thread_handle = NULL;\r
171         interval_manager* pman = NULL;\r
172         struct itimerspec timevalue;\r
173 \r
174         if(interval == CHART_INTERVAL_10MSEC)\r
175         {\r
176                 timerfd = &(chm.interval_10ms.timerfd);\r
177                 thread_handle = &(chm.interval_10ms.thread_handle);\r
178                 pman = &(chm.interval_10ms);\r
179                 timevalue.it_value.tv_sec = 0;\r
180                 timevalue.it_value.tv_nsec = 10000000L;\r
181                 timevalue.it_interval.tv_sec = 0;\r
182                 timevalue.it_interval.tv_nsec = 10000000L;\r
183         }\r
184         else if(interval == CHART_INTERVAL_100MSEC)\r
185         {\r
186                 timerfd = &(chm.interval_100ms.timerfd);\r
187                 thread_handle = &(chm.interval_100ms.thread_handle);\r
188                 pman = &(chm.interval_100ms);\r
189                 timevalue.it_value.tv_sec = 0;\r
190                 timevalue.it_value.tv_nsec = 100000000L;\r
191                 timevalue.it_interval.tv_sec = 0;\r
192                 timevalue.it_interval.tv_nsec = 100000000L;\r
193         }\r
194         else if(interval == CHART_INTERVAL_1SEC)\r
195         {\r
196                 timerfd = &(chm.interval_1s.timerfd);\r
197                 thread_handle = &(chm.interval_1s.thread_handle);\r
198                 pman = &(chm.interval_1s);\r
199                 timevalue.it_value.tv_sec = 1;\r
200                 timevalue.it_value.tv_nsec = 0;\r
201                 timevalue.it_interval.tv_sec = 1;\r
202                 timevalue.it_interval.tv_nsec = 0;\r
203         }\r
204         else\r
205         {\r
206                 return ERR_WRONG_PARAMETER;\r
207         }\r
208 \r
209         if(*timerfd != -1 || *thread_handle != -1)\r
210                 return 0;               // already thread exist\r
211 \r
212         *timerfd = timerfd_create(CLOCK_REALTIME, 0);\r
213         if(*timerfd == -1)\r
214                 return errno;\r
215 \r
216         if(timerfd_settime(*timerfd, 0, &timevalue, NULL) == -1)\r
217                 return errno;\r
218 \r
219         if(pthread_create(thread_handle, NULL, _chart_timerThread, pman) < 0)\r
220                 return ERR_THREAD_CREATE_FAIL;\r
221 \r
222         return 0;\r
223 }\r
224 \r
225 \r
226 // =====================================================================\r
227 // internal utility function\r
228 // =====================================================================\r
229 \r
230 static void add_to_callback_list(chart_interval interval, da_handle charthandle, da_handle series_handle,\r
231                 da_user_data_2_chart_data callback, void* user_data)\r
232 {\r
233         chart_interval_callback* newelem;\r
234         \r
235         newelem = (chart_interval_callback*)malloc(sizeof(chart_interval_callback));\r
236         newelem->chart_handle = charthandle;\r
237         newelem->series_handle = series_handle;\r
238         newelem->callback = callback;\r
239         newelem->user_data = user_data;\r
240 \r
241         chm.interval_for_series[charthandle][series_handle % 10] = interval;\r
242         \r
243         switch(interval)\r
244         {\r
245         case CHART_INTERVAL_10MSEC:\r
246                 pthread_mutex_lock(&chm.interval_10ms.list_mutex);\r
247                 newelem->next = chm.interval_10ms.callback_list;\r
248                 chm.interval_10ms.callback_list = newelem;\r
249                 pthread_mutex_unlock(&chm.interval_10ms.list_mutex);\r
250                 __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
251                 break;\r
252         case CHART_INTERVAL_100MSEC:\r
253                 pthread_mutex_lock(&chm.interval_100ms.list_mutex);\r
254                 newelem->next = chm.interval_100ms.callback_list;\r
255                 chm.interval_100ms.callback_list = newelem;\r
256                 pthread_mutex_unlock(&chm.interval_100ms.list_mutex);\r
257                 __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
258                 break;\r
259         case CHART_INTERVAL_1SEC:\r
260                 pthread_mutex_lock(&chm.interval_1s.list_mutex);\r
261                 newelem->next = chm.interval_1s.callback_list;\r
262                 chm.interval_1s.callback_list = newelem;\r
263                 pthread_mutex_unlock(&chm.interval_1s.list_mutex);\r
264                 __sync_add_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
265                 break;\r
266         default:\r
267                 free(newelem);\r
268                 break;\r
269         }\r
270 }\r
271 \r
272 static void remove_all_callback_list()\r
273 {\r
274         chart_interval_callback* cur;\r
275         \r
276         pthread_mutex_lock(&chm.interval_10ms.list_mutex);\r
277         while(chm.interval_10ms.callback_list != NULL)\r
278         {\r
279                 cur = chm.interval_10ms.callback_list;\r
280                 chm.interval_10ms.callback_list = cur->next;\r
281                 free(cur);\r
282         }\r
283         pthread_mutex_unlock(&chm.interval_10ms.list_mutex);\r
284 \r
285         pthread_mutex_lock(&chm.interval_100ms.list_mutex);\r
286         while(chm.interval_100ms.callback_list != NULL)\r
287         {\r
288                 cur = chm.interval_100ms.callback_list;\r
289                 chm.interval_100ms.callback_list = cur->next;\r
290                 free(cur);\r
291         }\r
292         pthread_mutex_unlock(&chm.interval_100ms.list_mutex);\r
293 \r
294         pthread_mutex_lock(&chm.interval_1s.list_mutex);\r
295         while(chm.interval_1s.callback_list != NULL)\r
296         {\r
297                 cur = chm.interval_1s.callback_list;\r
298                 chm.interval_1s.callback_list = cur->next;\r
299                 free(cur);\r
300         }\r
301         pthread_mutex_unlock(&chm.interval_1s.list_mutex);\r
302 \r
303         memset(&chm.interval_for_series, 0, sizeof(chm.interval_for_series));\r
304         __sync_and_and_fetch(&(gTraceInfo.custom_chart_callback_count), 0);\r
305 }\r
306 \r
307 static void remove_from_callback_list(da_handle charthandle, da_handle series_handle)\r
308 {\r
309         chart_interval_callback *prev, *cur;\r
310         chart_interval interval;\r
311 \r
312         interval = chm.interval_for_series[charthandle][series_handle % 10];\r
313         chm.interval_for_series[charthandle][series_handle % 10] = 0;\r
314 \r
315         switch(interval)\r
316         {\r
317         case CHART_INTERVAL_10MSEC:\r
318                 pthread_mutex_lock(&chm.interval_10ms.list_mutex);\r
319 \r
320                 prev = NULL;\r
321                 cur = chm.interval_10ms.callback_list;\r
322                 while(cur != NULL)\r
323                 {\r
324                         if(cur->chart_handle == charthandle && cur->series_handle == series_handle)\r
325                         {\r
326                                 if(prev)\r
327                                         prev->next = cur->next;\r
328                                 else\r
329                                         chm.interval_10ms.callback_list = cur->next;\r
330 \r
331                                 __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
332                                 free(cur);\r
333                                 break;\r
334                         }\r
335 \r
336                         prev = cur;\r
337                         cur = cur->next;\r
338                 }\r
339 \r
340                 pthread_mutex_unlock(&chm.interval_10ms.list_mutex);\r
341                 break;\r
342         case CHART_INTERVAL_100MSEC:\r
343                 pthread_mutex_lock(&chm.interval_100ms.list_mutex);\r
344 \r
345                 prev = NULL;\r
346                 cur = chm.interval_100ms.callback_list;\r
347                 while(cur != NULL)\r
348                 {\r
349                         if(cur->chart_handle == charthandle && cur->series_handle == series_handle)\r
350                         {\r
351                                 if(prev)\r
352                                         prev->next = cur->next;\r
353                                 else\r
354                                         chm.interval_100ms.callback_list = cur->next;\r
355 \r
356                                 __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
357                                 free(cur);\r
358                                 break;\r
359                         }\r
360 \r
361                         prev = cur;\r
362                         cur = cur->next;\r
363                 }\r
364 \r
365                 pthread_mutex_unlock(&chm.interval_100ms.list_mutex);\r
366                 break;\r
367         case CHART_INTERVAL_1SEC:\r
368                 pthread_mutex_lock(&chm.interval_1s.list_mutex);\r
369 \r
370                 prev = NULL;\r
371                 cur = chm.interval_1s.callback_list;\r
372                 while(cur != NULL)\r
373                 {\r
374                         if(cur->chart_handle == charthandle && cur->series_handle == series_handle)\r
375                         {\r
376                                 if(prev)\r
377                                         prev->next = cur->next;\r
378                                 else\r
379                                         chm.interval_1s.callback_list = cur->next;\r
380 \r
381                                 __sync_sub_and_fetch(&(gTraceInfo.custom_chart_callback_count), 1);\r
382                                 free(cur);\r
383                                 break;\r
384                         }\r
385 \r
386                         prev = cur;\r
387                         cur = cur->next;\r
388                 }\r
389 \r
390                 pthread_mutex_unlock(&chm.interval_1s.list_mutex);\r
391                 break;\r
392         default:\r
393                 break;\r
394         }\r
395 }\r
396 \r
397 \r
398 // =====================================================================\r
399 // constructor and destructor functions\r
400 // =====================================================================\r
401 \r
402 void __attribute__((constructor)) _init_lib()\r
403 {\r
404         probeBlockStart();\r
405 \r
406         memset(&chm, 0, sizeof(chm));\r
407         chm.interval_10ms.timerfd = -1;\r
408         chm.interval_100ms.timerfd = -1;\r
409         chm.interval_1s.timerfd = -1;\r
410         chm.interval_10ms.thread_handle = -1;\r
411         chm.interval_100ms.thread_handle = -1;\r
412         chm.interval_1s.thread_handle = -1;\r
413         pthread_mutex_init(&chm.interval_10ms.list_mutex, NULL);\r
414         pthread_mutex_init(&chm.interval_100ms.list_mutex, NULL);\r
415         pthread_mutex_init(&chm.interval_1s.list_mutex, NULL);\r
416 \r
417         probeBlockEnd();\r
418 }\r
419 \r
420 void __attribute__((destructor)) _fini_lib()\r
421 {\r
422         probeBlockStart();\r
423 \r
424         remove_all_callback_list();\r
425         if(chm.interval_10ms.timerfd != -1)\r
426                 close(chm.interval_10ms.timerfd);\r
427         if(chm.interval_100ms.timerfd != -1)\r
428                 close(chm.interval_100ms.timerfd);\r
429         if(chm.interval_1s.timerfd != -1)\r
430                 close(chm.interval_1s.timerfd);\r
431 \r
432         if(chm.interval_10ms.thread_handle != -1)\r
433         {\r
434                 pthread_join(chm.interval_10ms.thread_handle, NULL);\r
435         }\r
436         if(chm.interval_100ms.thread_handle != -1)\r
437         {\r
438                 pthread_join(chm.interval_100ms.thread_handle, NULL);\r
439         }\r
440         if(chm.interval_1s.thread_handle != -1)\r
441         {\r
442                 pthread_join(chm.interval_1s.thread_handle, NULL);\r
443         }\r
444 \r
445         probeBlockEnd();\r
446 }\r
447 \r
448 \r
449 // =====================================================================\r
450 // api definition\r
451 // =====================================================================\r
452 \r
453 void da_mark(chart_color color, char* mark_text)\r
454 {\r
455         DECLARE_CHART_VARIABLE;\r
456         \r
457         probeBlockStart();\r
458         \r
459         INIT_LOG;\r
460         setProbePoint(&probeInfo);\r
461         APPEND_LOG_BASIC(LC_CUSTOM);\r
462         APPEND_LOG_INPUT("%s", "");\r
463         APPEND_LOG_CHART_RESULT(0);\r
464         APPEND_LOG_CHART(0, 0, mark_text, color, 0.0f);\r
465         printLog(&log, MSG_LOG);\r
466 \r
467         PREPARE_LOCAL_BUF();\r
468         PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dp", color, mark_text);\r
469         PACK_COMMON_END(0, 0, 2);\r
470         PACK_CUSTOM(0, 0, mark_text, color, 0.0f);\r
471         FLUSH_LOCAL_BUF();\r
472 \r
473         probeBlockEnd();\r
474 }\r
475 \r
476 da_handle da_create_chart(char* chart_name)\r
477 {\r
478         DECLARE_CHART_VARIABLE;\r
479 \r
480         // check if there is available chart handle slot\r
481         if(chm.chart_handle_index + 1 >= MAX_CHART_HANDLE)\r
482                 return ERR_MAX_CHART_NUMBER;\r
483                 \r
484         // check if chart_name is null\r
485         if(chart_name == NULL)\r
486                 return ERR_WRONG_PARAMETER;\r
487                 \r
488         probeBlockStart();\r
489         ret = ++(chm.chart_handle_index);\r
490 \r
491         INIT_LOG;\r
492         setProbePoint(&probeInfo);\r
493         APPEND_LOG_BASIC(LC_CUSTOM);\r
494         APPEND_LOG_INPUT("%s", "");\r
495         APPEND_LOG_CHART_RESULT(ret);\r
496         APPEND_LOG_CHART(0, 0, chart_name, 0, 0.0f);\r
497         printLog(&log, MSG_LOG);\r
498 \r
499         PREPARE_LOCAL_BUF();\r
500         PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "p", chart_name);\r
501         PACK_COMMON_END(ret, 0, 2);\r
502         PACK_CUSTOM(0, 0, chart_name, 0, 0.0f);\r
503         FLUSH_LOCAL_BUF();\r
504         \r
505         probeBlockEnd();\r
506         \r
507         return ret;\r
508 }\r
509 \r
510 da_handle da_create_series(da_handle charthandle, char* seriesname,\r
511                 series_type type, chart_color color)\r
512 {\r
513         DECLARE_CHART_VARIABLE;\r
514         \r
515         // check if charthandle is valid handle or not\r
516         if(charthandle <= 0 || charthandle > chm.chart_handle_index)\r
517                 return ERR_WRONG_HANDLE;\r
518                 \r
519         // chech if extra parameter is valid\r
520         if(seriesname == NULL)\r
521                 return ERR_WRONG_PARAMETER;\r
522 \r
523         // check if there is available series spot\r
524         if(chm.series_handle_index[(int)charthandle] + 1 >= MAX_SERIES_PER_CHART)\r
525                 return ERR_MAX_CHART_NUMBER;\r
526         \r
527         probeBlockStart();\r
528         ret = ++(chm.series_handle_index[charthandle]);\r
529         ret += (10 * charthandle);\r
530         \r
531         INIT_LOG;\r
532         setProbePoint(&probeInfo);\r
533         APPEND_LOG_BASIC(LC_CUSTOM);\r
534         APPEND_LOG_INPUT("%s", "");\r
535         APPEND_LOG_CHART_RESULT(ret);\r
536         APPEND_LOG_CHART(charthandle, type, seriesname, color, 0.0f);\r
537         printLog(&log, MSG_LOG);\r
538 \r
539         PREPARE_LOCAL_BUF();\r
540         PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dpdd",  charthandle, seriesname, type, color);\r
541         PACK_COMMON_END(ret, 0, 2);\r
542         PACK_CUSTOM(charthandle, type, seriesname, color, 0.0f);\r
543         FLUSH_LOCAL_BUF();\r
544 \r
545         probeBlockEnd();\r
546         \r
547         return ret;\r
548 }\r
549                 \r
550 da_handle da_create_default_series(da_handle charthandle, char* seriesname)\r
551 {\r
552         return da_create_series(charthandle, seriesname,\r
553                         CHART_TYPE_AUTO, CHART_COLOR_AUTO);\r
554 }\r
555 \r
556 int da_set_callback(da_handle series_handle, da_user_data_2_chart_data callback,\r
557                 void* data_addr, chart_interval interval)\r
558 {\r
559         int cindex, sindex;\r
560         cindex = (int)(series_handle / 10);\r
561         sindex = series_handle % 10;\r
562         \r
563         // check series handle\r
564         if(cindex <= 0 || cindex > chm.chart_handle_index)\r
565                 return ERR_WRONG_HANDLE;\r
566         \r
567         if(sindex > chm.series_handle_index[(int)cindex])\r
568                 return ERR_WRONG_HANDLE;\r
569         \r
570         // check rest parameters\r
571         if(interval == CHART_NO_CYCLE && callback != NULL)\r
572                 return ERR_WRONG_PARAMETER;\r
573 \r
574         probeBlockStart();\r
575 \r
576         // remove previously registered callback\r
577         remove_from_callback_list(cindex, series_handle);\r
578 \r
579         // register new callback\r
580         if(callback != NULL)\r
581         {\r
582                 add_to_callback_list(interval, cindex, series_handle, callback, data_addr);\r
583                 start_callback_thread(interval);\r
584         }\r
585         probeBlockEnd();\r
586 \r
587         return 0;\r
588 }\r
589 \r
590 void da_log(da_handle series_handle, float uservalue)\r
591 {\r
592         DECLARE_CHART_VARIABLE;\r
593         \r
594         // chech if series handle is valid\r
595         int cindex, sindex;\r
596         cindex = (int)(series_handle / 10);\r
597         sindex = series_handle % 10;\r
598         \r
599         if(cindex <= 0 || cindex > chm.chart_handle_index)\r
600                 return;\r
601         \r
602         if(sindex > chm.series_handle_index[(int)cindex])\r
603                 return;\r
604         \r
605         probeBlockStart();\r
606         \r
607         INIT_LOG;\r
608         setProbePoint(&probeInfo);\r
609         APPEND_LOG_BASIC(LC_CUSTOM);\r
610         APPEND_LOG_INPUT("%s", "");\r
611         APPEND_LOG_CHART_RESULT(0);\r
612         APPEND_LOG_CHART(series_handle, 0, "", 0, uservalue);\r
613         printLog(&log, MSG_LOG);\r
614 \r
615         PREPARE_LOCAL_BUF();\r
616         PACK_COMMON_BEGIN(MSG_PROBE_CUSTOM, LC_CUSTOM, "dw", series_handle, uservalue);\r
617         PACK_COMMON_END(0, 0, 2);\r
618         PACK_CUSTOM(series_handle, 0, "", 0, uservalue);\r
619         FLUSH_LOCAL_BUF();\r
620         \r
621         probeBlockEnd();\r
622 }\r