/* ******************************************************************************* * Copyright (C) 2003-2008, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************* * file name: utrace.c * encoding: US-ASCII * tab size: 8 (not used) * indentation:4 */ #define UTRACE_IMPL #include "unicode/utrace.h" #include "utracimp.h" #include "cstring.h" #include "uassert.h" #include "ucln_cmn.h" static UTraceEntry *pTraceEntryFunc = NULL; static UTraceExit *pTraceExitFunc = NULL; static UTraceData *pTraceDataFunc = NULL; static const void *gTraceContext = NULL; U_EXPORT int32_t utrace_level = UTRACE_ERROR; U_CAPI void U_EXPORT2 utrace_entry(int32_t fnNumber) { if (pTraceEntryFunc != NULL) { (*pTraceEntryFunc)(gTraceContext, fnNumber); } } static const char gExitFmt[] = "Returns."; static const char gExitFmtValue[] = "Returns %d."; static const char gExitFmtStatus[] = "Returns. Status = %d."; static const char gExitFmtValueStatus[] = "Returns %d. Status = %d."; static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p."; U_CAPI void U_EXPORT2 utrace_exit(int32_t fnNumber, int32_t returnType, ...) { if (pTraceExitFunc != NULL) { va_list args; const char *fmt; switch (returnType) { case 0: fmt = gExitFmt; break; case UTRACE_EXITV_I32: fmt = gExitFmtValue; break; case UTRACE_EXITV_STATUS: fmt = gExitFmtStatus; break; case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS: fmt = gExitFmtValueStatus; break; case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS: fmt = gExitFmtPtrStatus; break; default: U_ASSERT(FALSE); fmt = gExitFmt; } va_start(args, returnType); (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args); va_end(args); } } U_CAPI void U_EXPORT2 utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) { if (pTraceDataFunc != NULL) { va_list args; va_start(args, fmt ); (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args); va_end(args); } } static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { int32_t i; /* Check whether a start of line indenting is needed. Three cases: * 1. At the start of the first line (output index == 0). * 2. At the start of subsequent lines (preceeding char in buffer == '\n') * 3. When preflighting buffer len (buffer capacity is exceeded), when * a \n is output. Ideally we wouldn't do the indent until the following char * is received, but that won't work because there's no place to remember that * the preceding char was \n. Meaning that we may overstimate the * buffer size needed. No harm done. */ if (*outIx==0 || /* case 1. */ (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */ (c=='\n' && *outIx>=capacity)) /* case 3 */ { /* At the start of a line. Indent. */ for(i=0; i<indent; i++) { if (*outIx < capacity) { outBuf[*outIx] = ' '; } (*outIx)++; } } if (*outIx < capacity) { outBuf[*outIx] = c; } if (c != 0) { /* Nulls only appear as end-of-string terminators. Move them to the output * buffer, but do not update the length of the buffer, so that any * following output will overwrite the null. */ (*outIx)++; } } static void outputHexBytes(int64_t val, int32_t charsToOutput, char *outBuf, int32_t *outIx, int32_t capacity) { static const char gHexChars[] = "0123456789abcdef"; int32_t shiftCount; for (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) { char c = gHexChars[(val >> shiftCount) & 0xf]; outputChar(c, outBuf, outIx, capacity, 0); } } /* Output a pointer value in hex. Work with any size of pointer */ static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) { int32_t i; int32_t incVal = 1; /* +1 for big endian, -1 for little endian */ char *p = (char *)&val; /* point to current byte to output in the ptr val */ #if !U_IS_BIG_ENDIAN /* Little Endian. Move p to most significant end of the value */ incVal = -1; p += sizeof(void *) - 1; #endif /* Loop through the bytes of the ptr as it sits in memory, from * most significant to least significant end */ for (i=0; i<sizeof(void *); i++) { outputHexBytes(*p, 2, outBuf, outIx, capacity); p += incVal; } } static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { int32_t i = 0; char c; if (s==NULL) { s = "*NULL*"; } do { c = s[i++]; outputChar(c, outBuf, outIx, capacity, indent); } while (c != 0); } static void outputUString(const UChar *s, int32_t len, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) { int32_t i = 0; UChar c; if (s==NULL) { outputString(NULL, outBuf, outIx, capacity, indent); return; } for (i=0; i<len || len==-1; i++) { c = s[i]; outputHexBytes(c, 4, outBuf, outIx, capacity); outputChar(' ', outBuf, outIx, capacity, indent); if (len == -1 && c==0) { break; } } } U_CAPI int32_t U_EXPORT2 utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) { int32_t outIx = 0; int32_t fmtIx = 0; char fmtC; char c; int32_t intArg; int64_t longArg = 0; char *ptrArg; /* Loop runs once for each character in the format string. */ for (;;) { fmtC = fmt[fmtIx++]; if (fmtC != '%') { /* Literal character, not part of a %sequence. Just copy it to the output. */ outputChar(fmtC, outBuf, &outIx, capacity, indent); if (fmtC == 0) { /* We hit the null that terminates the format string. * This is the normal (and only) exit from the loop that * interprets the format */ break; } continue; } /* We encountered a '%'. Pick up the following format char */ fmtC = fmt[fmtIx++]; switch (fmtC) { case 'c': /* single 8 bit char */ c = (char)va_arg(args, int32_t); outputChar(c, outBuf, &outIx, capacity, indent); break; case 's': /* char * string, null terminated. */ ptrArg = va_arg(args, char *); outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent); break; case 'S': /* UChar * string, with length, len==-1 for null terminated. */ ptrArg = va_arg(args, void *); /* Ptr */ intArg =(int32_t)va_arg(args, int32_t); /* Length */ outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent); break; case 'b': /* 8 bit int */ intArg = va_arg(args, int); outputHexBytes(intArg, 2, outBuf, &outIx, capacity); break; case 'h': /* 16 bit int */ intArg = va_arg(args, int); outputHexBytes(intArg, 4, outBuf, &outIx, capacity); break; case 'd': /* 32 bit int */ intArg = va_arg(args, int); outputHexBytes(intArg, 8, outBuf, &outIx, capacity); break; case 'l': /* 64 bit long */ longArg = va_arg(args, int64_t); outputHexBytes(longArg, 16, outBuf, &outIx, capacity); break; case 'p': /* Pointers. */ ptrArg = va_arg(args, void *); outputPtrBytes(ptrArg, outBuf, &outIx, capacity); break; case 0: /* Single '%' at end of fmt string. Output as literal '%'. * Back up index into format string so that the terminating null will be * re-fetched in the outer loop, causing it to terminate. */ outputChar('%', outBuf, &outIx, capacity, indent); fmtIx--; break; case 'v': { /* Vector of values, e.g. %vh */ char vectorType; int32_t vectorLen; const char *i8Ptr; int16_t *i16Ptr; int32_t *i32Ptr; int64_t *i64Ptr; void **ptrPtr; int32_t charsToOutput = 0; int32_t i; vectorType = fmt[fmtIx]; /* b, h, d, l, p, etc. */ if (vectorType != 0) { fmtIx++; } i8Ptr = (const char *)va_arg(args, void*); i16Ptr = (int16_t *)i8Ptr; i32Ptr = (int32_t *)i8Ptr; i64Ptr = (int64_t *)i8Ptr; ptrPtr = (void **)i8Ptr; vectorLen =(int32_t)va_arg(args, int32_t); if (ptrPtr == NULL) { outputString("*NULL* ", outBuf, &outIx, capacity, indent); } else { for (i=0; i<vectorLen || vectorLen==-1; i++) { switch (vectorType) { case 'b': charsToOutput = 2; longArg = *i8Ptr++; break; case 'h': charsToOutput = 4; longArg = *i16Ptr++; break; case 'd': charsToOutput = 8; longArg = *i32Ptr++; break; case 'l': charsToOutput = 16; longArg = *i64Ptr++; break; case 'p': charsToOutput = 0; outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity); longArg = *ptrPtr==NULL? 0: 1; /* test for null terminated array. */ ptrPtr++; break; case 'c': charsToOutput = 0; outputChar(*i8Ptr, outBuf, &outIx, capacity, indent); longArg = *i8Ptr; /* for test for null terminated array. */ i8Ptr++; break; case 's': charsToOutput = 0; outputString(*ptrPtr, outBuf, &outIx, capacity, indent); outputChar('\n', outBuf, &outIx, capacity, indent); longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */ ptrPtr++; break; case 'S': charsToOutput = 0; outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent); outputChar('\n', outBuf, &outIx, capacity, indent); longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */ ptrPtr++; break; } if (charsToOutput > 0) { outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity); outputChar(' ', outBuf, &outIx, capacity, indent); } if (vectorLen == -1 && longArg == 0) { break; } } } outputChar('[', outBuf, &outIx, capacity, indent); outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity); outputChar(']', outBuf, &outIx, capacity, indent); } break; default: /* %. in format string, where . is some character not in the set * of recognized format chars. Just output it as if % wasn't there. * (Covers "%%" outputing a single '%') */ outputChar(fmtC, outBuf, &outIx, capacity, indent); } } outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is null terminated */ return outIx + 1; /* outIx + 1 because outIx does not increment when outputing final null. */ } U_CAPI int32_t U_EXPORT2 utrace_format(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, ...) { int32_t retVal; va_list args; va_start(args, fmt ); retVal = utrace_vformat(outBuf, capacity, indent, fmt, args); va_end(args); return retVal; } U_CAPI void U_EXPORT2 utrace_setFunctions(const void *context, UTraceEntry *e, UTraceExit *x, UTraceData *d) { pTraceEntryFunc = e; pTraceExitFunc = x; pTraceDataFunc = d; gTraceContext = context; } U_CAPI void U_EXPORT2 utrace_getFunctions(const void **context, UTraceEntry **e, UTraceExit **x, UTraceData **d) { *e = pTraceEntryFunc; *x = pTraceExitFunc; *d = pTraceDataFunc; *context = gTraceContext; } U_CAPI void U_EXPORT2 utrace_setLevel(int32_t level) { if (level < UTRACE_OFF) { level = UTRACE_OFF; } if (level > UTRACE_VERBOSE) { level = UTRACE_VERBOSE; } utrace_level = level; } U_CAPI int32_t U_EXPORT2 utrace_getLevel() { return utrace_level; } U_CFUNC UBool utrace_cleanup() { pTraceEntryFunc = NULL; pTraceExitFunc = NULL; pTraceDataFunc = NULL; utrace_level = UTRACE_OFF; gTraceContext = NULL; return TRUE; } static const char * const trFnName[] = { "u_init", "u_cleanup", NULL }; static const char * const trConvNames[] = { "ucnv_open", "ucnv_openPackage", "ucnv_openAlgorithmic", "ucnv_clone", "ucnv_close", "ucnv_flushCache", "ucnv_load", "ucnv_unload", NULL }; static const char * const trCollNames[] = { "ucol_open", "ucol_close", "ucol_strcoll", "ucol_getSortKey", "ucol_getLocale", "ucol_nextSortKeyPart", "ucol_strcollIter", NULL }; U_CAPI const char * U_EXPORT2 utrace_functionName(int32_t fnNumber) { if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) { return trFnName[fnNumber]; } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) { return trConvNames[fnNumber - UTRACE_CONVERSION_START]; } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){ return trCollNames[fnNumber - UTRACE_COLLATION_START]; } else { return "[BOGUS Trace Function Number]"; } }