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