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