Home | History | Annotate | Download | only in common
      1 /*
      2 *******************************************************************************
      3 *   Copyright (C) 2003-2008, 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 
     20 static UTraceEntry     *pTraceEntryFunc = NULL;
     21 static UTraceExit      *pTraceExitFunc  = NULL;
     22 static UTraceData      *pTraceDataFunc  = NULL;
     23 static const void      *gTraceContext   = NULL;
     24 
     25 U_EXPORT int32_t
     26 utrace_level = UTRACE_ERROR;
     27 
     28 U_CAPI void U_EXPORT2
     29 utrace_entry(int32_t fnNumber) {
     30     if (pTraceEntryFunc != NULL) {
     31         (*pTraceEntryFunc)(gTraceContext, fnNumber);
     32     }
     33 }
     34 
     35 
     36 static const char gExitFmt[]             = "Returns.";
     37 static const char gExitFmtValue[]        = "Returns %d.";
     38 static const char gExitFmtStatus[]       = "Returns.  Status = %d.";
     39 static const char gExitFmtValueStatus[]  = "Returns %d.  Status = %d.";
     40 static const char gExitFmtPtrStatus[]    = "Returns %d.  Status = %p.";
     41 
     42 U_CAPI void U_EXPORT2
     43 utrace_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 
     77 U_CAPI void U_EXPORT2
     78 utrace_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 
     88 static 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 
    123 static 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   */
    134 static 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 
    153 static 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 
    167 static 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 
    186 U_CAPI int32_t U_EXPORT2
    187 utrace_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 
    380 U_CAPI int32_t U_EXPORT2
    381 utrace_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 
    392 U_CAPI void U_EXPORT2
    393 utrace_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 
    402 U_CAPI void U_EXPORT2
    403 utrace_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 
    411 U_CAPI void U_EXPORT2
    412 utrace_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 
    422 U_CAPI int32_t U_EXPORT2
    423 utrace_getLevel() {
    424     return utrace_level;
    425 }
    426 
    427 
    428 U_CFUNC UBool
    429 utrace_cleanup() {
    430     pTraceEntryFunc = NULL;
    431     pTraceExitFunc  = NULL;
    432     pTraceDataFunc  = NULL;
    433     utrace_level    = UTRACE_OFF;
    434     gTraceContext   = NULL;
    435     return TRUE;
    436 }
    437 
    438 
    439 static const char * const
    440 trFnName[] = {
    441     "u_init",
    442     "u_cleanup",
    443     NULL
    444 };
    445 
    446 
    447 static const char * const
    448 trConvNames[] = {
    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 
    461 static const char * const
    462 trCollNames[] = {
    463     "ucol_open",
    464     "ucol_close",
    465     "ucol_strcoll",
    466     "ucol_getSortKey",
    467     "ucol_getLocale",
    468     "ucol_nextSortKeyPart",
    469     "ucol_strcollIter",
    470     NULL
    471 };
    472 
    473 
    474 U_CAPI const char * U_EXPORT2
    475 utrace_functionName(int32_t fnNumber) {
    476     if(UTRACE_FUNCTION_START <= fnNumber && fnNumber < UTRACE_FUNCTION_LIMIT) {
    477         return trFnName[fnNumber];
    478     } else if(UTRACE_CONVERSION_START <= fnNumber && fnNumber < UTRACE_CONVERSION_LIMIT) {
    479         return trConvNames[fnNumber - UTRACE_CONVERSION_START];
    480     } else if(UTRACE_COLLATION_START <= fnNumber && fnNumber < UTRACE_COLLATION_LIMIT){
    481         return trCollNames[fnNumber - UTRACE_COLLATION_START];
    482     } else {
    483         return "[BOGUS Trace Function Number]";
    484     }
    485 }
    486 
    487