Home | History | Annotate | Download | only in common
      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 utrace_entry(int32_t fnNumber) {
     35     if (pTraceEntryFunc != NULL) {
     36         (*pTraceEntryFunc)(gTraceContext, fnNumber);
     37     }
     38 }
     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 utrace_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 
     82 U_CAPI void U_EXPORT2
     83 utrace_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 
     93 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     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 
    128 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     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   */
    139 static 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 
    158 static 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 
    172 static 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 
    191 U_CAPI int32_t U_EXPORT2
    192 utrace_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 
    385 U_CAPI int32_t U_EXPORT2
    386 utrace_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 
    397 U_CAPI void U_EXPORT2
    398 utrace_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 
    407 U_CAPI void U_EXPORT2
    408 utrace_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 
    416 U_CAPI void U_EXPORT2
    417 utrace_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 
    427 U_CAPI int32_t U_EXPORT2
    428 utrace_getLevel() {
    429     return utrace_level;
    430 }
    431 
    432 
    433 U_CFUNC UBool
    434 utrace_cleanup() {
    435     pTraceEntryFunc = NULL;
    436     pTraceExitFunc  = NULL;
    437     pTraceDataFunc  = NULL;
    438     utrace_level    = UTRACE_OFF;
    439     gTraceContext   = NULL;
    440     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 utrace_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