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
21static UTraceEntry *pTraceEntryFunc = nullptr;
22static UTraceExit *pTraceExitFunc = nullptr;
23static UTraceData *pTraceDataFunc = nullptr;
24static const void *gTraceContext = nullptr;
25
26/**
27 * \var utrace_level
28 * Trace level variable. Negative for "off".
29 */
30static int32_t
31utrace_level = UTRACE_ERROR;
32
33U_CAPI void U_EXPORT2
34utrace_entry(int32_t fnNumber) {
35 if (pTraceEntryFunc != nullptr) {
36 (*pTraceEntryFunc)(gTraceContext, fnNumber);
37 }
38}
39
40
41static const char gExitFmt[] = "Returns.";
42static const char gExitFmtValue[] = "Returns %d.";
43static const char gExitFmtStatus[] = "Returns. Status = %d.";
44static const char gExitFmtValueStatus[] = "Returns %d. Status = %d.";
45static const char gExitFmtPtrStatus[] = "Returns %d. Status = %p.";
46
47U_CAPI void U_EXPORT2
48utrace_exit(int32_t fnNumber, int32_t returnType, ...) {
49 if (pTraceExitFunc != nullptr) {
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 UPRV_UNREACHABLE_EXIT;
71 }
72
73 va_start(args, returnType);
74 (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args);
75 va_end(args);
76 }
77}
78
79
80
81U_CAPI void U_EXPORT2
82utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) {
83 if (pTraceDataFunc != nullptr) {
84 va_list args;
85 va_start(args, fmt );
86 (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args);
87 va_end(args);
88 }
89}
90
91
92static void outputChar(char c, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
93 int32_t i;
94 /* Check whether a start of line indenting is needed. Three cases:
95 * 1. At the start of the first line (output index == 0).
96 * 2. At the start of subsequent lines (preceding char in buffer == '\n')
97 * 3. When preflighting buffer len (buffer capacity is exceeded), when
98 * a \n is output. Ideally we wouldn't do the indent until the following char
99 * is received, but that won't work because there's no place to remember that
100 * the preceding char was \n. Meaning that we may overstimate the
101 * buffer size needed. No harm done.
102 */
103 if (*outIx==0 || /* case 1. */
104 (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') || /* case 2. */
105 (c=='\n' && *outIx>=capacity)) /* case 3 */
106 {
107 /* At the start of a line. Indent. */
108 for(i=0; i<indent; i++) {
109 if (*outIx < capacity) {
110 outBuf[*outIx] = ' ';
111 }
112 (*outIx)++;
113 }
114 }
115
116 if (*outIx < capacity) {
117 outBuf[*outIx] = c;
118 }
119 if (c != 0) {
120 /* NULs only appear as end-of-string terminators. Move them to the output
121 * buffer, but do not update the length of the buffer, so that any
122 * following output will overwrite the NUL. */
123 (*outIx)++;
124 }
125}
126
127static void outputHexBytes(int64_t val, int32_t charsToOutput,
128 char *outBuf, int32_t *outIx, int32_t capacity) {
129 static const char gHexChars[] = "0123456789abcdef";
130 int32_t shiftCount;
131 for (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) {
132 char c = gHexChars[(val >> shiftCount) & 0xf];
133 outputChar(c, outBuf, outIx, capacity, 0);
134 }
135}
136
137/* Output a pointer value in hex. Work with any size of pointer */
138static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) {
139 uint32_t i;
140 int32_t incVal = 1; /* +1 for big endian, -1 for little endian */
141 char *p = (char *)&val; /* point to current byte to output in the ptr val */
142
143#if !U_IS_BIG_ENDIAN
144 /* Little Endian. Move p to most significant end of the value */
145 incVal = -1;
146 p += sizeof(void *) - 1;
147#endif
148
149 /* Loop through the bytes of the ptr as it sits in memory, from
150 * most significant to least significant end */
151 for (i=0; i<sizeof(void *); i++) {
152 outputHexBytes(*p, 2, outBuf, outIx, capacity);
153 p += incVal;
154 }
155}
156
157static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
158 int32_t i = 0;
159 char c;
160 if (s==nullptr) {
161 s = "*NULL*";
162 }
163 do {
164 c = s[i++];
165 outputChar(c, outBuf, outIx, capacity, indent);
166 } while (c != 0);
167}
168
169
170
171static void outputUString(const char16_t *s, int32_t len,
172 char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
173 int32_t i = 0;
174 char16_t c;
175 if (s==nullptr) {
176 outputString(nullptr, outBuf, outIx, capacity, indent);
177 return;
178 }
179
180 for (i=0; i<len || len==-1; i++) {
181 c = s[i];
182 outputHexBytes(c, 4, outBuf, outIx, capacity);
183 outputChar(' ', outBuf, outIx, capacity, indent);
184 if (len == -1 && c==0) {
185 break;
186 }
187 }
188}
189
190U_CAPI int32_t U_EXPORT2
191utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) {
192 int32_t outIx = 0;
193 int32_t fmtIx = 0;
194 char fmtC;
195 char c;
196 int32_t intArg;
197 int64_t longArg = 0;
198 char *ptrArg;
199
200 /* Loop runs once for each character in the format string.
201 */
202 for (;;) {
203 fmtC = fmt[fmtIx++];
204 if (fmtC != '%') {
205 /* Literal character, not part of a %sequence. Just copy it to the output. */
206 outputChar(fmtC, outBuf, &outIx, capacity, indent);
207 if (fmtC == 0) {
208 /* We hit the NUL that terminates the format string.
209 * This is the normal (and only) exit from the loop that
210 * interprets the format
211 */
212 break;
213 }
214 continue;
215 }
216
217 /* We encountered a '%'. Pick up the following format char */
218 fmtC = fmt[fmtIx++];
219
220 switch (fmtC) {
221 case 'c':
222 /* single 8 bit char */
223 c = (char)va_arg(args, int32_t);
224 outputChar(c, outBuf, &outIx, capacity, indent);
225 break;
226
227 case 's':
228 /* char * string, NUL terminated. */
229 ptrArg = va_arg(args, char *);
230 outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent);
231 break;
232
233 case 'S':
234 /* char16_t * string, with length, len==-1 for NUL terminated. */
235 ptrArg = va_arg(args, char *); /* Ptr */
236 intArg =(int32_t)va_arg(args, int32_t); /* Length */
237 outputUString((const char16_t *)ptrArg, intArg, outBuf, &outIx, capacity, indent);
238 break;
239
240 case 'b':
241 /* 8 bit int */
242 intArg = va_arg(args, int);
243 outputHexBytes(intArg, 2, outBuf, &outIx, capacity);
244 break;
245
246 case 'h':
247 /* 16 bit int */
248 intArg = va_arg(args, int);
249 outputHexBytes(intArg, 4, outBuf, &outIx, capacity);
250 break;
251
252 case 'd':
253 /* 32 bit int */
254 intArg = va_arg(args, int);
255 outputHexBytes(intArg, 8, outBuf, &outIx, capacity);
256 break;
257
258 case 'l':
259 /* 64 bit long */
260 longArg = va_arg(args, int64_t);
261 outputHexBytes(longArg, 16, outBuf, &outIx, capacity);
262 break;
263
264 case 'p':
265 /* Pointers. */
266 ptrArg = va_arg(args, char *);
267 outputPtrBytes(ptrArg, outBuf, &outIx, capacity);
268 break;
269
270 case 0:
271 /* Single '%' at end of fmt string. Output as literal '%'.
272 * Back up index into format string so that the terminating NUL will be
273 * re-fetched in the outer loop, causing it to terminate.
274 */
275 outputChar('%', outBuf, &outIx, capacity, indent);
276 fmtIx--;
277 break;
278
279 case 'v':
280 {
281 /* Vector of values, e.g. %vh */
282 char vectorType;
283 int32_t vectorLen;
284 const char *i8Ptr;
285 int16_t *i16Ptr;
286 int32_t *i32Ptr;
287 int64_t *i64Ptr;
288 void **ptrPtr;
289 int32_t charsToOutput = 0;
290 int32_t i;
291
292 vectorType = fmt[fmtIx]; /* b, h, d, l, p, etc. */
293 if (vectorType != 0) {
294 fmtIx++;
295 }
296 i8Ptr = (const char *)va_arg(args, void*);
297 i16Ptr = (int16_t *)i8Ptr;
298 i32Ptr = (int32_t *)i8Ptr;
299 i64Ptr = (int64_t *)i8Ptr;
300 ptrPtr = (void **)i8Ptr;
301 vectorLen =(int32_t)va_arg(args, int32_t);
302 if (ptrPtr == nullptr) {
303 outputString("*NULL* ", outBuf, &outIx, capacity, indent);
304 } else {
305 for (i=0; i<vectorLen || vectorLen==-1; i++) {
306 switch (vectorType) {
307 case 'b':
308 charsToOutput = 2;
309 longArg = *i8Ptr++;
310 break;
311 case 'h':
312 charsToOutput = 4;
313 longArg = *i16Ptr++;
314 break;
315 case 'd':
316 charsToOutput = 8;
317 longArg = *i32Ptr++;
318 break;
319 case 'l':
320 charsToOutput = 16;
321 longArg = *i64Ptr++;
322 break;
323 case 'p':
324 charsToOutput = 0;
325 outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity);
326 longArg = *ptrPtr==nullptr? 0: 1; /* test for nullptr terminated array. */
327 ptrPtr++;
328 break;
329 case 'c':
330 charsToOutput = 0;
331 outputChar(*i8Ptr, outBuf, &outIx, capacity, indent);
332 longArg = *i8Ptr; /* for test for nullptr terminated array. */
333 i8Ptr++;
334 break;
335 case 's':
336 charsToOutput = 0;
337 outputString((const char *)*ptrPtr, outBuf, &outIx, capacity, indent);
338 outputChar('\n', outBuf, &outIx, capacity, indent);
339 longArg = *ptrPtr==nullptr? 0: 1; /* for test for nullptr term. array. */
340 ptrPtr++;
341 break;
342
343 case 'S':
344 charsToOutput = 0;
345 outputUString((const char16_t *)*ptrPtr, -1, outBuf, &outIx, capacity, indent);
346 outputChar('\n', outBuf, &outIx, capacity, indent);
347 longArg = *ptrPtr==nullptr? 0: 1; /* for test for nullptr term. array. */
348 ptrPtr++;
349 break;
350
351
352 }
353 if (charsToOutput > 0) {
354 outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity);
355 outputChar(' ', outBuf, &outIx, capacity, indent);
356 }
357 if (vectorLen == -1 && longArg == 0) {
358 break;
359 }
360 }
361 }
362 outputChar('[', outBuf, &outIx, capacity, indent);
363 outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity);
364 outputChar(']', outBuf, &outIx, capacity, indent);
365 }
366 break;
367
368
369 default:
370 /* %. in format string, where . is some character not in the set
371 * of recognized format chars. Just output it as if % wasn't there.
372 * (Covers "%%" outputting a single '%')
373 */
374 outputChar(fmtC, outBuf, &outIx, capacity, indent);
375 }
376 }
377 outputChar(0, outBuf, &outIx, capacity, indent); /* Make sure that output is NUL terminated */
378 return outIx + 1; /* outIx + 1 because outIx does not increment when outputting final NUL. */
379}
380
381
382
383
384U_CAPI int32_t U_EXPORT2
385utrace_format(char *outBuf, int32_t capacity,
386 int32_t indent, const char *fmt, ...) {
387 int32_t retVal;
388 va_list args;
389 va_start(args, fmt );
390 retVal = utrace_vformat(outBuf, capacity, indent, fmt, args);
391 va_end(args);
392 return retVal;
393}
394
395
396U_CAPI void U_EXPORT2
397utrace_setFunctions(const void *context,
398 UTraceEntry *e, UTraceExit *x, UTraceData *d) {
399 pTraceEntryFunc = e;
400 pTraceExitFunc = x;
401 pTraceDataFunc = d;
402 gTraceContext = context;
403}
404
405
406U_CAPI void U_EXPORT2
407utrace_getFunctions(const void **context,
408 UTraceEntry **e, UTraceExit **x, UTraceData **d) {
409 *e = pTraceEntryFunc;
410 *x = pTraceExitFunc;
411 *d = pTraceDataFunc;
412 *context = gTraceContext;
413}
414
415U_CAPI void U_EXPORT2
416utrace_setLevel(int32_t level) {
417 if (level < UTRACE_OFF) {
418 level = UTRACE_OFF;
419 }
420 if (level > UTRACE_VERBOSE) {
421 level = UTRACE_VERBOSE;
422 }
423 utrace_level = level;
424}
425
426U_CAPI int32_t U_EXPORT2
427utrace_getLevel() {
428 return utrace_level;
429}
430
431
432U_CFUNC UBool
433utrace_cleanup() {
434 pTraceEntryFunc = nullptr;
435 pTraceExitFunc = nullptr;
436 pTraceDataFunc = nullptr;
437 utrace_level = UTRACE_OFF;
438 gTraceContext = nullptr;
439 return true;
440}
441
442
443static const char * const
444trFnName[] = {
445 "u_init",
446 "u_cleanup",
447 nullptr
448};
449
450
451static const char * const
452trConvNames[] = {
453 "ucnv_open",
454 "ucnv_openPackage",
455 "ucnv_openAlgorithmic",
456 "ucnv_clone",
457 "ucnv_close",
458 "ucnv_flushCache",
459 "ucnv_load",
460 "ucnv_unload",
461 nullptr
462};
463
464
465static const char * const
466trCollNames[] = {
467 "ucol_open",
468 "ucol_close",
469 "ucol_strcoll",
470 "ucol_getSortKey",
471 "ucol_getLocale",
472 "ucol_nextSortKeyPart",
473 "ucol_strcollIter",
474 "ucol_openFromShortString",
475 "ucol_strcollUTF8",
476 nullptr
477};
478
479
480static const char* const
481trResDataNames[] = {
482 "resc",
483 "bundle-open",
484 "file-open",
485 "res-open",
486 nullptr
487};
488
489
490U_CAPI const char * U_EXPORT2
491utrace_functionName(int32_t fnNumber) {
492 if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) {
493 return trFnName[fnNumber];
494 } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) {
495 return trConvNames[fnNumber - UTRACE_CONVERSION_START];
496 } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){
497 return trCollNames[fnNumber - UTRACE_COLLATION_START];
498 } else if(UTRACE_UDATA_START <= fnNumber && fnNumber < UTRACE_RES_DATA_LIMIT){
499 return trResDataNames[fnNumber - UTRACE_UDATA_START];
500 } else {
501 return "[BOGUS Trace Function Number]";
502 }
503}
504
505