#pragma once #include "preprocessor.h" #include #include #include #include #include #include #include #include #include #include #include namespace NLWTrace { // Class to hold parameter values parsed from trace query predicate operators template struct TParamConv { static T FromString(const TString& text) { return ::FromString(text); } static TString ToString(const T& param) { return ::ToString(param); } }; template <> struct TParamConv { static TString FromString(const TString& text) { return text; } static TString ToString(TString const* param) { return TString(*param); } }; template <> struct TParamConv { static ui8 FromString(const TString& text) { return (ui8)::FromString(text); } static TString ToString(ui8 param) { return ::ToString((ui16)param); } }; template <> struct TParamConv { static i8 FromString(const TString& text) { return (i8)::FromString(text); } static TString ToString(i8 param) { return ::ToString((i16)param); } }; template <> struct TParamConv { static double FromString(const TString& text) { return ::FromString(text); } static TString ToString(double param) { return Sprintf("%.6lf", param); } }; // Fake parameter type used as a placeholder for not used parameters (above the number of defined params for a specific probe) class TNil { }; // Struct that holds and handles a value of parameter of any supported type. struct TParam { char Data[LWTRACE_MAX_PARAM_SIZE]; template const T& Get() const { return *reinterpret_cast(Data); } template T& Get() { return *reinterpret_cast(Data); } template void DefaultConstruct() { new (&Data) T(); } template void CopyConstruct(const T& other) { new (&Data) T(other); } template void Destruct() { Get().~T(); } }; template <> inline void TParam::DefaultConstruct() { } template <> inline void TParam::CopyConstruct(const TNil&) { } template <> inline void TParam::Destruct() { } class TTypedParam { private: EParamTypePb Type; TParam Param; // Contains garbage if PT_UNKNOWN public: TTypedParam() : Type(PT_UNKNOWN) { } explicit TTypedParam(EParamTypePb type) : Type(type) { switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ Param.DefaultConstruct(); \ return; FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return; default: Y_ABORT("unknown param type"); } } template explicit TTypedParam(const T& x, EParamTypePb type = PT_UNKNOWN) : Type(type) { Param.CopyConstruct(x); } #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ explicit TTypedParam(const t& x) \ : Type(PT_##v) { \ Param.CopyConstruct(x); \ } FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO TTypedParam(const TTypedParam& o) { Assign(o); } TTypedParam& operator=(const TTypedParam& o) { Reset(); Assign(o); return *this; } void Assign(const TTypedParam& o) { Type = o.Type; switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ Param.CopyConstruct(o.Param.Get()); \ return; FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return; default: Y_ABORT("unknown param type"); } } TTypedParam(TTypedParam&& o) : Type(o.Type) , Param(o.Param) { o.Type = PT_UNKNOWN; // To avoid Param destroy by source object dtor } TTypedParam(EParamTypePb type, const TParam& param) : Type(type) { Y_UNUSED(param); // for disabled lwtrace switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ Param.CopyConstruct(param.Get()); \ return; FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return; default: Y_ABORT("unknown param type"); } } ~TTypedParam() { Reset(); } void Reset() { switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ Param.Destruct(); \ return; FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return; default: Y_ABORT("unknown param type"); } Type = PT_UNKNOWN; } bool operator==(const TTypedParam& rhs) const { if (Y_LIKELY(Type == rhs.Type)) { switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ return Param.Get() == rhs.Param.Get(); FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return false; // All unknowns are equal default: Y_ABORT("unknown param type"); } } else { return false; } } bool operator!=(const TTypedParam& rhs) const { return !operator==(rhs); } bool operator<(const TTypedParam& rhs) const { if (Y_LIKELY(Type == rhs.Type)) { switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ return Param.Get() < rhs.Param.Get(); FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return false; // All unknowns are equal default: Y_ABORT("unknown param type"); } } else { return Type < rhs.Type; } } bool operator<=(const TTypedParam& rhs) const { if (Y_LIKELY(Type == rhs.Type)) { switch (Type) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ case PT_##v: \ return Param.Get() <= rhs.Param.Get(); FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO case PT_UNKNOWN: return true; // All unknowns are equal default: Y_ABORT("unknown param type"); } } else { return Type < rhs.Type; } } bool operator>(const TTypedParam& rhs) const { return !operator<=(rhs); } bool operator>=(const TTypedParam& rhs) const { return !operator<(rhs); } EParamTypePb GetType() const { return Type; } const TParam& GetParam() const { return Param; } }; class TLiteral { private: TTypedParam Values[EParamTypePb_ARRAYSIZE]; public: explicit TLiteral(const TString& text) { Y_UNUSED(text); /* That's for windows, where we have lwtrace disabled. */ #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ try { \ Values[PT_##v] = TTypedParam(TParamConv::FromString(text)); \ } catch (...) { \ Values[PT_##v] = TTypedParam(); \ } \ /**/ FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO } TLiteral() { } TLiteral(const TLiteral& o) { for (size_t i = 0; i < EParamTypePb_ARRAYSIZE; i++) { Values[i] = o.Values[i]; } } TLiteral& operator=(const TLiteral& o) { for (size_t i = 0; i < EParamTypePb_ARRAYSIZE; i++) { Values[i] = o.Values[i]; } return *this; } const TTypedParam& GetValue(EParamTypePb type) const { return Values[type]; } bool operator==(const TTypedParam& rhs) const { return Values[rhs.GetType()] == rhs; } bool operator!=(const TTypedParam& rhs) const { return !operator==(rhs); } bool operator<(const TTypedParam& rhs) const { return Values[rhs.GetType()] < rhs; } bool operator<=(const TTypedParam& rhs) const { return Values[rhs.GetType()] <= rhs; } bool operator>(const TTypedParam& rhs) const { return !operator<=(rhs); } bool operator>=(const TTypedParam& rhs) const { return !operator<(rhs); } }; inline bool operator==(const TTypedParam& lhs, const TLiteral& rhs) { return lhs == rhs.GetValue(lhs.GetType()); } inline bool operator!=(const TTypedParam& lhs, const TLiteral& rhs) { return !operator==(lhs, rhs); } inline bool operator<(const TTypedParam& lhs, const TLiteral& rhs) { return lhs < rhs.GetValue(lhs.GetType()); } inline bool operator<=(const TTypedParam& lhs, const TLiteral& rhs) { return lhs <= rhs.GetValue(lhs.GetType()); } inline bool operator>(const TTypedParam& lhs, const TLiteral& rhs) { return !operator<=(lhs, rhs); } inline bool operator>=(const TTypedParam& lhs, const TLiteral& rhs) { return !operator<(lhs, rhs); } // Struct that holds and handles all parameter values of different supported types struct TParams { TParam Param[LWTRACE_MAX_PARAMS]; }; using TSerializedParams = google::protobuf::RepeatedPtrField; // Represents a common class for all function "signatures" (parameter types and names). // Provides non-virtual interface to handle the signature and (emulated) virtual interface to handle TParams corresponding to the signature struct TSignature { const char** ParamTypes; const char* ParamNames[LWTRACE_MAX_PARAMS + 1]; size_t ParamCount; // Virtual table void (*SerializeParamsFunc)(const TParams& params, TString* values); void (*CloneParamsFunc)(TParams& newParams, const TParams& oldParams); void (*DestroyParamsFunc)(TParams& params); void (*SerializeToPbFunc)(const TParams& params, TSerializedParams& arr); bool (*DeserializeFromPbFunc)(TParams& params, const TSerializedParams& arr); // Virtual calls emulation void SerializeParams(const TParams& params, TString* values) const { (*SerializeParamsFunc)(params, values); } void CloneParams(TParams& newParams, const TParams& oldParams) const { (*CloneParamsFunc)(newParams, oldParams); } void DestroyParams(TParams& params) const { (*DestroyParamsFunc)(params); } void SerializeToPb(const TParams& params, TSerializedParams& arr) const { (*SerializeToPbFunc)(params, arr); } bool DeserializeFromPb(TParams& params, const TSerializedParams& arr) const { return (*DeserializeFromPbFunc)(params, arr); } void ToProtobuf(TEventPb& pb) const; size_t FindParamIndex(const TString& param) const { for (size_t i = 0; i < ParamCount; i++) { if (ParamNames[i] == param) { return i; } } return size_t(-1); } }; #ifndef LWTRACE_DISABLE // Implementation. Used for compilation error if not all expected parameters passed to a function call struct ERROR_not_enough_parameters : TNil {}; // Struct that holds static string with a name of parameter type template struct TParamType { enum { Supported = 0 }; static const char* NameString; }; #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ template <> \ struct TParamType { \ enum { Supported = 1 }; \ static const char* NameString; \ }; \ /**/ FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) FOR_NIL_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO template struct TParamTraits; // Enum types traits impl. template >> struct TEnumParamTraitsImpl { using TStoreType = typename TParamTraits>::TStoreType; using TFuncParam = TEnum; inline static void ToString(typename TTypeTraits::TFuncParam stored, TString* out) { if constexpr (google::protobuf::is_proto_enum::value) { const google::protobuf::EnumValueDescriptor* valueDescriptor = google::protobuf::GetEnumDescriptor()->FindValueByNumber(stored); if (valueDescriptor) { *out = TStringBuilder() << valueDescriptor->name() << " (" << stored << ")"; } else { *out = TParamConv::ToString(stored); } } else { *out = TParamConv::ToString(stored); } } inline static TStoreType ToStoreType(TFuncParam v) { return static_cast(v); } }; template struct TCustomTraitsImpl { using TStoreType = typename TParamTraits::TStoreType; //see STORE_TYPE_AS using TFuncParam = typename TCustomType::TFuncParam; inline static void ToString(typename TTypeTraits::TFuncParam stored, TString* out) { TCustomType::ToString(stored, out); } inline static TStoreType ToStoreType(TFuncParam v) { return TCustomType::ToStoreType(v); } }; template struct TParamTraitsImpl; template struct TParamTraitsImpl : TEnumParamTraitsImpl { }; template struct TParamTraitsImpl : TCustomTraitsImpl { }; template struct TParamTraits : TParamTraitsImpl> { }; // Standard stored types traits. #define STORE_TYPE_AS(input_t, store_as_t) \ template <> \ struct TParamTraits { \ using TStoreType = store_as_t; \ using TFuncParam = typename TTypeTraits::TFuncParam; \ \ inline static void ToString(typename TTypeTraits::TFuncParam stored, TString* out) { \ *out = TParamConv::ToString(stored); \ } \ \ inline static TStoreType ToStoreType(TFuncParam v) { \ return v; \ } \ }; \ /**/ STORE_TYPE_AS(ui8, ui64); STORE_TYPE_AS(i8, i64); STORE_TYPE_AS(ui16, ui64); STORE_TYPE_AS(i16, i64); STORE_TYPE_AS(ui32, ui64); STORE_TYPE_AS(i32, i64); STORE_TYPE_AS(bool, ui64); STORE_TYPE_AS(float, double); #define FOREACH_PARAMTYPE_MACRO(n, t, v) STORE_TYPE_AS(t, t) FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef STORE_TYPE_AS #undef FOREACH_PARAMTYPE_MACRO // Nil type staits. template <> struct TParamTraits { using TStoreType = TNil; using TFuncParam = TTypeTraits::TFuncParam; inline static void ToString(typename TTypeTraits::TFuncParam, TString*) { } inline static TNil ToStoreType(TFuncParam v) { return v; } }; inline EParamTypePb ParamTypeToProtobuf(const char* paramType) { #define FOREACH_PARAMTYPE_MACRO(n, t, v) \ if (strcmp(paramType, n) == 0) { \ return PT_##v; \ } \ /**/ FOREACH_PARAMTYPE(FOREACH_PARAMTYPE_MACRO) #undef FOREACH_PARAMTYPE_MACRO return PT_UNKNOWN; } template inline void SaveParamToPb(TSerializedParams& msg, const TParam& param); template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { Y_UNUSED(msg); Y_UNUSED(param); } template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { msg.Add()->SetIntValue(param.Get::TStoreType>()); } template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { msg.Add()->SetUintValue(param.Get::TStoreType>()); } template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { msg.Add()->SetDoubleValue(param.Get::TStoreType>()); } template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { msg.Add()->SetStrValue(param.Get::TStoreType>()); } template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { msg.Add()->SetStrValue(*param.Get::TStoreType>().Str); } template <> inline void SaveParamToPb(TSerializedParams& msg, const TParam& param) { msg.Add()->SetIntValue(param.Get::TStoreType>().Value); } template inline void LoadParamFromPb(const TTraceParam& msg, TParam& param); template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { param.DefaultConstruct(); param.Get() = msg.GetIntValue(); } template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { param.DefaultConstruct(); param.Get() = msg.GetUintValue(); } template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { param.DefaultConstruct(); param.Get() = msg.GetDoubleValue(); } template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { param.CopyConstruct(TCheck(msg.GetIntValue())); } template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { Y_UNUSED(msg); Y_UNUSED(param); static TString unsupported("unsupported"); // so far TSymbol deserialization is not supported // since it is not used for probes, it is ok param.CopyConstruct(TSymbol(&unsupported)); } template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { param.DefaultConstruct(); param.Get() = msg.GetStrValue(); } template <> inline void LoadParamFromPb(const TTraceParam& msg, TParam& param) { Y_UNUSED(msg); Y_UNUSED(param); } // Class representing a specific signature template struct TUserSignature { #define FOREACH_PARAMNUM_MACRO(i) static_assert(TParamType::TStoreType>::Supported == 1, "expect TParamType< typename TParamTraits::TStoreType >::Supported == 1"); FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO) // ERROR: unsupported type used as probe/event parameter type #undef FOREACH_PARAMNUM_MACRO static const char* ParamTypes[]; static const int ParamCount = LWTRACE_COUNT_PARAMS; // Implementation of virtual function (TSignature derived classes vtable emulation) inline static void SerializeParams(const TParams& params, TString* values) { #define FOREACH_PARAMNUM_MACRO(i) TParamTraits::ToString(params.Param[i].Get::TStoreType>(), values + i); FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO); #undef FOREACH_PARAMNUM_MACRO } // Implementation of virtual function (TSignature derived classes vtable emulation) inline static void CloneParams(TParams& newParams, const TParams& oldParams) { #define FOREACH_PARAMNUM_MACRO(i) newParams.Param[i].CopyConstruct::TStoreType>(oldParams.Param[i].Get::TStoreType>()); FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO); #undef FOREACH_PARAMNUM_MACRO } // Implementation of virtual function (TSignature derived classes vtable emulation) inline static void DestroyParams(TParams& params) { #define FOREACH_PARAMNUM_MACRO(i) params.Param[i].Destruct::TStoreType>(); FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO); #undef FOREACH_PARAMNUM_MACRO } // Implementation of virtual function (TSignature derived classes vtable emulation) inline static void SerializeToPb(const TParams& params, TSerializedParams& arr) { #define FOREACH_PARAMNUM_MACRO(i) \ SaveParamToPb::TStoreType>( \ arr, \ params.Param[i]); \ // FOREACH_PARAMNUM_MACRO FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO); #undef FOREACH_PARAMNUM_MACRO } // Implementation of virtual function (TSignature derived classes vtable emulation) inline static bool DeserializeFromPb(TParams& params, const TSerializedParams& arr) { if (arr.size() != ParamCount) { return false; } if (!ParamCount) { return true; } int paramIdx = 0; #define FOREACH_PARAMNUM_MACRO(i) \ if (paramIdx >= arr.size()) { \ return true; \ }; \ LoadParamFromPb::TStoreType>( \ arr.Get(paramIdx), \ params.Param[paramIdx]); \ ++paramIdx; \ // FOREACH_PARAMNUM_MACRO FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO); #undef FOREACH_PARAMNUM_MACRO return true; } }; // Array of static strings pointers for names of parameter types in a specific signature template const char* TUserSignature::ParamTypes[] = { #define FOREACH_PARAMNUM_MACRO(i) TParamType::TStoreType>::NameString, FOREACH_PARAMNUM(FOREACH_PARAMNUM_MACRO) nullptr #undef FOREACH_PARAMNUM_MACRO }; inline void TSignature::ToProtobuf(TEventPb& pb) const { for (size_t i = 0; i < ParamCount; i++) { pb.AddParamTypes(ParamTypeToProtobuf(ParamTypes[i])); pb.AddParamNames(ParamNames[i]); } } #else inline void TSignature::ToProtobuf(TEventPb&) const { } inline EParamTypePb ParamTypeToProtobuf(const char*) { return PT_UNKNOWN; } #endif }