/src/icu/source/common/utrace.cpp
Line | Count | Source (jump to first uncovered line) |
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 |
34 | 0 | utrace_entry(int32_t fnNumber) { |
35 | 0 | if (pTraceEntryFunc != NULL) { |
36 | 0 | (*pTraceEntryFunc)(gTraceContext, fnNumber); |
37 | 0 | } |
38 | 0 | } |
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 |
48 | 0 | utrace_exit(int32_t fnNumber, int32_t returnType, ...) { |
49 | 0 | if (pTraceExitFunc != NULL) { |
50 | 0 | va_list args; |
51 | 0 | const char *fmt; |
52 | |
|
53 | 0 | switch (returnType) { |
54 | 0 | case 0: |
55 | 0 | fmt = gExitFmt; |
56 | 0 | break; |
57 | 0 | case UTRACE_EXITV_I32: |
58 | 0 | fmt = gExitFmtValue; |
59 | 0 | break; |
60 | 0 | case UTRACE_EXITV_STATUS: |
61 | 0 | fmt = gExitFmtStatus; |
62 | 0 | break; |
63 | 0 | case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS: |
64 | 0 | fmt = gExitFmtValueStatus; |
65 | 0 | break; |
66 | 0 | case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS: |
67 | 0 | fmt = gExitFmtPtrStatus; |
68 | 0 | break; |
69 | 0 | default: |
70 | 0 | U_ASSERT(FALSE); |
71 | 0 | fmt = gExitFmt; |
72 | 0 | } |
73 | | |
74 | 0 | va_start(args, returnType); |
75 | 0 | (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args); |
76 | 0 | va_end(args); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | |
81 | | |
82 | | U_CAPI void U_EXPORT2 |
83 | 0 | utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) { |
84 | 0 | if (pTraceDataFunc != NULL) { |
85 | 0 | va_list args; |
86 | 0 | va_start(args, fmt ); |
87 | 0 | (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args); |
88 | 0 | va_end(args); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | | |
93 | 0 | static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { |
94 | 0 | 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 | 0 | if (*outIx==0 || /* case 1. */ |
105 | 0 | (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */ |
106 | 0 | (c=='\n' && *outIx>=capacity)) /* case 3 */ |
107 | 0 | { |
108 | | /* At the start of a line. Indent. */ |
109 | 0 | for(i=0; i<indent; i++) { |
110 | 0 | if (*outIx < capacity) { |
111 | 0 | outBuf[*outIx] = ' '; |
112 | 0 | } |
113 | 0 | (*outIx)++; |
114 | 0 | } |
115 | 0 | } |
116 | |
|
117 | 0 | if (*outIx < capacity) { |
118 | 0 | outBuf[*outIx] = c; |
119 | 0 | } |
120 | 0 | 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 | 0 | (*outIx)++; |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | | static void outputHexBytes(int64_t val, int32_t charsToOutput, |
129 | 0 | char *outBuf, int32_t *outIx, int32_t capacity) { |
130 | 0 | static const char gHexChars[] = "0123456789abcdef"; |
131 | 0 | int32_t shiftCount; |
132 | 0 | for (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) { |
133 | 0 | char c = gHexChars[(val >> shiftCount) & 0xf]; |
134 | 0 | outputChar(c, outBuf, outIx, capacity, 0); |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | /* Output a pointer value in hex. Work with any size of pointer */ |
139 | 0 | static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) { |
140 | 0 | uint32_t i; |
141 | 0 | int32_t incVal = 1; /* +1 for big endian, -1 for little endian */ |
142 | 0 | char *p = (char *)&val; /* point to current byte to output in the ptr val */ |
143 | |
|
144 | 0 | #if !U_IS_BIG_ENDIAN |
145 | | /* Little Endian. Move p to most significant end of the value */ |
146 | 0 | incVal = -1; |
147 | 0 | p += sizeof(void *) - 1; |
148 | 0 | #endif |
149 | | |
150 | | /* Loop through the bytes of the ptr as it sits in memory, from |
151 | | * most significant to least significant end */ |
152 | 0 | for (i=0; i<sizeof(void *); i++) { |
153 | 0 | outputHexBytes(*p, 2, outBuf, outIx, capacity); |
154 | 0 | p += incVal; |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | 0 | static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { |
159 | 0 | int32_t i = 0; |
160 | 0 | char c; |
161 | 0 | if (s==NULL) { |
162 | 0 | s = "*NULL*"; |
163 | 0 | } |
164 | 0 | do { |
165 | 0 | c = s[i++]; |
166 | 0 | outputChar(c, outBuf, outIx, capacity, indent); |
167 | 0 | } while (c != 0); |
168 | 0 | } |
169 | | |
170 | | |
171 | | |
172 | | static void outputUString(const UChar *s, int32_t len, |
173 | 0 | char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { |
174 | 0 | int32_t i = 0; |
175 | 0 | UChar c; |
176 | 0 | if (s==NULL) { |
177 | 0 | outputString(NULL, outBuf, outIx, capacity, indent); |
178 | 0 | return; |
179 | 0 | } |
180 | | |
181 | 0 | for (i=0; i<len || len==-1; i++) { |
182 | 0 | c = s[i]; |
183 | 0 | outputHexBytes(c, 4, outBuf, outIx, capacity); |
184 | 0 | outputChar(' ', outBuf, outIx, capacity, indent); |
185 | 0 | if (len == -1 && c==0) { |
186 | 0 | break; |
187 | 0 | } |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | U_CAPI int32_t U_EXPORT2 |
192 | 0 | utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) { |
193 | 0 | int32_t outIx = 0; |
194 | 0 | int32_t fmtIx = 0; |
195 | 0 | char fmtC; |
196 | 0 | char c; |
197 | 0 | int32_t intArg; |
198 | 0 | int64_t longArg = 0; |
199 | 0 | char *ptrArg; |
200 | | |
201 | | /* Loop runs once for each character in the format string. |
202 | | */ |
203 | 0 | for (;;) { |
204 | 0 | fmtC = fmt[fmtIx++]; |
205 | 0 | if (fmtC != '%') { |
206 | | /* Literal character, not part of a %sequence. Just copy it to the output. */ |
207 | 0 | outputChar(fmtC, outBuf, &outIx, capacity, indent); |
208 | 0 | 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 | 0 | break; |
214 | 0 | } |
215 | 0 | continue; |
216 | 0 | } |
217 | | |
218 | | /* We encountered a '%'. Pick up the following format char */ |
219 | 0 | fmtC = fmt[fmtIx++]; |
220 | |
|
221 | 0 | switch (fmtC) { |
222 | 0 | case 'c': |
223 | | /* single 8 bit char */ |
224 | 0 | c = (char)va_arg(args, int32_t); |
225 | 0 | outputChar(c, outBuf, &outIx, capacity, indent); |
226 | 0 | break; |
227 | | |
228 | 0 | case 's': |
229 | | /* char * string, null terminated. */ |
230 | 0 | ptrArg = va_arg(args, char *); |
231 | 0 | outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent); |
232 | 0 | break; |
233 | | |
234 | 0 | case 'S': |
235 | | /* UChar * string, with length, len==-1 for null terminated. */ |
236 | 0 | ptrArg = va_arg(args, char *); /* Ptr */ |
237 | 0 | intArg =(int32_t)va_arg(args, int32_t); /* Length */ |
238 | 0 | outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent); |
239 | 0 | break; |
240 | | |
241 | 0 | case 'b': |
242 | | /* 8 bit int */ |
243 | 0 | intArg = va_arg(args, int); |
244 | 0 | outputHexBytes(intArg, 2, outBuf, &outIx, capacity); |
245 | 0 | break; |
246 | | |
247 | 0 | case 'h': |
248 | | /* 16 bit int */ |
249 | 0 | intArg = va_arg(args, int); |
250 | 0 | outputHexBytes(intArg, 4, outBuf, &outIx, capacity); |
251 | 0 | break; |
252 | | |
253 | 0 | case 'd': |
254 | | /* 32 bit int */ |
255 | 0 | intArg = va_arg(args, int); |
256 | 0 | outputHexBytes(intArg, 8, outBuf, &outIx, capacity); |
257 | 0 | break; |
258 | | |
259 | 0 | case 'l': |
260 | | /* 64 bit long */ |
261 | 0 | longArg = va_arg(args, int64_t); |
262 | 0 | outputHexBytes(longArg, 16, outBuf, &outIx, capacity); |
263 | 0 | break; |
264 | | |
265 | 0 | case 'p': |
266 | | /* Pointers. */ |
267 | 0 | ptrArg = va_arg(args, char *); |
268 | 0 | outputPtrBytes(ptrArg, outBuf, &outIx, capacity); |
269 | 0 | break; |
270 | | |
271 | 0 | 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 | 0 | outputChar('%', outBuf, &outIx, capacity, indent); |
277 | 0 | fmtIx--; |
278 | 0 | break; |
279 | | |
280 | 0 | case 'v': |
281 | 0 | { |
282 | | /* Vector of values, e.g. %vh */ |
283 | 0 | char vectorType; |
284 | 0 | int32_t vectorLen; |
285 | 0 | const char *i8Ptr; |
286 | 0 | int16_t *i16Ptr; |
287 | 0 | int32_t *i32Ptr; |
288 | 0 | int64_t *i64Ptr; |
289 | 0 | void **ptrPtr; |
290 | 0 | int32_t charsToOutput = 0; |
291 | 0 | int32_t i; |
292 | | |
293 | 0 | vectorType = fmt[fmtIx]; /* b, h, d, l, p, etc. */ |
294 | 0 | if (vectorType != 0) { |
295 | 0 | fmtIx++; |
296 | 0 | } |
297 | 0 | i8Ptr = (const char *)va_arg(args, void*); |
298 | 0 | i16Ptr = (int16_t *)i8Ptr; |
299 | 0 | i32Ptr = (int32_t *)i8Ptr; |
300 | 0 | i64Ptr = (int64_t *)i8Ptr; |
301 | 0 | ptrPtr = (void **)i8Ptr; |
302 | 0 | vectorLen =(int32_t)va_arg(args, int32_t); |
303 | 0 | if (ptrPtr == NULL) { |
304 | 0 | outputString("*NULL* ", outBuf, &outIx, capacity, indent); |
305 | 0 | } else { |
306 | 0 | for (i=0; i<vectorLen || vectorLen==-1; i++) { |
307 | 0 | switch (vectorType) { |
308 | 0 | case 'b': |
309 | 0 | charsToOutput = 2; |
310 | 0 | longArg = *i8Ptr++; |
311 | 0 | break; |
312 | 0 | case 'h': |
313 | 0 | charsToOutput = 4; |
314 | 0 | longArg = *i16Ptr++; |
315 | 0 | break; |
316 | 0 | case 'd': |
317 | 0 | charsToOutput = 8; |
318 | 0 | longArg = *i32Ptr++; |
319 | 0 | break; |
320 | 0 | case 'l': |
321 | 0 | charsToOutput = 16; |
322 | 0 | longArg = *i64Ptr++; |
323 | 0 | break; |
324 | 0 | case 'p': |
325 | 0 | charsToOutput = 0; |
326 | 0 | outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity); |
327 | 0 | longArg = *ptrPtr==NULL? 0: 1; /* test for null terminated array. */ |
328 | 0 | ptrPtr++; |
329 | 0 | break; |
330 | 0 | case 'c': |
331 | 0 | charsToOutput = 0; |
332 | 0 | outputChar(*i8Ptr, outBuf, &outIx, capacity, indent); |
333 | 0 | longArg = *i8Ptr; /* for test for null terminated array. */ |
334 | 0 | i8Ptr++; |
335 | 0 | break; |
336 | 0 | case 's': |
337 | 0 | charsToOutput = 0; |
338 | 0 | outputString((const char *)*ptrPtr, outBuf, &outIx, capacity, indent); |
339 | 0 | outputChar('\n', outBuf, &outIx, capacity, indent); |
340 | 0 | longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */ |
341 | 0 | ptrPtr++; |
342 | 0 | break; |
343 | | |
344 | 0 | case 'S': |
345 | 0 | charsToOutput = 0; |
346 | 0 | outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent); |
347 | 0 | outputChar('\n', outBuf, &outIx, capacity, indent); |
348 | 0 | longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */ |
349 | 0 | ptrPtr++; |
350 | 0 | break; |
351 | | |
352 | | |
353 | 0 | } |
354 | 0 | if (charsToOutput > 0) { |
355 | 0 | outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity); |
356 | 0 | outputChar(' ', outBuf, &outIx, capacity, indent); |
357 | 0 | } |
358 | 0 | if (vectorLen == -1 && longArg == 0) { |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | } |
362 | 0 | } |
363 | 0 | outputChar('[', outBuf, &outIx, capacity, indent); |
364 | 0 | outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity); |
365 | 0 | outputChar(']', outBuf, &outIx, capacity, indent); |
366 | 0 | } |
367 | 0 | break; |
368 | | |
369 | | |
370 | 0 | 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 | 0 | outputChar(fmtC, outBuf, &outIx, capacity, indent); |
376 | 0 | } |
377 | 0 | } |
378 | 0 | outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is null terminated */ |
379 | 0 | return outIx + 1; /* outIx + 1 because outIx does not increment when outputing final null. */ |
380 | 0 | } |
381 | | |
382 | | |
383 | | |
384 | | |
385 | | U_CAPI int32_t U_EXPORT2 |
386 | | utrace_format(char *outBuf, int32_t capacity, |
387 | 0 | int32_t indent, const char *fmt, ...) { |
388 | 0 | int32_t retVal; |
389 | 0 | va_list args; |
390 | 0 | va_start(args, fmt ); |
391 | 0 | retVal = utrace_vformat(outBuf, capacity, indent, fmt, args); |
392 | 0 | va_end(args); |
393 | 0 | return retVal; |
394 | 0 | } |
395 | | |
396 | | |
397 | | U_CAPI void U_EXPORT2 |
398 | | utrace_setFunctions(const void *context, |
399 | 0 | UTraceEntry *e, UTraceExit *x, UTraceData *d) { |
400 | 0 | pTraceEntryFunc = e; |
401 | 0 | pTraceExitFunc = x; |
402 | 0 | pTraceDataFunc = d; |
403 | 0 | gTraceContext = context; |
404 | 0 | } |
405 | | |
406 | | |
407 | | U_CAPI void U_EXPORT2 |
408 | | utrace_getFunctions(const void **context, |
409 | 0 | UTraceEntry **e, UTraceExit **x, UTraceData **d) { |
410 | 0 | *e = pTraceEntryFunc; |
411 | 0 | *x = pTraceExitFunc; |
412 | 0 | *d = pTraceDataFunc; |
413 | 0 | *context = gTraceContext; |
414 | 0 | } |
415 | | |
416 | | U_CAPI void U_EXPORT2 |
417 | 0 | utrace_setLevel(int32_t level) { |
418 | 0 | if (level < UTRACE_OFF) { |
419 | 0 | level = UTRACE_OFF; |
420 | 0 | } |
421 | 0 | if (level > UTRACE_VERBOSE) { |
422 | 0 | level = UTRACE_VERBOSE; |
423 | 0 | } |
424 | 0 | utrace_level = level; |
425 | 0 | } |
426 | | |
427 | | U_CAPI int32_t U_EXPORT2 |
428 | 0 | utrace_getLevel() { |
429 | 0 | return utrace_level; |
430 | 0 | } |
431 | | |
432 | | |
433 | | U_CFUNC UBool |
434 | 1.43k | utrace_cleanup() { |
435 | 1.43k | pTraceEntryFunc = NULL; |
436 | 1.43k | pTraceExitFunc = NULL; |
437 | 1.43k | pTraceDataFunc = NULL; |
438 | 1.43k | utrace_level = UTRACE_OFF; |
439 | 1.43k | gTraceContext = NULL; |
440 | 1.43k | return TRUE; |
441 | 1.43k | } |
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 |
482 | 0 | utrace_functionName(int32_t fnNumber) { |
483 | 0 | if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) { |
484 | 0 | return trFnName[fnNumber]; |
485 | 0 | } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) { |
486 | 0 | return trConvNames[fnNumber - UTRACE_CONVERSION_START]; |
487 | 0 | } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){ |
488 | 0 | return trCollNames[fnNumber - UTRACE_COLLATION_START]; |
489 | 0 | } else { |
490 | 0 | return "[BOGUS Trace Function Number]"; |
491 | 0 | } |
492 | 0 | } |
493 | | |