tracing: Merge TracingSpanFields macro into TracingSpan

Leverages the VA_OPT macro library, which is a polyfill for __VA_OPT__
on non-C++20 platforms, to enable TracingSpan to support optional fields.

Source: https://github.com/willwray/VA_OPT
License: Boost Software License, Version 1.0
This commit is contained in:
Jack Grigg 2020-12-24 15:01:49 +00:00
parent e5a5bc5b83
commit 37b42d8a41
4 changed files with 170 additions and 29 deletions

View File

@ -124,6 +124,10 @@ Files: src/crypto/ctaes/*
Copyright: Copyright (c) 2016 Pieter Wuille
License: Expat
Files: src/rust/include/rust/VA_OPT.hpp
Copyright: Copyright (c) 2019 Will Wray
License: Boost-Software-License-1.0
Files: src/rust/include/tracing.h
src/rust/src/tracing_ffi.rs
Copyright: Copyright (c) 2020 Jack Grigg

View File

@ -2224,11 +2224,11 @@ CNode::~CNode()
void CNode::ReloadTracingSpan()
{
if (fLogIPs) {
span = TracingSpanFields("info", "net", "Peer",
span = TracingSpan("info", "net", "Peer",
"id", idStr.c_str(),
"addr", addrName.c_str());
} else {
span = TracingSpanFields("info", "net", "Peer",
span = TracingSpan("info", "net", "Peer",
"id", idStr.c_str());
}
}

View File

@ -0,0 +1,152 @@
// Copyright (c) 2019 Will Wray https://keybase.io/willwray
//
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
//
// Repo: https://github.com/willwray/VA_OPT
#pragma once
/*
VA_OPT.hpp
==========
Preprocessor utilities for testing emptiness of macro arguments
and for conditional expansion based on the emptiness of ARGS.
VA_OPT_SUPPORT(?)
1 if __VA_OPT__ support is detected else 0 (see platform note).
C++20's __VA_OPT__ finally provides a reliable test for empty ARGS.
The following macros use __VA_OPT__, if detected, otherwise they
provide 'polyfill' fallbacks (though with some failing edge cases).
IS_EMPTY(...)
1 if the ... ARGS is empty else 0.
IFN(...)
If ... ARGS are not empty then a trailing 'paren expression' (X)
is deparenthesized to X else the trailing (X) term is consumed.
E.g. IFN()(N) vanishes, while IFN(NotEmpty)(N) -> N
In other words, IFN(ARGS) is like __VA_OPT__, but with explicit
(ARGS) in place of an implicit __VA_ARGS__ check.
IFE(...)
If ... ARGS is empty expand trailing 'paren expression' (X) to X
else if ARGS are not empty consume the trailing paren expression.
E.g. IFE(NotEmpty)(E) vanishes, while IFE()(E) -> E
IFNE(...)(N,E...)
If ... ARGS are not empty expands to N else expands to E...
E.g. IFNE(ARG)(X,()) is equivalent to IFN(ARG)(X)IFE(ARG)(())
both put back a terminating () removed by the outer macro call.
Without VA_OPT_SUPPORT these 'emptiness' macros are not perfect;
IS_EMPTY, IFN, IFE, IFNE may cause a compile error ('too few args')
if the argument is a function-like macro name that expects ARG(s).
IBP(...)
IS_BEGIN_PARENS macro to test if an argument is parenthesised:
1 if ... ARGS begins with a 'paren expression' else 0.
Platform note: Current Sept 2019 __VA_OPT__ support:
-------------
Clang -std=c++2a enables it. GCC has it enabled without -std=c++2a
but warns "__VA_OPT__ is not available until C++2a" if another -std
flag is supplied along with -pedantic (dont know how to supress it).
MSVC TBD
Credits
-------
Props to pre-pro pioneers, particularly Paul Mensonides.
The 'emptiness' methods are adapted from BOOST_VMD_IS_EMPTY which,
. in turn, depends on BOOST Preprocessor's BOOST_PP_IS_BEGIN_PARENS
(adapted and exposed here as IBP 'Is Begin Parens'):
www.boost.org/doc/libs/1_71_0/libs/vmd
www.boost.org/doc/libs/1_71_0/libs/preprocessor
*/
#define VA_ARG1(A0,A1,...) A1
// VA_EMPTY works only if __VA_OPT__ is supported, else always -> 1
#define VA_EMPTY(...) VA_ARG1(__VA_OPT__(,)0,1,)
// VA_OPT_SUPPORT helper macro for __VA_OPT__ feature detection.
// Adapted from https://stackoverflow.com/a/48045656/7443483
// Use as #if VA_OPT_SUPPORT(?)
#define VA_OPT_SUPPORT ! VA_EMPTY
#if VA_OPT_SUPPORT(?)
# define IS_EMPTY(...) VA_EMPTY(__VA_ARGS__)
# define IFN(...) VA_EAT __VA_OPT__(()VA_IDENT)
# define IFE(...) VA_IDENT __VA_OPT__(()VA_EAT)
# define IFNE(...) VA_ARGTAIL __VA_OPT__((,)VA_ARG0)
#else
# define IS_EMPTY(...) IFP(IBP(__VA_ARGS__))(IE_GEN_0,IE_IBP)(__VA_ARGS__)
# define IFN(...) IFP(IBP(__VA_ARGS__))(GEN_IDENT,EAT_OR_IDENT)(__VA_ARGS__)
# define IFE(...) IFP(IBP(__VA_ARGS__))(GEN_EAT,IDENT_OR_EAT)(__VA_ARGS__)
# define IFNE(...) IFP(IBP(__VA_ARGS__))(GEN_ARGTAIL,ARG0_OR_TAIL)(__VA_ARGS__)
#endif
#define VA_EAT(...)
#define VA_IDENT(...) __VA_ARGS__
#define VA_ARG0_(A0,...) A0
#define VA_ARG0(...) VA_ARG0_(__VA_ARGS__)
#define VA_ARGTAIL_(A0,...) __VA_ARGS__
#define VA_ARGTAIL(...) VA_ARGTAIL_(__VA_ARGS__)
// IFP helper macros to test IBP for IFN and IS_EMPTY
#define IFP_0(T,...) __VA_ARGS__
#define IFP_1(T,...) T
#define IFP_CAT(A,...) A##__VA_ARGS__
#define IFP(BP) IFP_CAT(IFP_,BP)
// IS_BEGIN_PAREN helper macros adapted from BOOST VMD
#define IBP_CAT_(A,...) A##__VA_ARGS__
#define IBP_CAT(A,...) IBP_CAT_(A,__VA_ARGS__)
#define IBP_ARG0_(A,...) A
#define IBP_ARG0(...) IBP_ARG0_(__VA_ARGS__)
#define IBP_IS_ARGS(...) 1
#define IBP_1 1,
#define IBP_IBP_IS_ARGS 0,
// IBP IS_BEGIN_PAREN returns 1 or 0 if ... ARGS is parenthesised
#define IBP(...) IBP_ARG0(IBP_CAT(IBP_, IBP_IS_ARGS __VA_ARGS__))
// IFN, IFE, IFNE and IF_EMPTY helpers without __VA_OPT__ support
#if ! VA_OPT_SUPPORT(?)
# define IBP_(T,...) IBP_ARG0(IBP_CAT(IF##T##_, IBP_IS_ARGS __VA_ARGS__))
// IS_EMPTY helper macros, depend on IBP
# define IE_REDUCE_IBP(...) ()
# define IE_GEN_0(...) 0
# define IE_IBP(...) IBP(IE_REDUCE_IBP __VA_ARGS__ ())
# define GEN_IDENT(...) VA_IDENT
# define GEN_EAT(...) VA_EAT
# define GEN_ARGTAIL(...) VA_ARGTAIL
# define GEN_ARG0(...) VA_ARG0
// IFN, IFE, IFNE helper macros
# define EAT_OR_IDENT(...) IBP_(N,IE_REDUCE_IBP __VA_ARGS__ ())
# define IFN_1 VA_EAT,
# define IFN_IBP_IS_ARGS VA_IDENT,
# define IDENT_OR_EAT(...) IBP_(E,IE_REDUCE_IBP __VA_ARGS__ ())
# define IFE_1 VA_IDENT,
# define IFE_IBP_IS_ARGS VA_EAT,
# define ARG0_OR_TAIL(...) IBP_(NE,IE_REDUCE_IBP __VA_ARGS__ ())
# define IFNE_1 VA_ARGTAIL,
# define IFNE_IBP_IS_ARGS VA_ARG0,
#endif // IFN and IF_EMPTY defs

