Coverage Report

Created: 2025-06-24 06:43

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