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