OLD | NEW |
(Empty) | |
| 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, i
nt32_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 follow
ing char |
| 95 * is received, but that won't work because there's no place to rememb
er 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 out
put |
| 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 capa
city) { |
| 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 ca
pacity, 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, capacit
y, 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 wil
l 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 t
erminated 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, inde
nt); |
| 334 outputChar('\n', outBuf, &outIx, capacity, indent); |
| 335 longArg = *ptrPtr==NULL? 0: 1; /* for test for nul
l term. array. */ |
| 336 ptrPtr++; |
| 337 break; |
| 338 |
| 339 case 'S': |
| 340 charsToOutput = 0; |
| 341 outputUString((const UChar *)*ptrPtr, -1, outBuf, &o
utIx, capacity, indent); |
| 342 outputChar('\n', outBuf, &outIx, capacity, indent); |
| 343 longArg = *ptrPtr==NULL? 0: 1; /* for test for nul
l term. array. */ |
| 344 ptrPtr++; |
| 345 break; |
| 346 |
| 347 |
| 348 } |
| 349 if (charsToOutput > 0) { |
| 350 outputHexBytes(longArg, charsToOutput, outBuf, &outI
x, 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 the
re. |
| 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 i
s null terminated */ |
| 374 return outIx + 1; /* outIx + 1 because outIx does not increment when out
puting 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_CONVERSIO
N_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 |
OLD | NEW |