1 /*
2 *******************************************************************************
3 * Copyright (C) 2003-2005, 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;
27
28 U_CAPI void U_EXPORT2
utrace_entry(int32_t fnNumber)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
utrace_exit(int32_t fnNumber,int32_t returnType,...)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
utrace_data(int32_t fnNumber,int32_t level,const char * fmt,...)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
outputChar(char c,char * outBuf,int32_t * outIx,int32_t capacity,int32_t indent)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
outputHexBytes(int64_t val,int32_t charsToOutput,char * outBuf,int32_t * outIx,int32_t capacity)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 */
outputPtrBytes(void * val,char * outBuf,int32_t * outIx,int32_t capacity)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
outputString(const char * s,char * outBuf,int32_t * outIx,int32_t capacity,int32_t indent)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
outputUString(const UChar * s,int32_t len,char * outBuf,int32_t * outIx,int32_t capacity,int32_t indent)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
utrace_vformat(char * outBuf,int32_t capacity,int32_t indent,const char * fmt,va_list args)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
utrace_format(char * outBuf,int32_t capacity,int32_t indent,const char * fmt,...)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
utrace_setFunctions(const void * context,UTraceEntry * e,UTraceExit * x,UTraceData * d)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
utrace_getFunctions(const void ** context,UTraceEntry ** e,UTraceExit ** x,UTraceData ** d)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
utrace_setLevel(int32_t level)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
utrace_getLevel()423 utrace_getLevel() {
424 return utrace_level;
425 }
426
427
428 U_CFUNC UBool
utrace_cleanup()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
utrace_functionName(int32_t fnNumber)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
487