|
1 /* |
|
2 ******************************************************************************* |
|
3 * Copyright (C) 2003-2008, International Business Machines |
|
4 * Corporation and others. All Rights Reserved. |
|
5 ******************************************************************************* |
|
6 * file name: utrace.c |
|
7 * encoding: US-ASCII |
|
8 * tab size: 8 (not used) |
|
9 * indentation:4 |
|
10 */ |
|
11 |
|
12 #define UTRACE_IMPL |
|
13 #include "unicode/utrace.h" |
|
14 #include "utracimp.h" |
|
15 #include "cstring.h" |
|
16 #include "uassert.h" |
|
17 #include "ucln_cmn.h" |
|
18 |
|
19 |
|
20 static UTraceEntry *pTraceEntryFunc = NULL; |
|
21 static UTraceExit *pTraceExitFunc = NULL; |
|
22 static UTraceData *pTraceDataFunc = NULL; |
|
23 static const void *gTraceContext = NULL; |
|
24 |
|
25 U_EXPORT int32_t |
|
26 utrace_level = UTRACE_ERROR; |
|
27 |
|
28 U_CAPI void U_EXPORT2 |
|
29 utrace_entry(int32_t fnNumber) { |
|
30 if (pTraceEntryFunc != NULL) { |
|
31 (*pTraceEntryFunc)(gTraceContext, fnNumber); |
|
32 } |
|
33 } |
|
34 |
|
35 |
|
36 static const char gExitFmt[] = "Returns."; |
|
37 static const char gExitFmtValue[] = "Returns %d."; |
|
38 static const char gExitFmtStatus[] = "Returns. Status = %d."; |
|
39 static const char gExitFmtValueStatus[] = "Returns %d. Status = %d."; |
|
40 static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p."; |
|
41 |
|
42 U_CAPI void U_EXPORT2 |
|
43 utrace_exit(int32_t fnNumber, int32_t returnType, ...) { |
|
44 if (pTraceExitFunc != NULL) { |
|
45 va_list args; |
|
46 const char *fmt; |
|
47 |
|
48 switch (returnType) { |
|
49 case 0: |
|
50 fmt = gExitFmt; |
|
51 break; |
|
52 case UTRACE_EXITV_I32: |
|
53 fmt = gExitFmtValue; |
|
54 break; |
|
55 case UTRACE_EXITV_STATUS: |
|
56 fmt = gExitFmtStatus; |
|
57 break; |
|
58 case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS: |
|
59 fmt = gExitFmtValueStatus; |
|
60 break; |
|
61 case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS: |
|
62 fmt = gExitFmtPtrStatus; |
|
63 break; |
|
64 default: |
|
65 U_ASSERT(FALSE); |
|
66 fmt = gExitFmt; |
|
67 } |
|
68 |
|
69 va_start(args, returnType); |
|
70 (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args); |
|
71 va_end(args); |
|
72 } |
|
73 } |
|
74 |
|
75 |
|
76 |
|
77 U_CAPI void U_EXPORT2 |
|
78 utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) { |
|
79 if (pTraceDataFunc != NULL) { |
|
80 va_list args; |
|
81 va_start(args, fmt ); |
|
82 (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args); |
|
83 va_end(args); |
|
84 } |
|
85 } |
|
86 |
|
87 |
|
88 static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { |
|
89 int32_t i; |
|
90 /* Check whether a start of line indenting is needed. Three cases: |
|
91 * 1. At the start of the first line (output index == 0). |
|
92 * 2. At the start of subsequent lines (preceeding char in buffer == '\n') |
|
93 * 3. When preflighting buffer len (buffer capacity is exceeded), when |
|
94 * a \n is output. Ideally we wouldn't do the indent until the following char |
|
95 * is received, but that won't work because there's no place to remember that |
|
96 * the preceding char was \n. Meaning that we may overstimate the |
|
97 * buffer size needed. No harm done. |
|
98 */ |
|
99 if (*outIx==0 || /* case 1. */ |
|
100 (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */ |
|
101 (c=='\n' && *outIx>=capacity)) /* case 3 */ |
|
102 { |
|
103 /* At the start of a line. Indent. */ |
|
104 for(i=0; i<indent; i++) { |
|
105 if (*outIx < capacity) { |
|
106 outBuf[*outIx] = ' '; |
|
107 } |
|
108 (*outIx)++; |
|
109 } |
|
110 } |
|
111 |
|
112 if (*outIx < capacity) { |
|
113 outBuf[*outIx] = c; |
|
114 } |
|
115 if (c != 0) { |
|
116 /* Nulls only appear as end-of-string terminators. Move them to the output |
|
117 * buffer, but do not update the length of the buffer, so that any |
|
118 * following output will overwrite the null. */ |
|
119 (*outIx)++; |
|
120 } |
|
121 } |
|
122 |
|
123 static void outputHexBytes(int64_t val, int32_t charsToOutput, |
|
124 char *outBuf, int32_t *outIx, int32_t capacity) { |
|
125 static const char gHexChars[] = "0123456789abcdef"; |
|
126 int32_t shiftCount; |
|
127 for (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) { |
|
128 char c = gHexChars[(val >> shiftCount) & 0xf]; |
|
129 outputChar(c, outBuf, outIx, capacity, 0); |
|
130 } |
|
131 } |
|
132 |
|
133 /* Output a pointer value in hex. Work with any size of pointer */ |
|
134 static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) { |
|
135 int32_t i; |
|
136 int32_t incVal = 1; /* +1 for big endian, -1 for little endian */ |
|
137 char *p = (char *)&val; /* point to current byte to output in the ptr val */ |
|
138 |
|
139 #if !U_IS_BIG_ENDIAN |
|
140 /* Little Endian. Move p to most significant end of the value */ |
|
141 incVal = -1; |
|
142 p += sizeof(void *) - 1; |
|
143 #endif |
|
144 |
|
145 /* Loop through the bytes of the ptr as it sits in memory, from |
|
146 * most significant to least significant end */ |
|
147 for (i=0; i<sizeof(void *); i++) { |
|
148 outputHexBytes(*p, 2, outBuf, outIx, capacity); |
|
149 p += incVal; |
|
150 } |
|
151 } |
|
152 |
|
153 static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { |
|
154 int32_t i = 0; |
|
155 char c; |
|
156 if (s==NULL) { |
|
157 s = "*NULL*"; |
|
158 } |
|
159 do { |
|
160 c = s[i++]; |
|
161 outputChar(c, outBuf, outIx, capacity, indent); |
|
162 } while (c != 0); |
|
163 } |
|
164 |
|
165 |
|
166 |
|
167 static void outputUString(const UChar *s, int32_t len, |
|
168 char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { |
|
169 int32_t i = 0; |
|
170 UChar c; |
|
171 if (s==NULL) { |
|
172 outputString(NULL, outBuf, outIx, capacity, indent); |
|
173 return; |
|
174 } |
|
175 |
|
176 for (i=0; i<len || len==-1; i++) { |
|
177 c = s[i]; |
|
178 outputHexBytes(c, 4, outBuf, outIx, capacity); |
|
179 outputChar(' ', outBuf, outIx, capacity, indent); |
|
180 if (len == -1 && c==0) { |
|
181 break; |
|
182 } |
|
183 } |
|
184 } |
|
185 |
|
186 U_CAPI int32_t U_EXPORT2 |
|
187 utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) { |
|
188 int32_t outIx = 0; |
|
189 int32_t fmtIx = 0; |
|
190 char fmtC; |
|
191 char c; |
|
192 int32_t intArg; |
|
193 int64_t longArg = 0; |
|
194 char *ptrArg; |
|
195 |
|
196 /* Loop runs once for each character in the format string. |
|
197 */ |
|
198 for (;;) { |
|
199 fmtC = fmt[fmtIx++]; |
|
200 if (fmtC != '%') { |
|
201 /* Literal character, not part of a %sequence. Just copy it to the output. */ |
|
202 outputChar(fmtC, outBuf, &outIx, capacity, indent); |
|
203 if (fmtC == 0) { |
|
204 /* We hit the null that terminates the format string. |
|
205 * This is the normal (and only) exit from the loop that |
|
206 * interprets the format |
|
207 */ |
|
208 break; |
|
209 } |
|
210 continue; |
|
211 } |
|
212 |
|
213 /* We encountered a '%'. Pick up the following format char */ |
|
214 fmtC = fmt[fmtIx++]; |
|
215 |
|
216 switch (fmtC) { |
|
217 case 'c': |
|
218 /* single 8 bit char */ |
|
219 c = (char)va_arg(args, int32_t); |
|
220 outputChar(c, outBuf, &outIx, capacity, indent); |
|
221 break; |
|
222 |
|
223 case 's': |
|
224 /* char * string, null terminated. */ |
|
225 ptrArg = va_arg(args, char *); |
|
226 outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent); |
|
227 break; |
|
228 |
|
229 case 'S': |
|
230 /* UChar * string, with length, len==-1 for null terminated. */ |
|
231 ptrArg = va_arg(args, void *); /* Ptr */ |
|
232 intArg =(int32_t)va_arg(args, int32_t); /* Length */ |
|
233 outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent); |
|
234 break; |
|
235 |
|
236 case 'b': |
|
237 /* 8 bit int */ |
|
238 intArg = va_arg(args, int); |
|
239 outputHexBytes(intArg, 2, outBuf, &outIx, capacity); |
|
240 break; |
|
241 |
|
242 case 'h': |
|
243 /* 16 bit int */ |
|
244 intArg = va_arg(args, int); |
|
245 outputHexBytes(intArg, 4, outBuf, &outIx, capacity); |
|
246 break; |
|
247 |
|
248 case 'd': |
|
249 /* 32 bit int */ |
|
250 intArg = va_arg(args, int); |
|
251 outputHexBytes(intArg, 8, outBuf, &outIx, capacity); |
|
252 break; |
|
253 |
|
254 case 'l': |
|
255 /* 64 bit long */ |
|
256 longArg = va_arg(args, int64_t); |
|
257 outputHexBytes(longArg, 16, outBuf, &outIx, capacity); |
|
258 break; |
|
259 |
|
260 case 'p': |
|
261 /* Pointers. */ |
|
262 ptrArg = va_arg(args, void *); |
|
263 outputPtrBytes(ptrArg, outBuf, &outIx, capacity); |
|
264 break; |
|
265 |
|
266 case 0: |
|
267 /* Single '%' at end of fmt string. Output as literal '%'. |
|
268 * Back up index into format string so that the terminating null will be |
|
269 * re-fetched in the outer loop, causing it to terminate. |
|
270 */ |
|
271 outputChar('%', outBuf, &outIx, capacity, indent); |
|
272 fmtIx--; |
|
273 break; |
|
274 |
|
275 case 'v': |
|
276 { |
|
277 /* Vector of values, e.g. %vh */ |
|
278 char vectorType; |
|
279 int32_t vectorLen; |
|
280 const char *i8Ptr; |
|
281 int16_t *i16Ptr; |
|
282 int32_t *i32Ptr; |
|
283 int64_t *i64Ptr; |
|
284 void **ptrPtr; |
|
285 int32_t charsToOutput = 0; |
|
286 int32_t i; |
|
287 |
|
288 vectorType = fmt[fmtIx]; /* b, h, d, l, p, etc. */ |
|
289 if (vectorType != 0) { |
|
290 fmtIx++; |
|
291 } |
|
292 i8Ptr = (const char *)va_arg(args, void*); |
|
293 i16Ptr = (int16_t *)i8Ptr; |
|
294 i32Ptr = (int32_t *)i8Ptr; |
|
295 i64Ptr = (int64_t *)i8Ptr; |
|
296 ptrPtr = (void **)i8Ptr; |
|
297 vectorLen =(int32_t)va_arg(args, int32_t); |
|
298 if (ptrPtr == NULL) { |
|
299 outputString("*NULL* ", outBuf, &outIx, capacity, indent); |
|
300 } else { |
|
301 for (i=0; i<vectorLen || vectorLen==-1; i++) { |
|
302 switch (vectorType) { |
|
303 case 'b': |
|
304 charsToOutput = 2; |
|
305 longArg = *i8Ptr++; |
|
306 break; |
|
307 case 'h': |
|
308 charsToOutput = 4; |
|
309 longArg = *i16Ptr++; |
|
310 break; |
|
311 case 'd': |
|
312 charsToOutput = 8; |
|
313 longArg = *i32Ptr++; |
|
314 break; |
|
315 case 'l': |
|
316 charsToOutput = 16; |
|
317 longArg = *i64Ptr++; |
|
318 break; |
|
319 case 'p': |
|
320 charsToOutput = 0; |
|
321 outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity); |
|
322 longArg = *ptrPtr==NULL? 0: 1; /* test for null terminated array. */ |
|
323 ptrPtr++; |
|
324 break; |
|
325 case 'c': |
|
326 charsToOutput = 0; |
|
327 outputChar(*i8Ptr, outBuf, &outIx, capacity, indent); |
|
328 longArg = *i8Ptr; /* for test for null terminated array. */ |
|
329 i8Ptr++; |
|
330 break; |
|
331 case 's': |
|
332 charsToOutput = 0; |
|
333 outputString(*ptrPtr, outBuf, &outIx, capacity, indent); |
|
334 outputChar('\n', outBuf, &outIx, capacity, indent); |
|
335 longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */ |
|
336 ptrPtr++; |
|
337 break; |
|
338 |
|
339 case 'S': |
|
340 charsToOutput = 0; |
|
341 outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent); |
|
342 outputChar('\n', outBuf, &outIx, capacity, indent); |
|
343 longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */ |
|
344 ptrPtr++; |
|
345 break; |
|
346 |
|
347 |
|
348 } |
|
349 if (charsToOutput > 0) { |
|
350 outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity); |
|
351 outputChar(' ', outBuf, &outIx, capacity, indent); |
|
352 } |
|
353 if (vectorLen == -1 && longArg == 0) { |
|
354 break; |
|
355 } |
|
356 } |
|
357 } |
|
358 outputChar('[', outBuf, &outIx, capacity, indent); |
|
359 outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity); |
|
360 outputChar(']', outBuf, &outIx, capacity, indent); |
|
361 } |
|
362 break; |
|
363 |
|
364 |
|
365 default: |
|
366 /* %. in format string, where . is some character not in the set |
|
367 * of recognized format chars. Just output it as if % wasn't there. |
|
368 * (Covers "%%" outputing a single '%') |
|
369 */ |
|
370 outputChar(fmtC, outBuf, &outIx, capacity, indent); |
|
371 } |
|
372 } |
|
373 outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is null terminated */ |
|
374 return outIx + 1; /* outIx + 1 because outIx does not increment when outputing final null. */ |
|
375 } |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 U_CAPI int32_t U_EXPORT2 |
|
381 utrace_format(char *outBuf, int32_t capacity, |
|
382 int32_t indent, const char *fmt, ...) { |
|
383 int32_t retVal; |
|
384 va_list args; |
|
385 va_start(args, fmt ); |
|
386 retVal = utrace_vformat(outBuf, capacity, indent, fmt, args); |
|
387 va_end(args); |
|
388 return retVal; |
|
389 } |
|
390 |
|
391 |
|
392 U_CAPI void U_EXPORT2 |
|
393 utrace_setFunctions(const void *context, |
|
394 UTraceEntry *e, UTraceExit *x, UTraceData *d) { |
|
395 pTraceEntryFunc = e; |
|
396 pTraceExitFunc = x; |
|
397 pTraceDataFunc = d; |
|
398 gTraceContext = context; |
|
399 } |
|
400 |
|
401 |
|
402 U_CAPI void U_EXPORT2 |
|
403 utrace_getFunctions(const void **context, |
|
404 UTraceEntry **e, UTraceExit **x, UTraceData **d) { |
|
405 *e = pTraceEntryFunc; |
|
406 *x = pTraceExitFunc; |
|
407 *d = pTraceDataFunc; |
|
408 *context = gTraceContext; |
|
409 } |
|
410 |
|
411 U_CAPI void U_EXPORT2 |
|
412 utrace_setLevel(int32_t level) { |
|
413 if (level < UTRACE_OFF) { |
|
414 level = UTRACE_OFF; |
|
415 } |
|
416 if (level > UTRACE_VERBOSE) { |
|
417 level = UTRACE_VERBOSE; |
|
418 } |
|
419 utrace_level = level; |
|
420 } |
|
421 |
|
422 U_CAPI int32_t U_EXPORT2 |
|
423 utrace_getLevel() { |
|
424 return utrace_level; |
|
425 } |
|
426 |
|
427 |
|
428 U_CFUNC UBool |
|
429 utrace_cleanup() { |
|
430 pTraceEntryFunc = NULL; |
|
431 pTraceExitFunc = NULL; |
|
432 pTraceDataFunc = NULL; |
|
433 utrace_level = UTRACE_OFF; |
|
434 gTraceContext = NULL; |
|
435 return TRUE; |
|
436 } |
|
437 |
|
438 |
|
439 static const char * const |
|
440 trFnName[] = { |
|
441 "u_init", |
|
442 "u_cleanup", |
|
443 NULL |
|
444 }; |
|
445 |
|
446 |
|
447 static const char * const |
|
448 trConvNames[] = { |
|
449 "ucnv_open", |
|
450 "ucnv_openPackage", |
|
451 "ucnv_openAlgorithmic", |
|
452 "ucnv_clone", |
|
453 "ucnv_close", |
|
454 "ucnv_flushCache", |
|
455 "ucnv_load", |
|
456 "ucnv_unload", |
|
457 NULL |
|
458 }; |
|
459 |
|
460 |
|
461 static const char * const |
|
462 trCollNames[] = { |
|
463 "ucol_open", |
|
464 "ucol_close", |
|
465 "ucol_strcoll", |
|
466 "ucol_getSortKey", |
|
467 "ucol_getLocale", |
|
468 "ucol_nextSortKeyPart", |
|
469 "ucol_strcollIter", |
|
470 NULL |
|
471 }; |
|
472 |
|
473 |
|
474 U_CAPI const char * U_EXPORT2 |
|
475 utrace_functionName(int32_t fnNumber) { |
|
476 if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) { |
|
477 return trFnName[fnNumber]; |
|
478 } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) { |
|
479 return trConvNames[fnNumber - UTRACE_CONVERSION_START]; |
|
480 } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){ |
|
481 return trCollNames[fnNumber - UTRACE_COLLATION_START]; |
|
482 } else { |
|
483 return "[BOGUS Trace Function Number]"; |
|
484 } |
|
485 } |
|
486 |