]>
Commit | Line | Data |
---|---|---|
f3c0d7a5 A |
1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
374ca955 A |
3 | /* |
4 | ******************************************************************************* | |
57a6839d | 5 | * Copyright (C) 2003-2014, International Business Machines |
374ca955 A |
6 | * Corporation and others. All Rights Reserved. |
7 | ******************************************************************************* | |
8 | * file name: utrace.c | |
f3c0d7a5 | 9 | * encoding: UTF-8 |
374ca955 A |
10 | * tab size: 8 (not used) |
11 | * indentation:4 | |
12 | */ | |
13 | ||
374ca955 A |
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 | ||
f3c0d7a5 A |
26 | /** |
27 | * \var utrace_level | |
28 | * Trace level variable. Negative for "off". | |
29 | */ | |
30 | static int32_t | |
46f4442e | 31 | utrace_level = UTRACE_ERROR; |
374ca955 A |
32 | |
33 | U_CAPI void U_EXPORT2 | |
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 | |
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 | |
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 | ||
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 | ||
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 */ | |
139 | static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) { | |
f3c0d7a5 | 140 | uint32_t i; |
374ca955 A |
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 | ||
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 | ||
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 | |
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. */ | |
f3c0d7a5 | 236 | ptrArg = va_arg(args, char *); /* Ptr */ |
374ca955 | 237 | intArg =(int32_t)va_arg(args, int32_t); /* Length */ |
73c04bcf | 238 | outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent); |
374ca955 A |
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. */ | |
f3c0d7a5 | 267 | ptrArg = va_arg(args, char *); |
374ca955 A |
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; | |
f3c0d7a5 | 338 | outputString((const char *)*ptrPtr, outBuf, &outIx, capacity, indent); |
374ca955 A |
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; | |
73c04bcf | 346 | outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent); |
374ca955 A |
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 | |
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 | |
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 | |
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 | |
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 | |
428 | utrace_getLevel() { | |
429 | return utrace_level; | |
430 | } | |
431 | ||
432 | ||
433 | U_CFUNC UBool | |
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", | |
57a6839d A |
475 | "ucol_openFromShortString", |
476 | "ucol_strcollUTF8", | |
374ca955 A |
477 | NULL |
478 | }; | |
479 | ||
480 | ||
481 | U_CAPI const char * U_EXPORT2 | |
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 |