1/* 2******************************************************************************* 3* Copyright (C) 2003-2014, 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 20static UTraceEntry *pTraceEntryFunc = NULL; 21static UTraceExit *pTraceExitFunc = NULL; 22static UTraceData *pTraceDataFunc = NULL; 23static const void *gTraceContext = NULL; 24 25U_EXPORT int32_t 26utrace_level = UTRACE_ERROR; 27 28U_CAPI void U_EXPORT2 29utrace_entry(int32_t fnNumber) { 30 if (pTraceEntryFunc != NULL) { 31 (*pTraceEntryFunc)(gTraceContext, fnNumber); 32 } 33} 34 35 36static const char gExitFmt[] = "Returns."; 37static const char gExitFmtValue[] = "Returns %d."; 38static const char gExitFmtStatus[] = "Returns. Status = %d."; 39static const char gExitFmtValueStatus[] = "Returns %d. Status = %d."; 40static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p."; 41 42U_CAPI void U_EXPORT2 43utrace_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 77U_CAPI void U_EXPORT2 78utrace_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 88static 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 123static 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 */ 134static 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 153static 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 167static 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 186U_CAPI int32_t U_EXPORT2 187utrace_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 380U_CAPI int32_t U_EXPORT2 381utrace_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 392U_CAPI void U_EXPORT2 393utrace_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 402U_CAPI void U_EXPORT2 403utrace_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 411U_CAPI void U_EXPORT2 412utrace_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 422U_CAPI int32_t U_EXPORT2 423utrace_getLevel() { 424 return utrace_level; 425} 426 427 428U_CFUNC UBool 429utrace_cleanup() { 430 pTraceEntryFunc = NULL; 431 pTraceExitFunc = NULL; 432 pTraceDataFunc = NULL; 433 utrace_level = UTRACE_OFF; 434 gTraceContext = NULL; 435 return TRUE; 436} 437 438 439static const char * const 440trFnName[] = { 441 "u_init", 442 "u_cleanup", 443 NULL 444}; 445 446 447static const char * const 448trConvNames[] = { 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 461static const char * const 462trCollNames[] = { 463 "ucol_open", 464 "ucol_close", 465 "ucol_strcoll", 466 "ucol_getSortKey", 467 "ucol_getLocale", 468 "ucol_nextSortKeyPart", 469 "ucol_strcollIter", 470 "ucol_openFromShortString", 471 "ucol_strcollUTF8", 472 NULL 473}; 474 475 476U_CAPI const char * U_EXPORT2 477utrace_functionName(int32_t fnNumber) { 478 if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) { 479 return trFnName[fnNumber]; 480 } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) { 481 return trConvNames[fnNumber - UTRACE_CONVERSION_START]; 482 } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){ 483 return trCollNames[fnNumber - UTRACE_COLLATION_START]; 484 } else { 485 return "[BOGUS Trace Function Number]"; 486 } 487} 488 489