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