From ed4481bb589ee2ac29b8e42395f449be2e246b7b Mon Sep 17 00:00:00 2001 From: Urban Wallasch Date: Sat, 19 Oct 2019 12:22:02 +0200 Subject: [PATCH 1/1] * trace: initial commit --- trace/trace.c | 138 +++++++++++++++++++++++ trace/trace.h | 303 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+) create mode 100644 trace/trace.c create mode 100644 trace/trace.h diff --git a/trace/trace.c b/trace/trace.c new file mode 100644 index 0000000..05df17e --- /dev/null +++ b/trace/trace.c @@ -0,0 +1,138 @@ +/* + * trace.c + * + * Simple, non-exhaustive quick check for trace.h + * + * Build with: + * cc [-std=c11 [-Wpedantic]] -Wall -Wextra -DTRACE_ON -otrace trace.c + * + */ + +#include "trace.h" + +#include +#include + +int main(void) { + char c = 'A'; + + signed char sc = 'b'; + unsigned char uc = 'd'; + + wchar_t wc1 = L'\xFFFE'; + char16_t wc2 = u'\xFFFE'; + char32_t wc3 = U'\xFFFE'; + + short s = -512; + unsigned short us = 512; + + int i = -1024; + unsigned int ui = 2048; + + long l = -100000; + unsigned long ul = 200000; + + long long ll = -100000; + unsigned long long ull = 200000; + + size_t sz = 8888; + + float f = 2.7182; + double d = 3.1415; + long double ld = 123.456; + + void *pv = (void *)0xdeadbeef; + char *pc = "Konnichiwa"; + int *pi = &i; + unsigned *pui = &ui; + double *pd = &d; + + struct { int i; } blah = { 555 }, blubb; + +#if defined(__STRICT_ANSI__) + /* Arrays don't work with the extended pointer heuristic! */ + int arr[10] = {42, 43, 44}; + (void)TRACE( arr ); +#endif + + blubb = TRACE( blah ); + (void)TRACE( blubb.i ); + (void)TRACEX( &blubb ); + + (void)TRACE( 1+1 ); + (void)TRACE( c ); + c = TRACE( c+1 ); + (void)TRACE( c ); + (void)TRACE( c++ ); + (void)TRACE( c ); + (void)TRACE( ++c ); + + (void)TRACEX(wc1); + (void)TRACEX(wc2); + (void)TRACEX(wc3); + + + sc = TRACE( sc ) - 1; + (void)TRACEX( sc ); + uc = TRACE( uc ) + 1; + (void)TRACEX( uc ); + + s = TRACE( s ) - 1; + (void)TRACEX( s ); + us = TRACE( us ) + 1; + (void)TRACEX( us ); + + i = TRACE( i ) - 1; + (void)TRACEX( i ); + ui = TRACE( ui ) + 1; + (void)TRACEX( ui ); + + l = TRACE( l ) - 1; + (void)TRACEX( l ); + ul = TRACE( ul ) + 1; + (void)TRACEX( ul ); + + ll = TRACE( ll ) - 1; + (void)TRACEX( ll ); + ull = TRACE( ull ) + 1; + (void)TRACEX( ull ); + + sz = TRACE( sz ) + 1; + (void)TRACEX( sz ); + + f = TRACE( f ) + 1.1111; + (void)TRACEX( f ); + d = TRACE( d ) + 1.1111; + (void)TRACEX( d ); + ld = TRACE( ld ) + 1.1111; + (void)TRACEX( ld ); + + (void)TRACES("tuturu"); + + pv = TRACE( pv ); + pv = 0; + (void)TRACE(pv); + (void)TRACE((char *)pv); + (void)TRACEX((char *)pv); + (void)TRACES((char *)pv); + (void)TRACEXS((char *)pv); + + pc = TRACE( pc ); + (void)TRACES( pc ); + (void)TRACEX( pc ); + (void)TRACEXS( pc ); + + pi = TRACE( pi ); + (void)TRACE( pi ); + + pui = TRACE( pui ); + (void)TRACE( pui ); + (void)TRACEX( pui ); + (void)TRACES( pui ); + (void)TRACEXS( pui ); + + pd = TRACE( pd ); + (void)TRACE( pd ); + + return 0; +} diff --git a/trace/trace.h b/trace/trace.h new file mode 100644 index 0000000..e16fe83 --- /dev/null +++ b/trace/trace.h @@ -0,0 +1,303 @@ +/* + * trace.h + * + * Function-like macros to issue tracing information on expressions. + * + * TRACE(e) - default formatting + * TRACEX(e) - print unsigned and floating point values in hexadecimal + * TRACES(e) - treat character pointers like strings + * TRACEXS(e) - all of the above + * + * An expression `e´ passed as argument to one of the trace macros is + * delegated by a C11 _Generic selector to one of the variadic inline + * helper functions, based on its type. The macros evaluate their + * argument only once and resolve to the value of the expression. + * + * Expressions having a type not compatible with any of the explicit + * associations are reported as not recognized. + * + * Should build cleanly using + * cc [-std=c11 [-Wpedantic]] -Wall -Wextra -DTRACE_ON [...] + * with cc being one of: glibc-gcc, musl-gcc, glibc-clang, musl-clang. + * + * CAVEAT: + * In C11 mode any expression of array type decays to (and thus takes + * on the type of) a pointer to its first element; non-trivial pointers + * are listed as expressions of unrecognized type. + * When compiled without explicitly requesting C standard conformance + * using either gcc or clang, an additional heuristic will interpret + * any otherwise unrecognized suitably sized object as pointer. + * However, as a trade-off, any attempt to pass in an expression of + * array type will result in a compile time error. + * + */ + +#ifndef TRACE_H_ +#define TRACE_H_ + +#ifdef TRACE_ON + +#ifdef __cplusplus + #error "C++: Now there's your problem!" +#endif + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ < 201112L) + #error "At least C11 support is required!" +#endif + +#include +#include +#include +#include + +/* Attempt to infer integer promotion target types from implementation. */ +#if (INT_MAX) == (INT32_MAX) + #define I_PROM_ int32_t + #define U_PROM_ uint32_t +#elif (INT_MAX) == (INT64_MAX) + #define I_PROM_ int64_t + #define U_PROM_ uint64_t +#else + #error "Integer type promotion heuristic failed. Take five!" +#endif + +/* Adjust this to complement the libc printf %p conversion format! */ +#if defined(__QNXNTO__) + #define TRACE_PPFX_ "0x" +#else + #define TRACE_PPFX_ "" +#endif + +/* Helper macros. */ +#define TRACE_PRF_(...) ((void)fprintf(stderr, __VA_ARGS__)) +#define TRACE_LOC_(fi_, fn_, ln_) TRACE_PRF_("%s:%s@%d ", fi_, fn_, ln_) + +/* Generate helper functions for arithmetic types: */ +#define TRACE_AGEN_(t_, p_, s_, c_, f_) \ + static inline t_ trace_##s_##_(const char *fi, const char *fn, \ + int ln, const char *n, ...){ \ + t_ v; va_list a; va_start(a, n); v = va_arg(a, p_); va_end(a); \ + if (fi && fn) { \ + TRACE_LOC_(fi, fn, ln); \ + } \ + TRACE_PRF_("%s=<%s> "f_"\n", n, c_, v); \ + return v; \ + } + +TRACE_AGEN_(char, I_PROM_, ch, "char", "'%c'") +TRACE_AGEN_(int8_t, I_PROM_, i8, "i8", "%"PRId8) +TRACE_AGEN_(uint8_t, U_PROM_, u8, "u8", "%"PRIu8) +TRACE_AGEN_(uint8_t, U_PROM_, x8, "u8", "0x%02"PRIx8) +TRACE_AGEN_(int16_t, I_PROM_, i16, "i16", "%"PRId16) +TRACE_AGEN_(uint16_t, U_PROM_, u16, "u16", "%"PRIu16) +TRACE_AGEN_(uint16_t, U_PROM_, x16, "u16", "0x%04"PRIx16) +TRACE_AGEN_(int32_t, I_PROM_, i32, "i32", "%"PRId32) +TRACE_AGEN_(uint32_t, U_PROM_, u32, "u32", "%"PRIu32) +TRACE_AGEN_(uint32_t, U_PROM_, x32, "u32", "0x%08"PRIx32) +TRACE_AGEN_(int64_t, int64_t, i64, "i64", "%"PRId64) +TRACE_AGEN_(uint64_t, uint64_t, u64, "u64", "%"PRIu64) +TRACE_AGEN_(uint64_t, uint64_t, x64, "u64", "0x%016"PRIx64) +TRACE_AGEN_(long long, long long, ll, "ll", "%lld") +TRACE_AGEN_(unsigned long long, unsigned long long, ull, "ull", "%llu") +TRACE_AGEN_(unsigned long long, unsigned long long, xll, "ull", "0x%016llx") +TRACE_AGEN_(float, double, f, "float", "%f") +TRACE_AGEN_(float, double, xf, "float", "%a") +TRACE_AGEN_(double, double, d, "double", "%f") +TRACE_AGEN_(double, double, xd, "double", "%a") +TRACE_AGEN_(long double, long double, ld, "ldouble", "%Lf") +TRACE_AGEN_(long double, long double, xld, "ldouble", "%La") + +#undef TRACE_AGEN_ +#undef I_PROM_ +#undef U_PROM_ + +/* Generate helper functions for pointer types: */ +#define TRACE_PGEN_(t_, s_, c_) \ + static inline t_ trace_p##s_##_(const char *fi, const char *fn, \ + int ln, const char *n, ... ){ \ + t_ v; va_list a; va_start(a, n); v = va_arg(a, t_); va_end(a); \ + TRACE_LOC_(fi, fn, ln); \ + TRACE_PRF_("%s=<%s*> "TRACE_PPFX_"%p", n, c_, (void *)v); \ + if (v) { \ + TRACE_PRF_(", *"); trace_##s_##_(NULL, NULL, 0, n, *v); \ + } else { \ + TRACE_PRF_("\n"); \ + } \ + return v; \ + } + +TRACE_PGEN_(char *, ch, "char") +TRACE_PGEN_(int8_t *, i8, "i8") +TRACE_PGEN_(uint8_t *, u8, "u8") +TRACE_PGEN_(uint8_t *, x8, "u8") +TRACE_PGEN_(int16_t *, i16, "i16") +TRACE_PGEN_(uint16_t *, u16, "u16") +TRACE_PGEN_(uint16_t *, x16, "u16") +TRACE_PGEN_(int32_t *, i32, "i32") +TRACE_PGEN_(uint32_t *, u32, "u32") +TRACE_PGEN_(uint32_t *, x32, "u32") +TRACE_PGEN_(int64_t *, i64, "i64") +TRACE_PGEN_(uint64_t *, u64, "u64") +TRACE_PGEN_(uint64_t *, x64, "u64") +TRACE_PGEN_(long long *, ll, "ll") +TRACE_PGEN_(unsigned long long *, ull, "ull") +TRACE_PGEN_(unsigned long long *, xll, "ull") +TRACE_PGEN_(float *, f, "float") +TRACE_PGEN_(float *, xf, "float") +TRACE_PGEN_(double *, d, "double") +TRACE_PGEN_(double *, xd, "double") +TRACE_PGEN_(long double *, ld, "ldouble") +TRACE_PGEN_(long double *, xld, "ldouble") + +#undef TRACE_PGEN_ + +/* Void pointers and strings require special handling: */ +static inline void *trace_pvd_(const char *fi, const char *fn, int ln, + const char *n, ...){ + void *v; va_list a; va_start(a, n); v = va_arg(a, void *); va_end(a); + TRACE_LOC_(fi, fn, ln); + TRACE_PRF_("%s= "TRACE_PPFX_"%p\n", n, v); + return v; +} + +static inline void *trace_pstr_(const char *fi, const char *fn, int ln, + const char *n, ...){ + char *v; va_list a; va_start(a, n); v = va_arg(a, char *); va_end(a); + TRACE_LOC_(fi, fn, ln); + TRACE_PRF_("%s= "TRACE_PPFX_"%p, %s= \"%s\"\n", + n, (void *)v, n, v); + return v; +} + +/* Handle the default case depending on current compile mode. */ +#if !defined(__STRICT_ANSI__) && (defined(__GNUC__) || defined(__clang__)) + /* Use non-standard compiler extensions to guess arbitrary pointer + * types. Chokes on arrays! */ + #define TRACE_DFLT(fi_, fn_, ln_, x_) \ + ({ \ + const char *typ, *fmt; typeof(x_) y_ = x_; \ + if (sizeof(y_) == sizeof(void *)) { \ + typ = "?*?"; fmt = "%s=<%s> "TRACE_PPFX_"%p\n"; \ + } else { \ + typ = "???"; fmt = "%s=<%s>\n"; \ + } \ + TRACE_LOC_(fi_, fn_, ln_); TRACE_PRF_(fmt, #x_, typ, y_); \ + y_; \ + }) +#else + /* Use C standard conforming catch-all. */ + #define TRACE_DFLT(fi_, fn_, ln_, x_) \ + (TRACE_LOC_(fi_, fn_, ln_), TRACE_PRF_("%s=\n", #x_), x_) + #undef TRACE_PPFX_ +#endif + +/* + * Issue a tracing message about an expression, formatted as + * file:func@line: expr= value + * for arithmetic types, or + * file:func@line: expr= address, *exp= value + * for supported pointer types, or + * file:func@line: expr= + * for expressions of unrecognized type. + * + * In non-standard conforming mode anything that looks even + * remotely like some arbitrary pointer is printed as: + * file:func@line: expr= address + * + * Parameters: + * x_ the expression to be traced + * + * Returns: + * The value of x_. + */ +#define TRACE(x_) (_Generic( (x_), \ + /* arithmetic types */ \ + char: trace_ch_ (__FILE__, __func__, __LINE__, #x_, x_), \ + int8_t: trace_i8_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint8_t: trace_u8_ (__FILE__, __func__, __LINE__, #x_, x_), \ + int16_t: trace_i16_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint16_t: trace_u16_ (__FILE__, __func__, __LINE__, #x_, x_), \ + int32_t: trace_i32_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint32_t: trace_u32_ (__FILE__, __func__, __LINE__, #x_, x_), \ + int64_t: trace_i64_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint64_t: trace_u64_ (__FILE__, __func__, __LINE__, #x_, x_), \ + long long: trace_ll_ (__FILE__, __func__, __LINE__, #x_, x_), \ + unsigned long long: \ + trace_ull_ (__FILE__, __func__, __LINE__, #x_, x_), \ + float: trace_f_ (__FILE__, __func__, __LINE__, #x_, x_), \ + double: trace_d_ (__FILE__, __func__, __LINE__, #x_, x_), \ + long double: trace_ld_ (__FILE__, __func__, __LINE__, #x_, x_), \ + /* pointer types */ \ + void *: trace_pvd_ (__FILE__, __func__, __LINE__, #x_, x_), \ + char *: trace_pch_ (__FILE__, __func__, __LINE__, #x_, x_), \ + int8_t *: trace_pi8_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint8_t *: trace_pu8_ (__FILE__, __func__, __LINE__, #x_, x_), \ + int16_t *: trace_pi16_(__FILE__, __func__, __LINE__, #x_, x_), \ + uint16_t *: trace_pu16_(__FILE__, __func__, __LINE__, #x_, x_), \ + int32_t *: trace_pi32_(__FILE__, __func__, __LINE__, #x_, x_), \ + uint32_t *: trace_pu32_(__FILE__, __func__, __LINE__, #x_, x_), \ + int64_t *: trace_pi64_(__FILE__, __func__, __LINE__, #x_, x_), \ + uint64_t *: trace_pu64_(__FILE__, __func__, __LINE__, #x_, x_), \ + long long *: trace_pll_ (__FILE__, __func__, __LINE__, #x_, x_), \ + unsigned long long *: \ + trace_pull_(__FILE__, __func__, __LINE__, #x_, x_), \ + float *: trace_pf_ (__FILE__, __func__, __LINE__, #x_, x_), \ + double *: trace_pd_ (__FILE__, __func__, __LINE__, #x_, x_), \ + long double *: trace_pld_ (__FILE__, __func__, __LINE__, #x_, x_), \ + /* catch-all clause */ \ + default: TRACE_DFLT (__FILE__, __func__, __LINE__, x_) \ +)) + +/* + * Same as TRACE(), except unsigned and floating point values are + * printed in hexadecimal. + */ +#define TRACEX(x_) (_Generic( (x_), \ + /* arithmetic types */ \ + uint8_t: trace_x8_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint16_t: trace_x16_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint32_t: trace_x32_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint64_t: trace_x64_ (__FILE__, __func__, __LINE__, #x_, x_), \ + unsigned long long: \ + trace_xll_ (__FILE__, __func__, __LINE__, #x_, x_), \ + float: trace_xf_ (__FILE__, __func__, __LINE__, #x_, x_), \ + double: trace_xd_ (__FILE__, __func__, __LINE__, #x_, x_), \ + long double: trace_xld_ (__FILE__, __func__, __LINE__, #x_, x_), \ + /* pointer types */ \ + uint8_t *: trace_px8_ (__FILE__, __func__, __LINE__, #x_, x_), \ + uint16_t *: trace_px16_(__FILE__, __func__, __LINE__, #x_, x_), \ + uint32_t *: trace_px32_(__FILE__, __func__, __LINE__, #x_, x_), \ + uint64_t *: trace_px64_(__FILE__, __func__, __LINE__, #x_, x_), \ + unsigned long long *: \ + trace_pxll_(__FILE__, __func__, __LINE__, #x_, x_), \ + float *: trace_pxf_ (__FILE__, __func__, __LINE__, #x_, x_), \ + double *: trace_pxd_ (__FILE__, __func__, __LINE__, #x_, x_), \ + long double *: trace_pxld_(__FILE__, __func__, __LINE__, #x_, x_), \ + /* catch-all clause */ \ + default: (TRACE(x_)) \ +)) + +/* + * Same as TRACE(), except char pointers are printed as strings. + */ +#define TRACES(x_) (_Generic( (x_), \ + char *: trace_pstr_(__FILE__, __func__, __LINE__, #x_, x_), \ + default: (TRACE(x_)) \ +)) + +/* + * Same as TRACEX(), except char pointers are printed as strings. + */ +#define TRACEXS(x_) (_Generic( (x_), \ + char *: trace_pstr_(__FILE__, __func__, __LINE__, #x_, x_), \ + default: (TRACEX(x_)) \ +)) + +#else + #define TRACE(x_) (x_) + #define TRACEX(x_) (x_) + #define TRACES(x_) (x_) + #define TRACEXS(x_) (x_) +#endif //def TRACE_ON + +#endif //ndef TRACE_H_ -- 2.30.2