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 = NULL;
22static UTraceExit      *pTraceExitFunc  = NULL;
23static UTraceData      *pTraceDataFunc  = NULL;
24static const void      *gTraceContext   = NULL;
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 != NULL) {
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 != NULL) {
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            U_ASSERT(FALSE);
71            fmt = gExitFmt;
72        }
73
74        va_start(args, returnType);
75        (*pTraceExitFunc)(gTraceContext, fnNumber, fmt, args);
76        va_end(args);
77    }
78}
79
80
81
82U_CAPI void U_EXPORT2
83utrace_data(int32_t fnNumber, int32_t level, const char *fmt, ...) {
84    if (pTraceDataFunc != NULL) {
85           va_list args;
86           va_start(args, fmt );
87           (*pTraceDataFunc)(gTraceContext, fnNumber, level, fmt, args);
88           va_end(args);
89    }
90}
91
92
93static 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    if (*outIx==0 ||   /* case 1. */
105        (c!='\n' && c!=0 && *outIx < capacity && outBuf[(*outIx)-1]=='\n') ||  /* case 2. */
106        (c=='\n' && *outIx>=capacity))    /* case 3 */
107    {
108        /* At the start of a line.  Indent. */
109        for(i=0; i<indent; i++) {
110            if (*outIx < capacity) {
111                outBuf[*outIx] = ' ';
112            }
113            (*outIx)++;
114        }
115    }
116
117    if (*outIx < capacity) {
118        outBuf[*outIx] = c;
119    }
120    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        (*outIx)++;
125    }
126}
127
128static 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    for  (shiftCount=(charsToOutput-1)*4; shiftCount >= 0; shiftCount-=4) {
133        char c = gHexChars[(val >> shiftCount) & 0xf];
134        outputChar(c, outBuf, outIx, capacity, 0);
135    }
136}
137
138/* Output a pointer value in hex.  Work with any size of pointer   */
139static void outputPtrBytes(void *val, char *outBuf, int32_t *outIx, int32_t capacity) {
140    uint32_t  i;
141    int32_t  incVal = 1;              /* +1 for big endian, -1 for little endian          */
142    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    incVal = -1;
147    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    for (i=0; i<sizeof(void *); i++) {
153        outputHexBytes(*p, 2, outBuf, outIx, capacity);
154        p += incVal;
155    }
156}
157
158static void outputString(const char *s, char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
159    int32_t i = 0;
160    char    c;
161    if (s==NULL) {
162        s = "*NULL*";
163    }
164    do {
165        c = s[i++];
166        outputChar(c, outBuf, outIx, capacity, indent);
167    } while (c != 0);
168}
169
170
171
172static void outputUString(const UChar *s, int32_t len,
173                          char *outBuf, int32_t *outIx, int32_t capacity, int32_t indent) {
174    int32_t i = 0;
175    UChar   c;
176    if (s==NULL) {
177        outputString(NULL, outBuf, outIx, capacity, indent);
178        return;
179    }
180
181    for (i=0; i<len || len==-1; i++) {
182        c = s[i];
183        outputHexBytes(c, 4, outBuf, outIx, capacity);
184        outputChar(' ', outBuf, outIx, capacity, indent);
185        if (len == -1 && c==0) {
186            break;
187        }
188    }
189}
190
191U_CAPI int32_t U_EXPORT2
192utrace_vformat(char *outBuf, int32_t capacity, int32_t indent, const char *fmt, va_list args) {
193    int32_t   outIx  = 0;
194    int32_t   fmtIx  = 0;
195    char      fmtC;
196    char      c;
197    int32_t   intArg;
198    int64_t   longArg = 0;
199    char      *ptrArg;
200
201    /*   Loop runs once for each character in the format string.
202     */
203    for (;;) {
204        fmtC = fmt[fmtIx++];
205        if (fmtC != '%') {
206            /* Literal character, not part of a %sequence.  Just copy it to the output. */
207            outputChar(fmtC, outBuf, &outIx, capacity, indent);
208            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                break;
214            }
215            continue;
216        }
217
218        /* We encountered a '%'.  Pick up the following format char */
219        fmtC = fmt[fmtIx++];
220
221        switch (fmtC) {
222        case 'c':
223            /* single 8 bit char   */
224            c = (char)va_arg(args, int32_t);
225            outputChar(c, outBuf, &outIx, capacity, indent);
226            break;
227
228        case 's':
229            /* char * string, null terminated.  */
230            ptrArg = va_arg(args, char *);
231            outputString((const char *)ptrArg, outBuf, &outIx, capacity, indent);
232            break;
233
234        case 'S':
235            /* UChar * string, with length, len==-1 for null terminated. */
236            ptrArg = va_arg(args, char *);             /* Ptr    */
237            intArg =(int32_t)va_arg(args, int32_t);    /* Length */
238            outputUString((const UChar *)ptrArg, intArg, outBuf, &outIx, capacity, indent);
239            break;
240
241        case 'b':
242            /*  8 bit int  */
243            intArg = va_arg(args, int);
244            outputHexBytes(intArg, 2, outBuf, &outIx, capacity);
245            break;
246
247        case 'h':
248            /*  16 bit int  */
249            intArg = va_arg(args, int);
250            outputHexBytes(intArg, 4, outBuf, &outIx, capacity);
251            break;
252
253        case 'd':
254            /*  32 bit int  */
255            intArg = va_arg(args, int);
256            outputHexBytes(intArg, 8, outBuf, &outIx, capacity);
257            break;
258
259        case 'l':
260            /*  64 bit long  */
261            longArg = va_arg(args, int64_t);
262            outputHexBytes(longArg, 16, outBuf, &outIx, capacity);
263            break;
264
265        case 'p':
266            /*  Pointers.   */
267            ptrArg = va_arg(args, char *);
268            outputPtrBytes(ptrArg, outBuf, &outIx, capacity);
269            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            outputChar('%', outBuf, &outIx, capacity, indent);
277            fmtIx--;
278            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                int32_t   charsToOutput = 0;
291                int32_t   i;
292
293                vectorType = fmt[fmtIx];    /* b, h, d, l, p, etc. */
294                if (vectorType != 0) {
295                    fmtIx++;
296                }
297                i8Ptr = (const char *)va_arg(args, void*);
298                i16Ptr = (int16_t *)i8Ptr;
299                i32Ptr = (int32_t *)i8Ptr;
300                i64Ptr = (int64_t *)i8Ptr;
301                ptrPtr = (void **)i8Ptr;
302                vectorLen =(int32_t)va_arg(args, int32_t);
303                if (ptrPtr == NULL) {
304                    outputString("*NULL* ", outBuf, &outIx, capacity, indent);
305                } else {
306                    for (i=0; i<vectorLen || vectorLen==-1; i++) {
307                        switch (vectorType) {
308                        case 'b':
309                            charsToOutput = 2;
310                            longArg = *i8Ptr++;
311                            break;
312                        case 'h':
313                            charsToOutput = 4;
314                            longArg = *i16Ptr++;
315                            break;
316                        case 'd':
317                            charsToOutput = 8;
318                            longArg = *i32Ptr++;
319                            break;
320                        case 'l':
321                            charsToOutput = 16;
322                            longArg = *i64Ptr++;
323                            break;
324                        case 'p':
325                            charsToOutput = 0;
326                            outputPtrBytes(*ptrPtr, outBuf, &outIx, capacity);
327                            longArg = *ptrPtr==NULL? 0: 1;    /* test for null terminated array. */
328                            ptrPtr++;
329                            break;
330                        case 'c':
331                            charsToOutput = 0;
332                            outputChar(*i8Ptr, outBuf, &outIx, capacity, indent);
333                            longArg = *i8Ptr;    /* for test for null terminated array. */
334                            i8Ptr++;
335                            break;
336                        case 's':
337                            charsToOutput = 0;
338                            outputString((const char *)*ptrPtr, outBuf, &outIx, capacity, indent);
339                            outputChar('\n', outBuf, &outIx, capacity, indent);
340                            longArg = *ptrPtr==NULL? 0: 1;   /* for test for null term. array. */
341                            ptrPtr++;
342                            break;
343
344                        case 'S':
345                            charsToOutput = 0;
346                            outputUString((const UChar *)*ptrPtr, -1, outBuf, &outIx, capacity, indent);
347                            outputChar('\n', outBuf, &outIx, capacity, indent);
348                            longArg = *ptrPtr==NULL? 0: 1;   /* for test for null term. array. */
349                            ptrPtr++;
350                            break;
351
352
353                        }
354                        if (charsToOutput > 0) {
355                            outputHexBytes(longArg, charsToOutput, outBuf, &outIx, capacity);
356                            outputChar(' ', outBuf, &outIx, capacity, indent);
357                        }
358                        if (vectorLen == -1 && longArg == 0) {
359                            break;
360                        }
361                    }
362                }
363                outputChar('[', outBuf, &outIx, capacity, indent);
364                outputHexBytes(vectorLen, 8, outBuf, &outIx, capacity);
365                outputChar(']', outBuf, &outIx, capacity, indent);
366            }
367            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             outputChar(fmtC, outBuf, &outIx, capacity, indent);
376        }
377    }
378    outputChar(0, outBuf, &outIx, capacity, indent);  /* Make sure that output is null terminated  */
379    return outIx + 1;     /* outIx + 1 because outIx does not increment when outputing final null. */
380}
381
382
383
384
385U_CAPI int32_t U_EXPORT2
386utrace_format(char *outBuf, int32_t capacity,
387                int32_t indent, const char *fmt,  ...) {
388    int32_t retVal;
389    va_list args;
390    va_start(args, fmt );
391    retVal = utrace_vformat(outBuf, capacity, indent, fmt, args);
392    va_end(args);
393    return retVal;
394}
395
396
397U_CAPI void U_EXPORT2
398utrace_setFunctions(const void *context,
399                    UTraceEntry *e, UTraceExit *x, UTraceData *d) {
400    pTraceEntryFunc = e;
401    pTraceExitFunc  = x;
402    pTraceDataFunc  = d;
403    gTraceContext   = context;
404}
405
406
407U_CAPI void U_EXPORT2
408utrace_getFunctions(const void **context,
409                    UTraceEntry **e, UTraceExit **x, UTraceData **d) {
410    *e = pTraceEntryFunc;
411    *x = pTraceExitFunc;
412    *d = pTraceDataFunc;
413    *context = gTraceContext;
414}
415
416U_CAPI void U_EXPORT2
417utrace_setLevel(int32_t level) {
418    if (level < UTRACE_OFF) {
419        level = UTRACE_OFF;
420    }
421    if (level > UTRACE_VERBOSE) {
422        level = UTRACE_VERBOSE;
423    }
424    utrace_level = level;
425}
426
427U_CAPI int32_t U_EXPORT2
428utrace_getLevel() {
429    return utrace_level;
430}
431
432
433U_CFUNC UBool
434utrace_cleanup() {
435    pTraceEntryFunc = NULL;
436    pTraceExitFunc  = NULL;
437    pTraceDataFunc  = NULL;
438    utrace_level    = UTRACE_OFF;
439    gTraceContext   = NULL;
440    return TRUE;
441}
442
443
444static const char * const
445trFnName[] = {
446    "u_init",
447    "u_cleanup",
448    NULL
449};
450
451
452static const char * const
453trConvNames[] = {
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
466static const char * const
467trCollNames[] = {
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
481U_CAPI const char * U_EXPORT2
482utrace_functionName(int32_t fnNumber) {
483    if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) {
484        return trFnName[fnNumber];
485    } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) {
486        return trConvNames[fnNumber - UTRACE_CONVERSION_START];
487    } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){
488        return trCollNames[fnNumber - UTRACE_COLLATION_START];
489    } else {
490        return "[BOGUS Trace Function Number]";
491    }
492}
493
494