// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2003-2014, International Business Machines * Corporation and others. All Rights Reserved. ******************************************************************************* * file name: utrace.c * encoding: UTF-8 * tab size: 8 (not used) * indentation:4 */ #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; /** * \var utrace_level * Trace level variable. Negative for "off". */ static 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: UPRV_UNREACHABLE_EXIT; } 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 (preceding 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= 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) { uint32_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 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 "%%" outputting 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 outputting 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", "ucol_openFromShortString", "ucol_strcollUTF8", NULL }; static const char* const trResDataNames[] = { "resc", "bundle-open", "file-open", "res-open", 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 if(UTRACE_UDATA_START <= fnNumber && fnNumber < UTRACE_RES_DATA_LIMIT){ return trResDataNames[fnNumber - UTRACE_UDATA_START]; } else { return "[BOGUS Trace Function Number]"; } }