Imported Upstream version 58.1
[platform/upstream/icu.git] / source / common / utrace.c
1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *   Copyright (C) 2003-2014, International Business Machines
6 *   Corporation and others.  All Rights Reserved.
7 *******************************************************************************
8 *   file name:  utrace.c
9 *   encoding:   US-ASCII
10 *   tab size:   8 (not used)
11 *   indentation:4
12 */
13
14 #define   UTRACE_IMPL
15 #include "unicode/utrace.h"
16 #include "utracimp.h"
17 #include "cstring.h"
18 #include "uassert.h"
19 #include "ucln_cmn.h"
20
21
22 static UTraceEntry     *pTraceEntryFunc = NULL;
23 static UTraceExit      *pTraceExitFunc  = NULL;
24 static UTraceData      *pTraceDataFunc  = NULL;
25 static const void      *gTraceContext   = NULL;
26
27 U_EXPORT int32_t
28 utrace_level = UTRACE_ERROR;
29
30 U_CAPI void U_EXPORT2
31 utrace_entry(int32_t fnNumber) {
32     if (pTraceEntryFunc != NULL) {
33         (*pTraceEntryFunc)(gTraceContext, fnNumber);
34     }
35 }
36
37
38 static const char gExitFmt[]             = "Returns.";
39 static const char gExitFmtValue[]        = "Returns %d.";
40 static const char gExitFmtStatus[]       = "Returns.  Status = %d.";
41 static const char gExitFmtValueStatus[]  = "Returns %d.  Status = %d.";
42 static const char gExitFmtPtrStatus[]    = "Returns %d.  Status = %p.";
43
44 U_CAPI void U_EXPORT2
45 utrace_exit(int32_t fnNumber, int32_t returnType, ...) {
46     if (pTraceExitFunc != NULL) {
47         va_list     args;
48         const char *fmt;
49
50         switch (returnType) {
51         case 0:
52             fmt = gExitFmt;
53             break;
54         case UTRACE_EXITV_I32:
55             fmt = gExitFmtValue;
56             break;
57         case UTRACE_EXITV_STATUS:
58             fmt = gExitFmtStatus;
59             break;
60         case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS:
61             fmt = gExitFmtValueStatus;
62             break;
63         case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS:
64             fmt = gExitFmtPtrStatus;
65             break;
66         default:
67             U_ASSERT(FALSE);
68             fmt = gExitFmt;
69         }
70
71         va_start(args, returnType);
72         (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args);
73         va_end(args);
74     }
75 }
76  
77
78  
79 U_CAPI void U_EXPORT2 
80 utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) {
81     if (pTraceDataFunc != NULL) {
82            va_list args;
83            va_start(args, fmt ); 
84            (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args);
85            va_end(args);
86     }
87 }
88
89
90 static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
91     int32_t i;
92     /* Check whether a start of line indenting is needed.  Three cases:
93      *   1.  At the start of the first line  (output index == 0).
94      *   2.  At the start of subsequent lines  (preceeding char in buffer == '\n')
95      *   3.  When preflighting buffer len (buffer capacity is exceeded), when
96      *       a \n is output.  Ideally we wouldn't do the indent until the following char
97      *       is received, but that won't work because there's no place to remember that
98      *       the preceding char was \n.  Meaning that we may overstimate the
99      *       buffer size needed.  No harm done.
100      */
101     if (*outIx==0 ||   /* case 1. */
102         (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') ||  /* case 2. */
103         (c=='\n' && *outIx>=capacity))    /* case 3 */
104     {
105         /* At the start of a line.  Indent. */
106         for(i=0; i<indent; i++) {
107             if (*outIx < capacity) {
108                 outBuf[*outIx] = ' ';
109             }
110             (*outIx)++;
111         }
112     }
113
114     if (*outIx < capacity) {
115         outBuf[*outIx] = c;
116     }
117     if (c != 0) {
118         /* Nulls only appear as end-of-string terminators.  Move them to the output
119          *  buffer, but do not update the length of the buffer, so that any
120          *  following output will overwrite the null. */
121         (*outIx)++;
122     }
123 }
124
125 static void outputHexBytes(int64_t val, int32_t charsToOutput,
126                            char *outBuf, int32_t *outIx, int32_t capacity) {
127     static const char gHexChars[] = "0123456789abcdef";
128     int32_t shiftCount;
129     for  (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) {
130         char c = gHexChars[(val >> shiftCount) & 0xf];
131         outputChar(c, outBuf, outIx, capacity, 0);
132     }
133 }
134
135 /* Output a pointer value in hex.  Work with any size of pointer   */
136 static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) {
137     int32_t  i;
138     int32_t  incVal = 1;              /* +1 for big endian, -1 for little endian          */
139     char     *p     = (char *)&val;   /* point to current byte to output in the ptr val  */
140
141 #if !U_IS_BIG_ENDIAN
142     /* Little Endian.  Move p to most significant end of the value      */
143     incVal = -1;
144     p += sizeof(void *) - 1;
145 #endif
146
147     /* Loop through the bytes of the ptr as it sits in memory, from 
148      * most significant to least significant end                    */
149     for (i=0; i<sizeof(void *); i++) {
150         outputHexBytes(*p, 2, outBuf, outIx, capacity);
151         p += incVal;
152     }
153 }
154
155 static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
156     int32_t i = 0;
157     char    c;
158     if (s==NULL) {
159         s = "*NULL*";
160     }
161     do {
162         c = s[i++];
163         outputChar(c, outBuf, outIx, capacity, indent);
164     } while (c != 0);
165 }
166         
167
168
169 static void outputUString(const UChar *s, int32_t len, 
170                           char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
171     int32_t i = 0;
172     UChar   c;
173     if (s==NULL) {
174         outputString(NULL, outBuf, outIx, capacity, indent);
175         return;
176     }
177
178     for (i=0; i<len || len==-1; i++) {
179         c = s[i];
180         outputHexBytes(c, 4, outBuf, outIx, capacity);
181         outputChar(' ', outBuf, outIx, capacity, indent);
182         if (len == -1 && c==0) {
183             break;
184         }
185     }
186 }
187         
188 U_CAPI int32_t U_EXPORT2
189 utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) {
190     int32_t   outIx  = 0;
191     int32_t   fmtIx  = 0;
192     char      fmtC;
193     char      c;
194     int32_t   intArg;
195     int64_t   longArg = 0;
196     char      *ptrArg;
197
198     /*   Loop runs once for each character in the format string.
199      */
200     for (;;) {
201         fmtC = fmt[fmtIx++];
202         if (fmtC != '%') {
203             /* Literal character, not part of a %sequence.  Just copy it to the output. */
204             outputChar(fmtC, outBuf, &outIx, capacity, indent);
205             if (fmtC == 0) {
206                 /* We hit the null that terminates the format string.
207                  * This is the normal (and only) exit from the loop that
208                  * interprets the format
209                  */
210                 break;
211             }
212             continue;
213         }
214
215         /* We encountered a '%'.  Pick up the following format char */
216         fmtC = fmt[fmtIx++];
217
218         switch (fmtC) {
219         case 'c':
220             /* single 8 bit char   */
221             c = (char)va_arg(args, int32_t);
222             outputChar(c, outBuf, &outIx, capacity, indent);
223             break;
224
225         case 's':
226             /* char * string, null terminated.  */
227             ptrArg = va_arg(args, char *);
228             outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent);
229             break;
230
231         case 'S':
232             /* UChar * string, with length, len==-1 for null terminated. */
233             ptrArg = va_arg(args, void *);             /* Ptr    */
234             intArg =(int32_t)va_arg(args, int32_t);    /* Length */
235             outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent);
236             break;
237
238         case 'b':
239             /*  8 bit int  */
240             intArg = va_arg(args, int);
241             outputHexBytes(intArg, 2, outBuf, &outIx, capacity);
242             break;
243
244         case 'h':
245             /*  16 bit int  */
246             intArg = va_arg(args, int);
247             outputHexBytes(intArg, 4, outBuf, &outIx, capacity);
248             break;
249
250         case 'd':
251             /*  32 bit int  */
252             intArg = va_arg(args, int);
253             outputHexBytes(intArg, 8, outBuf, &outIx, capacity);
254             break;
255
256         case 'l':
257             /*  64 bit long  */
258             longArg = va_arg(args, int64_t);
259             outputHexBytes(longArg, 16, outBuf, &outIx, capacity);
260             break;
261             
262         case 'p':
263             /*  Pointers.   */
264             ptrArg = va_arg(args, void *);
265             outputPtrBytes(ptrArg, outBuf, &outIx, capacity);
266             break;
267
268         case 0:
269             /* Single '%' at end of fmt string.  Output as literal '%'.   
270              * Back up index into format string so that the terminating null will be
271              * re-fetched in the outer loop, causing it to terminate.
272              */
273             outputChar('%', outBuf, &outIx, capacity, indent);
274             fmtIx--;
275             break;
276
277         case 'v':
278             {
279                 /* Vector of values, e.g. %vh */
280                 char     vectorType;
281                 int32_t  vectorLen;
282                 const char   *i8Ptr;
283                 int16_t  *i16Ptr;
284                 int32_t  *i32Ptr;
285                 int64_t  *i64Ptr;
286                 void     **ptrPtr;
287                 int32_t   charsToOutput = 0;
288                 int32_t   i;
289                 
290                 vectorType = fmt[fmtIx];    /* b, h, d, l, p, etc. */
291                 if (vectorType != 0) {
292                     fmtIx++;
293                 }
294                 i8Ptr = (const char *)va_arg(args, void*);
295                 i16Ptr = (int16_t *)i8Ptr;
296                 i32Ptr = (int32_t *)i8Ptr;
297                 i64Ptr = (int64_t *)i8Ptr;
298                 ptrPtr = (void **)i8Ptr;
299                 vectorLen =(int32_t)va_arg(args, int32_t);
300                 if (ptrPtr == NULL) {
301                     outputString("*NULL* ", outBuf, &outIx, capacity, indent);
302                 } else {
303                     for (i=0; i<vectorLen || vectorLen==-1; i++) { 
304                         switch (vectorType) {
305                         case 'b':
306                             charsToOutput = 2;
307                             longArg = *i8Ptr++;
308                             break;
309                         case 'h':
310                             charsToOutput = 4;
311                             longArg = *i16Ptr++;
312                             break;
313                         case 'd':
314                             charsToOutput = 8;
315                             longArg = *i32Ptr++;
316                             break;
317                         case 'l':
318                             charsToOutput = 16;
319                             longArg = *i64Ptr++;
320                             break;
321                         case 'p':
322                             charsToOutput = 0;
323                             outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity);
324                             longArg = *ptrPtr==NULL? 0: 1;    /* test for null terminated array. */
325                             ptrPtr++;
326                             break;
327                         case 'c':
328                             charsToOutput = 0;
329                             outputChar(*i8Ptr, outBuf, &outIx, capacity, indent);
330                             longArg = *i8Ptr;    /* for test for null terminated array. */
331                             i8Ptr++;
332                             break;
333                         case 's':
334                             charsToOutput = 0;
335                             outputString(*ptrPtr, outBuf, &outIx, capacity, indent);
336                             outputChar('\n', outBuf, &outIx, capacity, indent);
337                             longArg = *ptrPtr==NULL? 0: 1;   /* for test for null term. array. */
338                             ptrPtr++;
339                             break;
340
341                         case 'S':
342                             charsToOutput = 0;
343                             outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent);
344                             outputChar('\n', outBuf, &outIx, capacity, indent);
345                             longArg = *ptrPtr==NULL? 0: 1;   /* for test for null term. array. */
346                             ptrPtr++;
347                             break;
348
349                             
350                         }
351                         if (charsToOutput > 0) {
352                             outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity);
353                             outputChar(' ', outBuf, &outIx, capacity, indent);
354                         }
355                         if (vectorLen == -1 && longArg == 0) {
356                             break;
357                         }
358                     }
359                 }
360                 outputChar('[', outBuf, &outIx, capacity, indent);
361                 outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity);
362                 outputChar(']', outBuf, &outIx, capacity, indent);
363             }
364             break;
365
366
367         default:
368             /* %. in format string, where . is some character not in the set
369              *    of recognized format chars.  Just output it as if % wasn't there.
370              *    (Covers "%%" outputing a single '%')
371              */
372              outputChar(fmtC, outBuf, &outIx, capacity, indent);
373         }
374     }
375     outputChar(0, outBuf, &outIx, capacity, indent);  /* Make sure that output is null terminated  */
376     return outIx + 1;     /* outIx + 1 because outIx does not increment when outputing final null. */
377 }
378
379
380
381
382 U_CAPI int32_t U_EXPORT2
383 utrace_format(char *outBuf, int32_t capacity,
384                 int32_t indent, const char *fmt,  ...) {
385     int32_t retVal;
386     va_list args;
387     va_start(args, fmt ); 
388     retVal = utrace_vformat(outBuf, capacity, indent, fmt, args);
389     va_end(args);
390     return retVal;
391 }
392
393
394 U_CAPI void U_EXPORT2
395 utrace_setFunctions(const void *context,
396                     UTraceEntry *e, UTraceExit *x, UTraceData *d) {
397     pTraceEntryFunc = e;
398     pTraceExitFunc  = x;
399     pTraceDataFunc  = d;
400     gTraceContext   = context;
401 }
402
403
404 U_CAPI void U_EXPORT2
405 utrace_getFunctions(const void **context,
406                     UTraceEntry **e, UTraceExit **x, UTraceData **d) {
407     *e = pTraceEntryFunc;
408     *x = pTraceExitFunc;
409     *d = pTraceDataFunc;
410     *context = gTraceContext;
411 }
412
413 U_CAPI void U_EXPORT2
414 utrace_setLevel(int32_t level) {
415     if (level < UTRACE_OFF) {
416         level = UTRACE_OFF;
417     }
418     if (level > UTRACE_VERBOSE) {
419         level = UTRACE_VERBOSE;
420     }
421     utrace_level = level;
422 }
423
424 U_CAPI int32_t U_EXPORT2
425 utrace_getLevel() {
426     return utrace_level;
427 }
428
429
430 U_CFUNC UBool 
431 utrace_cleanup() {
432     pTraceEntryFunc = NULL;
433     pTraceExitFunc  = NULL;
434     pTraceDataFunc  = NULL;
435     utrace_level    = UTRACE_OFF;
436     gTraceContext   = NULL;
437     return TRUE;
438 }
439
440
441 static const char * const
442 trFnName[] = {
443     "u_init",
444     "u_cleanup",
445     NULL
446 };
447
448
449 static const char * const
450 trConvNames[] = {
451     "ucnv_open",
452     "ucnv_openPackage",
453     "ucnv_openAlgorithmic",
454     "ucnv_clone",
455     "ucnv_close",
456     "ucnv_flushCache",
457     "ucnv_load",
458     "ucnv_unload",
459     NULL
460 };
461
462     
463 static const char * const
464 trCollNames[] = {
465     "ucol_open",
466     "ucol_close",
467     "ucol_strcoll",
468     "ucol_getSortKey",
469     "ucol_getLocale",
470     "ucol_nextSortKeyPart",
471     "ucol_strcollIter",
472     "ucol_openFromShortString",
473     "ucol_strcollUTF8",
474     NULL
475 };
476
477                 
478 U_CAPI const char * U_EXPORT2
479 utrace_functionName(int32_t fnNumber) {
480     if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) {
481         return trFnName[fnNumber];
482     } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) {
483         return trConvNames[fnNumber - UTRACE_CONVERSION_START];
484     } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){
485         return trCollNames[fnNumber - UTRACE_COLLATION_START];
486     } else {
487         return "[BOGUS Trace Function Number]";
488     }
489 }
490