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