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