View File

@ -6,6 +6,7 @@
#define ZCASH_RUST_INCLUDE_TRACING_H
#include "rust/types.h"
#include "rust/VA_OPT.hpp"
#include "tracing/map.h"
#include <stddef.h>
@ -117,8 +118,8 @@ void tracing_log(
#define T_FIELD_NAME(x, y) x
#define T_FIELD_VALUE(x, y) y
#define T_FIELD_NAMES(...) MAP_PAIR_LIST(T_FIELD_NAME, __VA_ARGS__)
#define T_FIELD_VALUES(...) MAP_PAIR_LIST(T_FIELD_VALUE, __VA_ARGS__)
#define T_FIELD_NAMES(...) IFN(__VA_ARGS__)(MAP_PAIR_LIST(T_FIELD_NAME, __VA_ARGS__))
#define T_FIELD_VALUES(...) IFN(__VA_ARGS__)(MAP_PAIR_LIST(T_FIELD_VALUE, __VA_ARGS__))
#define T_DOUBLEESCAPE(a) #a
#define T_ESCAPEQUOTE(a) T_DOUBLEESCAPE(a)
@ -251,22 +252,6 @@ public:
};
} // namespace tracing
/// Expands to a `tracing::Span` object which is used to record a span.
/// The `Span::Enter` method on that object records that the span has been
/// entered, and returns a RAII guard object, which will exit the span when
/// dropped.
///
/// level, target, and name MUST be static constants, and MUST be valid UTF-8
/// strings.
#define TracingSpan(level, target, name) ([&] { \
static constexpr const char* const FIELDS[] = {}; \
const char* T_VALUES[] = {}; \
static TracingCallsite* CALLSITE = \
T_CALLSITE(name, target, level, FIELDS, true); \
return tracing::Span( \
CALLSITE, T_VALUES, T_ARRLEN(T_VALUES)); \
}())
/// Expands to a `tracing::Span` object which is used to record a span.
/// The `Span::Enter` method on that object records that the span has been
/// entered, and returns a RAII guard object, which will exit the span when
@ -276,15 +261,15 @@ public:
///
/// level, target, name, and all keys MUST be static constants, and MUST be
/// valid UTF-8 strings.
#define TracingSpanFields(level, target, name, ...) ([&] { \
static constexpr const char* const FIELDS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* T_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
static TracingCallsite* CALLSITE = \
T_CALLSITE(name, target, level, FIELDS, true); \
return tracing::Span( \
CALLSITE, T_VALUES, T_ARRLEN(T_VALUES)); \
#define TracingSpan(level, target, name, ...) ([&] { \
static constexpr const char* const FIELDS[] = \
{T_FIELD_NAMES(__VA_ARGS__)}; \
const char* T_VALUES[] = \
{T_FIELD_VALUES(__VA_ARGS__)}; \
static TracingCallsite* CALLSITE = \
T_CALLSITE(name, target, level, FIELDS, true); \
return tracing::Span( \
CALLSITE, T_VALUES, T_ARRLEN(T_VALUES)); \
}())
#endif