1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 2003-2014, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
10 * tab size: 8 (not used)
15 #include "unicode/utrace.h"
22 static UTraceEntry *pTraceEntryFunc = NULL;
23 static UTraceExit *pTraceExitFunc = NULL;
24 static UTraceData *pTraceDataFunc = NULL;
25 static const void *gTraceContext = NULL;
28 utrace_level = UTRACE_ERROR;
31 utrace_entry(int32_t fnNumber) {
32 if (pTraceEntryFunc != NULL) {
33 (*pTraceEntryFunc)(gTraceContext, fnNumber);
38 static const char gExitFmt[] = "Returns.";
39 static const char gExitFmtValue[] = "Returns %d.";
40 static const char gExitFmtStatus[] = "Returns. Status = %d.";
41 static const char gExitFmtValueStatus[] = "Returns %d. Status = %d.";
42 static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p.";
45 utrace_exit(int32_t fnNumber, int32_t returnType, ...) {
46 if (pTraceExitFunc != NULL) {
54 case UTRACE_EXITV_I32:
57 case UTRACE_EXITV_STATUS:
60 case UTRACE_EXITV_I32 | UTRACE_EXITV_STATUS:
61 fmt = gExitFmtValueStatus;
63 case UTRACE_EXITV_PTR | UTRACE_EXITV_STATUS:
64 fmt = gExitFmtPtrStatus;
71 va_start(args, returnType);
72 (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args);
80 utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) {
81 if (pTraceDataFunc != NULL) {
84 (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args);
90 static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
92 /* Check whether a start of line indenting is needed. Three cases:
93 * 1. At the start of the first line (output index == 0).
94 * 2. At the start of subsequent lines (preceeding char in buffer == '\n')
95 * 3. When preflighting buffer len (buffer capacity is exceeded), when
96 * a \n is output. Ideally we wouldn't do the indent until the following char
97 * is received, but that won't work because there's no place to remember that
98 * the preceding char was \n. Meaning that we may overstimate the
99 * buffer size needed. No harm done.
101 if (*outIx==0 || /* case 1. */
102 (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */
103 (c=='\n' && *outIx>=capacity)) /* case 3 */
105 /* At the start of a line. Indent. */
106 for(i=0; i<indent; i++) {
107 if (*outIx < capacity) {
108 outBuf[*outIx] = ' ';
114 if (*outIx < capacity) {
118 /* Nulls only appear as end-of-string terminators. Move them to the output
119 * buffer, but do not update the length of the buffer, so that any
120 * following output will overwrite the null. */
125 static void outputHexBytes(int64_t val, int32_t charsToOutput,
126 char *outBuf, int32_t *outIx, int32_t capacity) {
127 static const char gHexChars[] = "0123456789abcdef";
129 for (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) {
130 char c = gHexChars[(val >> shiftCount) & 0xf];
131 outputChar(c, outBuf, outIx, capacity, 0);
135 /* Output a pointer value in hex. Work with any size of pointer */
136 static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) {
138 int32_t incVal = 1; /* +1 for big endian, -1 for little endian */
139 char *p = (char *)&val; /* point to current byte to output in the ptr val */
142 /* Little Endian. Move p to most significant end of the value */
144 p += sizeof(void *) - 1;
147 /* Loop through the bytes of the ptr as it sits in memory, from
148 * most significant to least significant end */
149 for (i=0; i<sizeof(void *); i++) {
150 outputHexBytes(*p, 2, outBuf, outIx, capacity);
155 static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
163 outputChar(c, outBuf, outIx, capacity, indent);
169 static void outputUString(const UChar *s, int32_t len,
170 char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
174 outputString(NULL, outBuf, outIx, capacity, indent);
178 for (i=0; i<len || len==-1; i++) {
180 outputHexBytes(c, 4, outBuf, outIx, capacity);
181 outputChar(' ', outBuf, outIx, capacity, indent);
182 if (len == -1 && c==0) {
188 U_CAPI int32_t U_EXPORT2
189 utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) {
198 /* Loop runs once for each character in the format string.
203 /* Literal character, not part of a %sequence. Just copy it to the output. */
204 outputChar(fmtC, outBuf, &outIx, capacity, indent);
206 /* We hit the null that terminates the format string.
207 * This is the normal (and only) exit from the loop that
208 * interprets the format
215 /* We encountered a '%'. Pick up the following format char */
220 /* single 8 bit char */
221 c = (char)va_arg(args, int32_t);
222 outputChar(c, outBuf, &outIx, capacity, indent);
226 /* char * string, null terminated. */
227 ptrArg = va_arg(args, char *);
228 outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent);
232 /* UChar * string, with length, len==-1 for null terminated. */
233 ptrArg = va_arg(args, void *); /* Ptr */
234 intArg =(int32_t)va_arg(args, int32_t); /* Length */
235 outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent);
240 intArg = va_arg(args, int);
241 outputHexBytes(intArg, 2, outBuf, &outIx, capacity);
246 intArg = va_arg(args, int);
247 outputHexBytes(intArg, 4, outBuf, &outIx, capacity);
252 intArg = va_arg(args, int);
253 outputHexBytes(intArg, 8, outBuf, &outIx, capacity);
258 longArg = va_arg(args, int64_t);
259 outputHexBytes(longArg, 16, outBuf, &outIx, capacity);
264 ptrArg = va_arg(args, void *);
265 outputPtrBytes(ptrArg, outBuf, &outIx, capacity);
269 /* Single '%' at end of fmt string. Output as literal '%'.
270 * Back up index into format string so that the terminating null will be
271 * re-fetched in the outer loop, causing it to terminate.
273 outputChar('%', outBuf, &outIx, capacity, indent);
279 /* Vector of values, e.g. %vh */
287 int32_t charsToOutput = 0;
290 vectorType = fmt[fmtIx]; /* b, h, d, l, p, etc. */
291 if (vectorType != 0) {
294 i8Ptr = (const char *)va_arg(args, void*);
295 i16Ptr = (int16_t *)i8Ptr;
296 i32Ptr = (int32_t *)i8Ptr;
297 i64Ptr = (int64_t *)i8Ptr;
298 ptrPtr = (void **)i8Ptr;
299 vectorLen =(int32_t)va_arg(args, int32_t);
300 if (ptrPtr == NULL) {
301 outputString("*NULL* ", outBuf, &outIx, capacity, indent);
303 for (i=0; i<vectorLen || vectorLen==-1; i++) {
304 switch (vectorType) {
323 outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity);
324 longArg = *ptrPtr==NULL? 0: 1; /* test for null terminated array. */
329 outputChar(*i8Ptr, outBuf, &outIx, capacity, indent);
330 longArg = *i8Ptr; /* for test for null terminated array. */
335 outputString(*ptrPtr, outBuf, &outIx, capacity, indent);
336 outputChar('\n', outBuf, &outIx, capacity, indent);
337 longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */
343 outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent);
344 outputChar('\n', outBuf, &outIx, capacity, indent);
345 longArg = *ptrPtr==NULL? 0: 1; /* for test for null term. array. */
351 if (charsToOutput > 0) {
352 outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity);
353 outputChar(' ', outBuf, &outIx, capacity, indent);
355 if (vectorLen == -1 && longArg == 0) {
360 outputChar('[', outBuf, &outIx, capacity, indent);
361 outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity);
362 outputChar(']', outBuf, &outIx, capacity, indent);
368 /* %. in format string, where . is some character not in the set
369 * of recognized format chars. Just output it as if % wasn't there.
370 * (Covers "%%" outputing a single '%')
372 outputChar(fmtC, outBuf, &outIx, capacity, indent);
375 outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is null terminated */
376 return outIx + 1; /* outIx + 1 because outIx does not increment when outputing final null. */
382 U_CAPI int32_t U_EXPORT2
383 utrace_format(char *outBuf, int32_t capacity,
384 int32_t indent, const char *fmt, ...) {
387 va_start(args, fmt );
388 retVal = utrace_vformat(outBuf, capacity, indent, fmt, args);
394 U_CAPI void U_EXPORT2
395 utrace_setFunctions(const void *context,
396 UTraceEntry *e, UTraceExit *x, UTraceData *d) {
400 gTraceContext = context;
404 U_CAPI void U_EXPORT2
405 utrace_getFunctions(const void **context,
406 UTraceEntry **e, UTraceExit **x, UTraceData **d) {
407 *e = pTraceEntryFunc;
410 *context = gTraceContext;
413 U_CAPI void U_EXPORT2
414 utrace_setLevel(int32_t level) {
415 if (level < UTRACE_OFF) {
418 if (level > UTRACE_VERBOSE) {
419 level = UTRACE_VERBOSE;
421 utrace_level = level;
424 U_CAPI int32_t U_EXPORT2
432 pTraceEntryFunc = NULL;
433 pTraceExitFunc = NULL;
434 pTraceDataFunc = NULL;
435 utrace_level = UTRACE_OFF;
436 gTraceContext = NULL;
441 static const char * const
449 static const char * const
453 "ucnv_openAlgorithmic",
463 static const char * const
470 "ucol_nextSortKeyPart",
472 "ucol_openFromShortString",
478 U_CAPI const char * U_EXPORT2
479 utrace_functionName(int32_t fnNumber) {
480 if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) {
481 return trFnName[fnNumber];
482 } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) {
483 return trConvNames[fnNumber - UTRACE_CONVERSION_START];
484 } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){
485 return trCollNames[fnNumber - UTRACE_COLLATION_START];
487 return "[BOGUS Trace Function Number]";