• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
utrace_entry(int32_t fnNumber)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
utrace_exit(int32_t fnNumber,int32_t returnType,...)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
utrace_data(int32_t fnNumber,int32_t level,const char * fmt,...)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 
outputChar(char c,char * outBuf,int32_t * outIx,int32_t capacity,int32_t indent)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 
outputHexBytes(int64_t val,int32_t charsToOutput,char * outBuf,int32_t * outIx,int32_t capacity)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   */
outputPtrBytes(void * val,char * outBuf,int32_t * outIx,int32_t capacity)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 
outputString(const char * s,char * outBuf,int32_t * outIx,int32_t capacity,int32_t indent)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 
outputUString(const UChar * s,int32_t len,char * outBuf,int32_t * outIx,int32_t capacity,int32_t indent)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
utrace_vformat(char * outBuf,int32_t capacity,int32_t indent,const char * fmt,va_list args)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
utrace_format(char * outBuf,int32_t capacity,int32_t indent,const char * fmt,...)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
utrace_setFunctions(const void * context,UTraceEntry * e,UTraceExit * x,UTraceData * d)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
utrace_getFunctions(const void ** context,UTraceEntry ** e,UTraceExit ** x,UTraceData ** d)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
utrace_setLevel(int32_t level)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
utrace_getLevel()425 utrace_getLevel() {
426     return utrace_level;
427 }
428 
429 
430 U_CFUNC UBool
utrace_cleanup()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
utrace_functionName(int32_t fnNumber)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 
491