diff --git a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h index 396dd4e6638b..0162f8093e4b 100644 --- a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h +++ b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h @@ -1018,6 +1018,10 @@ struct BaseMarkerType { static constexpr const char* TableLabel = nullptr; static constexpr const char* TooltipLabel = nullptr; + // This indicates whether this marker type wants the names passed to the + // individual marker calls stores along with the marker. + static constexpr bool StoreName = false; + static constexpr MarkerSchema::ETWMarkerGroup Group = MarkerSchema::ETWMarkerGroup::Generic; diff --git a/tools/profiler/docs/markers-guide.rst b/tools/profiler/docs/markers-guide.rst index 86744c523c4a..ed18b3586711 100644 --- a/tools/profiler/docs/markers-guide.rst +++ b/tools/profiler/docs/markers-guide.rst @@ -319,7 +319,7 @@ A marker type must have a unique name, it is used to keep track of the type of markers in the profiler storage, and to identify them uniquely on profiler.firefox.com. (It does not need to be the same as the ``struct``'s name.) -This name is defined in a special static data member ``Name``: +This type name is defined in a special static data member ``Name``: .. code-block:: cpp @@ -333,6 +333,14 @@ In addition you must add a description of your marker in a special static data m // … static constexpr const char* Description = "This is my marker!"; +If you expect users to be passing unique names for individual instances of the marker, +you may want to add the following to ensure those names get stored when using ETW: + +.. code-block:: cpp + + // … + static constexpr bool StoreName = true; + Marker Type Data ^^^^^^^^^^^^^^^^ diff --git a/tools/profiler/public/ETWTools.h b/tools/profiler/public/ETWTools.h index 2f4dc2658387..736b570841c0 100644 --- a/tools/profiler/public/ETWTools.h +++ b/tools/profiler/public/ETWTools.h @@ -22,6 +22,8 @@ namespace ETW { extern std::atomic gETWCollectionMask; +constexpr const char* kNameKey = "MarkerName"; + // Forward-declare the g_hMyComponentProvider variable that you will use for // tracing in this component TRACELOGGING_DECLARE_PROVIDER(kFirefoxTraceLoggingProvider); @@ -29,9 +31,16 @@ TRACELOGGING_DECLARE_PROVIDER(kFirefoxTraceLoggingProvider); void Init(); void Shutdown(); +template +struct MarkerHasPayload : std::false_type {}; +template +struct MarkerHasPayload> + : std::true_type {}; + // This describes the base fields for all markers (information extracted from // MarkerOptions. struct BaseMarkerDescription { + static constexpr bool StoreName = false; using MS = mozilla::MarkerSchema; static constexpr MS::PayloadField PayloadFields[] = { {"StartTime", MS::InputType::TimeStamp, "Start Time"}, @@ -43,19 +52,25 @@ struct BaseMarkerDescription { // This is the MarkerType object for markers with no statically declared type, // their name is written dynamically. -struct SimpleMarkerType { +struct SimpleMarkerType : public mozilla::BaseMarkerType { using MS = mozilla::MarkerSchema; + static constexpr const char* Name = "SimpleMarker"; - static constexpr MS::PayloadField PayloadFields[] = { - {"MarkerName", MS::InputType::CString, "Simple Marker Name"}}; + static constexpr bool StoreName = true; }; // This gets the space required in the Tlg static struct to pack the fields. template constexpr std::size_t GetPackingSpace() { size_t length = 0; - for (size_t i = 0; i < std::size(T::PayloadFields); i++) { - length += std::string_view{T::PayloadFields[i].Key}.size() + 1; + if constexpr (MarkerHasPayload::value) { + for (size_t i = 0; i < std::size(T::PayloadFields); i++) { + length += std::string_view{T::PayloadFields[i].Key}.size() + 1; + length += sizeof(uint8_t); + } + } + if (T::StoreName) { + length += std::string_view{kNameKey}.size() + 1; length += sizeof(uint8_t); } return length; @@ -123,12 +138,20 @@ struct StaticMetaData { fieldStorage[pos++] = GetTlgInputType(BaseMarkerDescription::PayloadFields[i].InputTy); } - for (uint32_t i = 0; i < std::size(T::PayloadFields); i++) { - for (size_t c = 0; - c < std::string_view{T::PayloadFields[i].Key}.size() + 1; c++) { - fieldStorage[pos++] = T::PayloadFields[i].Key[c]; + if (T::StoreName) { + for (size_t c = 0; c < std::string_view{kNameKey}.size() + 1; c++) { + fieldStorage[pos++] = kNameKey[c]; + } + fieldStorage[pos++] = TlgInANSISTRING; + } + if constexpr (MarkerHasPayload::value) { + for (uint32_t i = 0; i < std::size(T::PayloadFields); i++) { + for (size_t c = 0; + c < std::string_view{T::PayloadFields[i].Key}.size() + 1; c++) { + fieldStorage[pos++] = T::PayloadFields[i].Key[c]; + } + fieldStorage[pos++] = GetTlgInputType(T::PayloadFields[i].InputTy); } - fieldStorage[pos++] = GetTlgInputType(T::PayloadFields[i].InputTy); } } }; @@ -228,8 +251,7 @@ void CreateDataDescForPayload(PayloadBuffer& aBuffer, template struct MarkerSupportsETW : std::false_type {}; template -struct MarkerSupportsETW> - : std::true_type {}; +struct MarkerSupportsETW> : std::true_type {}; template struct MarkerHasTranslator : std::false_type {}; @@ -273,32 +295,16 @@ static inline void StoreBaseEventDataDesc( sizeof(uint32_t)); } -// This is used for markers with no explicit type or markers which have not -// been converted to the updated schema yet. -static inline void EmitETWMarker(const mozilla::ProfilerString8View& aName, - const mozilla::MarkerCategory& aCategory, - const mozilla::MarkerOptions& aOptions = {}) { - if (!(gETWCollectionMask & - uint64_t(mozilla::MarkerSchema::ETWMarkerGroup::Generic))) { - return; +template +constexpr size_t GetETWDescriptorCount() { + size_t count = 2 + std::size(BaseMarkerDescription::PayloadFields); + if (MarkerType::StoreName) { + count++; } - - static const __declspec(allocate(_tlgSegMetadataEvents)) __declspec( - align(1)) constexpr StaticMetaData - staticData; - - std::array descriptors = {}; - - // This is storage space allocated on the stack for POD values. - BaseEventStorage dataStorage = {}; - - StoreBaseEventDataDesc(dataStorage, descriptors.data(), aCategory, - std::move(aOptions)); - - EventDataDescCreate(&descriptors[7], aName.StringView().data(), - aName.StringView().size() + 1); - _tlgWriteTransfer(kFirefoxTraceLoggingProvider, &staticData.metaData.Channel, - NULL, NULL, descriptors.size(), descriptors.data()); + if constexpr (MarkerHasPayload::value) { + count += std::size(MarkerType::PayloadFields); + } + return count; } template @@ -310,7 +316,7 @@ static inline void EmitETWMarker(const mozilla::ProfilerString8View& aName, // If our MarkerType has not been made to support ETW, emit only the base // event. Avoid attempting to compile the rest of the function. if constexpr (!MarkerSupportsETW::value) { - return EmitETWMarker(aName, aCategory, aOptions); + return EmitETWMarker(aName, aCategory, aOptions, SimpleMarkerType{}); } else { if (!(gETWCollectionMask & uint64_t(MarkerType::Group))) { return; @@ -321,9 +327,7 @@ static inline void EmitETWMarker(const mozilla::ProfilerString8View& aName, staticData; // Allocate the exact amount of descriptors required by this event. - std::array + std::array()> descriptors = {}; // Memory allocated on the stack for storing intermediate values. @@ -333,23 +337,33 @@ static inline void EmitETWMarker(const mozilla::ProfilerString8View& aName, StoreBaseEventDataDesc(dataStorage, descriptors.data(), aCategory, aOptions); - if constexpr (MarkerHasTranslator::value) { - // When this function is implemented the arguments are passed back to the - // MarkerType object which is expected to call OutputMarkerSchema with - // the correct argument format. - buffer.mDescriptors = descriptors.data() + 2 + - std::size(BaseMarkerDescription::PayloadFields); - MarkerType::TranslateMarkerInputToSchema(&buffer, aPayloadArguments...); - } else { - const size_t argCount = sizeof...(PayloadArguments); - static_assert( - argCount == std::size(MarkerType::PayloadFields), - "Number and type of fields must be equal to number and type of " - "payload arguments. If this is not the case a " - "TranslateMarkerInputToSchema function must be defined."); - size_t i = 2 + std::size(BaseMarkerDescription::PayloadFields); - (CreateDataDescForPayload(buffer, descriptors[i++], aPayloadArguments), - ...); + if constexpr (MarkerType::StoreName) { + EventDataDescCreate(&descriptors[7], aName.StringView().data(), + aName.StringView().size() + 1); + } + + if constexpr (MarkerHasPayload::value) { + if constexpr (MarkerHasTranslator::value) { + // When this function is implemented the arguments are passed back to + // the MarkerType object which is expected to call OutputMarkerSchema + // with the correct argument format. + buffer.mDescriptors = descriptors.data() + 2 + + std::size(BaseMarkerDescription::PayloadFields) + + (MarkerType::StoreName ? 1 : 0); + + MarkerType::TranslateMarkerInputToSchema(&buffer, aPayloadArguments...); + } else { + const size_t argCount = sizeof...(PayloadArguments); + static_assert( + argCount == std::size(MarkerType::PayloadFields), + "Number and type of fields must be equal to number and type of " + "payload arguments. If this is not the case a " + "TranslateMarkerInputToSchema function must be defined."); + size_t i = 2 + std::size(BaseMarkerDescription::PayloadFields) + + (MarkerType::StoreName ? 1 : 0); + (CreateDataDescForPayload(buffer, descriptors[i++], aPayloadArguments), + ...); + } } _tlgWriteTransfer(kFirefoxTraceLoggingProvider, @@ -381,9 +395,6 @@ void OutputMarkerSchema(void* aContext, MarkerType aMarkerType, namespace ETW { static inline void Init() {} static inline void Shutdown() {} -static inline void EmitETWMarker(const mozilla::ProfilerString8View& aName, - const mozilla::MarkerCategory& aCategory, - const mozilla::MarkerOptions& aOptions = {}) {} template static inline void EmitETWMarker(const mozilla::ProfilerString8View& aName, const mozilla::MarkerCategory& aCategory,