driver/tracing.h (378 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ #ifndef __TRACING_H__ #define __TRACING_H__ #include <inttypes.h> #include <stdio.h> #include <time.h> #include "log.h" #define TRACE_LOG_LEVEL LOG_LEVEL_DBG /* name of the buffer into which theh trace messages get printed */ #define _BUFF _trace_buff /* size of the trace buffer */ #define TBUFF_SIZE (ESODBC_LOG_BUF_SIZE - /*final `.`*/1) /* room left in the buffer */ #define _AVAIL(_ps) ((int)(sizeof(_BUFF)/sizeof(_BUFF[0])) - _ps) /* check if buffer limit has been reached */ #define _CHECK_WRITE_(/*written*/_n, /*position*/_ps) \ if (_n < 0) { /* printing failed */ \ assert(_ps < _AVAIL(0)); \ _BUFF[_ps] = '\0'; /* if previously usable, make sure it's NTS'd */\ break; \ } else if (_AVAIL(_ps) <= _ps + _n) { /* buffer end reached */ \ _ps = _AVAIL(0); \ break; \ } else { \ _ps += _n; \ } /*INDENT-OFF*/ /* TODO: the SQL[U]LEN for _WIN32 */ #define _PRINT_PARAM_VAL(type, val, prec) \ do { \ int _prec; \ wchar_t *_w; \ if (_AVAIL(_ps) <= 0) { \ break; \ } \ switch(type) { \ /* * numeric pointers */ \ /* SQLNUMERIC/SQLDATE/SQLCHAR/etc. = unsigned char */ \ /* SQLSCHAR = char */ \ case 'c': /* char signed */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hhd", \ val ? *(char *)(uintptr_t)val : 0); \ break; \ case 'C': /* char unsigned */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hhu", \ val ? *(unsigned char *)(uintptr_t)val : 0); \ break; \ /* SQL[U]SMALLINT = [unsigned] short */ \ case 't': /* short signed */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hd", \ val ? *(short *)(uintptr_t)val : 0); \ break; \ case 'T': /* short unsigned */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hu", \ val ? *(unsigned short *)(uintptr_t)val : 0); \ break; \ /* SQL[U]INTEGER = [unsigned] long */ \ case 'g': /* long signed */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%ld", \ val ? *(long *)(uintptr_t)val : 0); \ break; \ case 'G': /* long unsigned */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lu", \ val ? *(unsigned long *)(uintptr_t)val : 0); \ break; \ /* SQL[U]LEN = [unsigned] long OR [u]int64_t (64b _WIN32) */ \ case 'n': /* long/int64_t signed */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lld", \ val ? (int64_t)*(SQLLEN *)(uintptr_t)val : 0); \ break; \ case 'N': /* long/int64_t unsigned */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%llu", \ val ? (uint64_t)*(SQLULEN *)(uintptr_t)val : 0); \ break; \ /* * non-numeric pointers */ \ case 'p': /* void* */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "@0x%p", \ (void *)(uintptr_t)val); \ break; \ /* TODO: support for (buffer*, max, written*) pattern */ \ case 'W': /* wchar_t* with precision (always following param) */ \ if (val) { \ _w = (wchar_t *)(uintptr_t)val; \ _prec = (int)(intptr_t)prec; \ _prec = (_prec == SQL_NTS) ? (int)wcslen(_w) : _prec; \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), \ "[%d] `" LWPDL "`", _prec, _prec, _w); \ } else { \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), TS_NULL); \ } \ break; \ case 'w': /* NTS wchar_t* */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "[%zu] `" LWPD "`", \ val ? wcslen((wchar_t *)(uintptr_t)val) : 0, \ val ? (wchar_t *)(uintptr_t)val : TWS_NULL); \ break; \ case 's': /* NTS char* */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "[%zu] `" LCPD "`", \ val ? strlen((char *)(uintptr_t)val) : 0, \ val ? (char *)(uintptr_t)val : TS_NULL); \ break; \ /* * imediat values */ \ /* long longs */ \ case 'z': /* long long signed (SQLLEN) */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lld", \ (long long)(intptr_t)val); \ break; \ case 'Z': /* long long unsigned (SQLULEN) */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%llu", \ (unsigned long long)(uintptr_t)val); \ break; \ /* longs */ \ case 'l': /* long signed (SQLINTEGER) */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%ld", \ (long)(intptr_t)val); \ break; \ case 'L': /* long unsigned (SQLUINTEGER) */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%lu", \ (unsigned long)(uintptr_t)val); \ break; \ /* ints */ \ case 'd': /* int signed */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%d", \ (int)(intptr_t)val); \ break; \ case 'u': /* int unsigned */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%u", \ (unsigned)(uintptr_t)val); \ break; \ case 'h': /* short signed (SQLSMALLINT) */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hd", \ (short)(intptr_t)val); \ break; \ case 'H': /* short unsigned (SQLUSMALLINT) */ \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%hu", \ (unsigned short)(uintptr_t)val); \ break; \ default: \ _n = snprintf(_BUFF+_ps, _AVAIL(_ps), "BUG! unknown type: %d",\ type); \ break; \ } \ _CHECK_WRITE_(_n, _ps) \ } while (0) /*INDENT-ON*/ #define _IS_PTR(type, _is_ptr) \ do {\ switch (type) { \ case 'l': \ case 'L': \ case 'd': \ case 'u': \ case 'h': \ case 'H': \ _is_ptr = FALSE; \ break; \ default: \ _is_ptr = TRUE; \ } \ } while (0) #define _PRINT_PARAM(type, param, prec, add_comma) \ do { \ BOOL _is_ptr; \ _IS_PTR(type, _is_ptr); \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), "%s%s%s=", \ add_comma ? ", " : "", _is_ptr ? "*" : "", # param); \ if (0 < _n) /* no proper err check */ \ _ps += _n; \ _PRINT_PARAM_VAL(type, param, prec); \ } while (0) #define _IN 0 #define _TRACE_IN "ENTER: " #define _OUT 1 #define _TRACE_OUT "EXIT: " /* account for the total time (across all threads!) spent within the driver: * the time the ODBC API calls take to be serviced. */ #ifdef WITH_OAPI_TIMING extern volatile LONG64 api_ticks; extern thread_local clock_t in_ticks; # define OAPI_TIMING(inout) \ do { \ if (inout == _IN) { \ in_ticks = clock(); \ } else { /* _OUT */ \ clock_t out_ticks = clock(); \ if (out_ticks != (clock_t)-1 && in_ticks < out_ticks) { \ InterlockedExchangeAdd64(&api_ticks, out_ticks - in_ticks); \ } \ } \ } while (0) #else /* WITH_API_TIMING */ # define OAPI_TIMING #endif /* WITH_API_TIMING */ #define _TRACE_HEADER_(inout, hnd) \ char _BUFF[TBUFF_SIZE]; \ int _ps = 0, _n; \ esodbc_filelog_st *_log; \ /* no accounting of the "out" tracing, but that should be OK, this being * most useful with release builds on non-dbg logging (when the rest of * the tracing code is skipped anyways) */\ OAPI_TIMING(inout); \ _log = (hnd && HDRH(hnd)->log) ? HDRH(hnd)->log : _gf_log; \ if ((! _log) || (_log->level < TRACE_LOG_LEVEL)) { \ /* skip all the printing as early as possible */ \ break; \ } \ _n = snprintf(_BUFF + _ps, _AVAIL(_ps), inout ? _TRACE_OUT : _TRACE_IN); \ _CHECK_WRITE_(_n, _ps) #define _TRACE_FOOTER_ \ _esodbc_log(_log, TRACE_LOG_LEVEL, /*werr*/0, \ __func__, __FILE__, __LINE__, "%s.", _BUFF); #define TRACE1(inout, hnd, fmt, p0) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, SQL_NTS, 0); \ _TRACE_FOOTER_ \ } while(0) #define TRACE2(inout, hnd, fmt, p0, p1) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE3(inout, hnd, fmt, p0, p1, p2) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE4(inout, hnd, fmt, p0, p1, p2, p3) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE5(inout, hnd, fmt, p0, p1, p2, p3, p4) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE6(inout, hnd, fmt, p0, p1, p2, p3, p4, p5) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE7(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE8(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE9(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, p8, 1); \ _PRINT_PARAM(fmt[8], p8, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE10(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, p8, 1); \ _PRINT_PARAM(fmt[8], p8, p9, 1); \ _PRINT_PARAM(fmt[9], p9, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE11(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, p8, 1); \ _PRINT_PARAM(fmt[8], p8, p9, 1); \ _PRINT_PARAM(fmt[9], p9, p10, 1); \ _PRINT_PARAM(fmt[10], p10, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE12(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, \ p11) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, p8, 1); \ _PRINT_PARAM(fmt[8], p8, p9, 1); \ _PRINT_PARAM(fmt[9], p9, p10, 1); \ _PRINT_PARAM(fmt[10], p10, p11, 1); \ _PRINT_PARAM(fmt[11], p11, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) /*INDENT-OFF*/ //astyle trips on these following two defs #define TRACE13(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, \ p11, p12) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, p8, 1); \ _PRINT_PARAM(fmt[8], p8, p9, 1); \ _PRINT_PARAM(fmt[9], p9, p10, 1); \ _PRINT_PARAM(fmt[10], p10, p11, 1); \ _PRINT_PARAM(fmt[11], p11, p12, 1); \ _PRINT_PARAM(fmt[12], p12, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) #define TRACE14(inout, hnd, fmt, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, \ p11, p12, p13) \ do { \ _TRACE_HEADER_(inout, hnd) \ _PRINT_PARAM(fmt[0], p0, p1, 0); \ _PRINT_PARAM(fmt[1], p1, p2, 1); \ _PRINT_PARAM(fmt[2], p2, p3, 1); \ _PRINT_PARAM(fmt[3], p3, p4, 1); \ _PRINT_PARAM(fmt[4], p4, p5, 1); \ _PRINT_PARAM(fmt[5], p5, p6, 1); \ _PRINT_PARAM(fmt[6], p6, p7, 1); \ _PRINT_PARAM(fmt[7], p7, p8, 1); \ _PRINT_PARAM(fmt[8], p8, p9, 1); \ _PRINT_PARAM(fmt[9], p9, p10, 1); \ _PRINT_PARAM(fmt[10], p10, p11, 1); \ _PRINT_PARAM(fmt[11], p11, p12, 1); \ _PRINT_PARAM(fmt[12], p12, p13, 1); \ _PRINT_PARAM(fmt[13], p13, SQL_NTS, 1); \ _TRACE_FOOTER_ \ } while(0) /*INDENT-ON*/ #endif /* __TRACING_H__ */ /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */