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