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 = NULL; 22 static UTraceExit *pTraceExitFunc = NULL; 23 static UTraceData *pTraceDataFunc = NULL; 24 static const void *gTraceContext = NULL; 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 != 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) { 140 uint32_t i; 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. */ 236 ptrArg = va_arg(args, char *); /* Ptr */ 237 intArg =(int32_t)va_arg(args, int32_t); /* Length */ 238 outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent); 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. */ 267 ptrArg = va_arg(args, char *); 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; 338 outputString((const char *)*ptrPtr, outBuf, &outIx, capacity, indent); 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; 346 outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent); 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", 475 "ucol_openFromShortString", 476 "ucol_strcollUTF8", 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 494