From b1727e644709a7367ddab03112ed036066d9894e Mon Sep 17 00:00:00 2001 From: Gregory LEOCADIE Date: Mon, 28 Aug 2023 18:04:41 +0200 Subject: [PATCH] [Profiler] Add Sample value type provider (#4480) * Add Sample value type provider * Address comment --- .../AllocationsProvider.cpp | 30 +++++- .../AllocationsProvider.h | 18 +++- .../Datadog.Profiler.Native/CollectorBase.h | 18 ++-- .../ContentionProvider.cpp | 5 +- .../ContentionProvider.h | 8 +- .../CorProfilerCallback.cpp | 54 +++-------- .../CpuTimeProvider.cpp | 4 +- .../Datadog.Profiler.Native/CpuTimeProvider.h | 9 +- .../Datadog.Profiler.Native.vcxproj | 2 + .../Datadog.Profiler.Native.vcxproj.filters | 42 +++++--- .../ExceptionsProvider.cpp | 5 +- .../ExceptionsProvider.h | 8 +- .../Datadog.Profiler.Native/GCBaseRawSample.h | 9 +- .../GarbageCollectionProvider.cpp | 5 +- .../GarbageCollectionProvider.h | 4 +- .../IUpscaleProvider.h | 3 +- .../LibddprofExporter.cpp | 2 +- .../LibddprofExporter.h | 2 +- .../LiveObjectsProvider.cpp | 6 +- .../LiveObjectsProvider.h | 9 +- .../RawAllocationSample.h | 7 +- .../RawContentionSample.h | 7 +- .../Datadog.Profiler.Native/RawCpuSample.h | 5 +- .../RawExceptionSample.h | 5 +- .../RawGarbageCollectionSample.h | 2 +- .../Datadog.Profiler.Native/RawSample.h | 4 +- .../RawStopTheWorldSample.h | 2 +- .../RawWallTimeSample.h | 5 +- .../SampleValueTypeProvider.cpp | 54 +++++++++++ .../SampleValueTypeProvider.h | 29 ++++++ .../StopTheWorldGCProvider.cpp | 6 +- .../StopTheWorldGCProvider.h | 11 +-- .../WallTimeProvider.cpp | 5 +- .../WallTimeProvider.h | 9 +- .../Datadog.Profiler.Native.Tests.vcxproj | 1 + ...adog.Profiler.Native.Tests.vcxproj.filters | 26 ++++- .../ProviderTest.cpp | 16 ++- .../SampleValueTypeProviderTest.cpp | 97 +++++++++++++++++++ 38 files changed, 382 insertions(+), 152 deletions(-) create mode 100644 profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.cpp create mode 100644 profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.h create mode 100644 profiler/test/Datadog.Profiler.Native.Tests/SampleValueTypeProviderTest.cpp diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.cpp index 8c9ebde1ac67..342d087e64a0 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.cpp @@ -14,6 +14,7 @@ #include "Log.h" #include "MetricsRegistry.h" #include "OsSpecificApi.h" +#include "SampleValueTypeProvider.h" #include "shared/src/native-src/com_ptr.h" #include "shared/src/native-src/string.h" @@ -22,12 +23,10 @@ std::vector AllocationsProvider::SampleTypeDefinitions( { {"alloc-samples", "count"}, - {"alloc-size", "bytes"} - }); - + {"alloc-size", "bytes"}}); AllocationsProvider::AllocationsProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo4* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -38,7 +37,28 @@ AllocationsProvider::AllocationsProvider( ISampledAllocationsListener* pListener, MetricsRegistry& metricsRegistry) : - CollectorBase("AllocationsProvider", valueOffset, SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration), + AllocationsProvider( + valueTypeProvider.GetOrRegister(SampleTypeDefinitions), + pCorProfilerInfo, pManagedThreadList, pFrameStore, + pThreadsCpuManager, pAppDomainStore, pRuntimeIdStore, + pConfiguration, + pListener, + metricsRegistry) +{ +} + +AllocationsProvider::AllocationsProvider( + std::vector valueTypes, + ICorProfilerInfo4* pCorProfilerInfo, + IManagedThreadList* pManagedThreadList, + IFrameStore* pFrameStore, + IThreadsCpuManager* pThreadsCpuManager, + IAppDomainStore* pAppDomainStore, + IRuntimeIdStore* pRuntimeIdStore, + IConfiguration* pConfiguration, + ISampledAllocationsListener* pListener, + MetricsRegistry& metricsRegistry) : + CollectorBase("AllocationsProvider", std::move(valueTypes), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration), _pCorProfilerInfo(pCorProfilerInfo), _pManagedThreadList(pManagedThreadList), _pFrameStore(pFrameStore), diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.h index 02dc84cad2a5..274a1f72e7e5 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/AllocationsProvider.h @@ -19,6 +19,7 @@ class IThreadsCpuManager; class IAppDomainStore; class IRuntimeIdStore; class ISampledAllocationsListener; +class SampleValueTypeProvider; class AllocationsProvider @@ -27,11 +28,20 @@ class AllocationsProvider public IAllocationsListener { public: - static std::vector SampleTypeDefinitions; + AllocationsProvider( + SampleValueTypeProvider& valueTypeProvider, + ICorProfilerInfo4* pCorProfilerInfo, + IManagedThreadList* pManagedThreadList, + IFrameStore* pFrameStore, + IThreadsCpuManager* pThreadsCpuManager, + IAppDomainStore* pAppDomainStore, + IRuntimeIdStore* pRuntimeIdStore, + IConfiguration* pConfiguration, + ISampledAllocationsListener* pListener, + MetricsRegistry& metricsRegistry); -public: AllocationsProvider( - uint32_t valueOffset, + std::vector valueTypeProvider, ICorProfilerInfo4* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -50,6 +60,8 @@ class AllocationsProvider uint64_t allocationAmount) override; private: + static std::vector SampleTypeDefinitions; + ICorProfilerInfo4* _pCorProfilerInfo; IManagedThreadList* _pManagedThreadList; IFrameStore* _pFrameStore; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CollectorBase.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CollectorBase.h index 69fc10e6d894..8c63c75b00ce 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CollectorBase.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CollectorBase.h @@ -20,6 +20,7 @@ #include "IThreadsCpuManager.h" #include "ProviderBase.h" #include "RawSample.h" +#include "SampleValueTypeProvider.h" #include "shared/src/native-src/string.h" @@ -51,8 +52,7 @@ class CollectorBase public: CollectorBase( const char* name, - uint32_t valueOffset, - std::size_t nbValues, + std::vector valueOffsets, IThreadsCpuManager* pThreadsCpuManager, IFrameStore* pFrameStore, IAppDomainStore* pAppDomainStore, @@ -60,18 +60,13 @@ class CollectorBase IConfiguration* pConfiguration ) : ProviderBase(name), - _valueOffset{valueOffset}, _pFrameStore{pFrameStore}, _pAppDomainStore{pAppDomainStore}, _pRuntimeIdStore{pRuntimeIdStore}, _pThreadsCpuManager{pThreadsCpuManager}, _isTimestampsAsLabelEnabled{pConfiguration->IsTimestampsAsLabelEnabled()} { - _valueOffsets.reserve(nbValues); - for (auto i = _valueOffset; i < _valueOffset + nbValues; i++) - { - _valueOffsets.push_back(i); - } + _valueOffsets = std::move(valueOffsets); } // interfaces implementation @@ -130,7 +125,7 @@ class CollectorBase } // allow inherited classes to add values and specific labels - rawSample.OnTransform(sample, _valueOffset); + rawSample.OnTransform(sample, _valueOffsets); return sample; } @@ -141,7 +136,7 @@ class CollectorBase return OpSysTools::GetHighPrecisionTimestamp(); } - std::vector const& GetValueOffsets() const + std::vector const& GetValueOffsets() const { return _valueOffsets; } @@ -239,7 +234,6 @@ class CollectorBase } private: - uint32_t _valueOffset = 0; IFrameStore* _pFrameStore = nullptr; IAppDomainStore* _pAppDomainStore = nullptr; IRuntimeIdStore* _pRuntimeIdStore = nullptr; @@ -253,5 +247,5 @@ class CollectorBase std::mutex _rawSamplesLock; std::list _collectedSamples; - std::vector _valueOffsets; + std::vector _valueOffsets; }; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.cpp index e0e8d83572d4..b62567f11ba3 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.cpp @@ -12,6 +12,7 @@ #include "IUpscaleProvider.h" #include "OsSpecificApi.h" #include "Sample.h" +#include "SampleValueTypeProvider.h" std::vector ContentionProvider::SampleTypeDefinitions( @@ -22,7 +23,7 @@ std::vector ContentionProvider::SampleTypeDefinitions( ContentionProvider::ContentionProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo4* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -32,7 +33,7 @@ ContentionProvider::ContentionProvider( IConfiguration* pConfiguration, MetricsRegistry& metricsRegistry) : - CollectorBase("ContentionProvider", valueOffset, SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration), + CollectorBase("ContentionProvider", valueTypeProvider.GetOrRegister(SampleTypeDefinitions), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration), _pCorProfilerInfo{pCorProfilerInfo}, _pManagedThreadList{pManagedThreadList}, _sampler(pConfiguration->ContentionSampleLimit(), pConfiguration->GetUploadInterval(), false), diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.h index 68c5850ec67a..e99bb0f783f1 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ContentionProvider.h @@ -21,6 +21,7 @@ class IFrameStore; class IThreadsCpuManager; class IAppDomainStore; class IRuntimeIdStore; +class SampleValueTypeProvider; class ContentionProvider : @@ -28,12 +29,9 @@ class ContentionProvider : public IContentionListener, public IUpscaleProvider { -public: - static std::vector SampleTypeDefinitions; - public: ContentionProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo4* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -50,6 +48,8 @@ class ContentionProvider : private: static std::string GetBucket(double contentionDurationNs); + static std::vector SampleTypeDefinitions; + ICorProfilerInfo4* _pCorProfilerInfo; IManagedThreadList* _pManagedThreadList; GroupSampler _sampler; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CorProfilerCallback.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CorProfilerCallback.cpp index 7dcd3ff1d390..7fe538794a29 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CorProfilerCallback.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CorProfilerCallback.cpp @@ -44,6 +44,7 @@ #include "RuntimeIdStore.h" #include "RuntimeInfo.h" #include "Sample.h" +#include "SampleValueTypeProvider.h" #include "StackSamplerLoopManager.h" #include "ThreadsCpuManager.h" #include "WallTimeProvider.h" @@ -128,35 +129,22 @@ bool CorProfilerCallback::InitializeServices() auto* pRuntimeIdStore = RegisterService(); - // Each sample contains a vector of values. - // The list of a provider value definitions is available statically. - // Based on previous providers list, an offset in the values vector is computed and passed to the provider constructor. - // So a provider knows which value slot can be used to store its value(s). - uint32_t valuesOffset = 0; - std::vector sampleTypeDefinitions; + auto valueTypeProvider = SampleValueTypeProvider(); if (_pConfiguration->IsWallTimeProfilingEnabled()) { - auto valueTypes = WallTimeProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); - _pWallTimeProvider = RegisterService(valuesOffset, _pThreadsCpuManager, _pFrameStore.get(), _pAppDomainStore.get(), pRuntimeIdStore, _pConfiguration.get()); - valuesOffset += static_cast(valueTypes.size()); + _pWallTimeProvider = RegisterService(valueTypeProvider, _pThreadsCpuManager, _pFrameStore.get(), _pAppDomainStore.get(), pRuntimeIdStore, _pConfiguration.get()); } if (_pConfiguration->IsCpuProfilingEnabled()) { - auto valueTypes = CpuTimeProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); - _pCpuTimeProvider = RegisterService(valuesOffset, _pThreadsCpuManager, _pFrameStore.get(), _pAppDomainStore.get(), pRuntimeIdStore, _pConfiguration.get()); - valuesOffset += static_cast(valueTypes.size()); + _pCpuTimeProvider = RegisterService(valueTypeProvider, _pThreadsCpuManager, _pFrameStore.get(), _pAppDomainStore.get(), pRuntimeIdStore, _pConfiguration.get()); } if (_pConfiguration->IsExceptionProfilingEnabled()) { - auto valueTypes = ExceptionsProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); _pExceptionsProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pCorProfilerInfo, _pManagedThreadList, _pFrameStore.get(), @@ -165,7 +153,6 @@ bool CorProfilerCallback::InitializeServices() _pAppDomainStore.get(), pRuntimeIdStore, _metricsRegistry); - valuesOffset += static_cast(valueTypes.size()); } // _pCorProfilerInfoEvents must have been set for any CLR events-based profiler to work @@ -176,10 +163,8 @@ bool CorProfilerCallback::InitializeServices() { if (_pCorProfilerInfoLiveHeap != nullptr) { - auto valueTypes = LiveObjectsProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); _pLiveObjectsProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pCorProfilerInfoLiveHeap, _pManagedThreadList, _pFrameStore.get(), @@ -188,12 +173,9 @@ bool CorProfilerCallback::InitializeServices() pRuntimeIdStore, _pConfiguration.get(), _metricsRegistry); - valuesOffset += static_cast(valueTypes.size()); - valueTypes = AllocationsProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); _pAllocationsProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pCorProfilerInfo, _pManagedThreadList, _pFrameStore.get(), @@ -204,7 +186,6 @@ bool CorProfilerCallback::InitializeServices() _pLiveObjectsProvider, _metricsRegistry ); - valuesOffset += static_cast(valueTypes.size()); if (!_pConfiguration->IsAllocationProfilingEnabled()) { @@ -220,10 +201,8 @@ bool CorProfilerCallback::InitializeServices() // check for allocations profiling only (without heap profiling) if (_pConfiguration->IsAllocationProfilingEnabled() && (_pAllocationsProvider == nullptr)) { - auto valueTypes = AllocationsProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); _pAllocationsProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pCorProfilerInfo, _pManagedThreadList, _pFrameStore.get(), @@ -234,15 +213,12 @@ bool CorProfilerCallback::InitializeServices() nullptr, // no listener _metricsRegistry ); - valuesOffset += static_cast(valueTypes.size()); } if (_pConfiguration->IsContentionProfilingEnabled()) { - auto valueTypes = ContentionProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); _pContentionProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pCorProfilerInfo, _pManagedThreadList, _pFrameStore.get(), @@ -252,16 +228,12 @@ bool CorProfilerCallback::InitializeServices() _pConfiguration.get(), _metricsRegistry ); - valuesOffset += static_cast(valueTypes.size()); } if (_pConfiguration->IsGarbageCollectionProfilingEnabled()) { - // Use the same value type for timeline - auto valueTypes = GarbageCollectionProvider::SampleTypeDefinitions; - sampleTypeDefinitions.insert(sampleTypeDefinitions.end(), valueTypes.cbegin(), valueTypes.cend()); _pStopTheWorldProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pFrameStore.get(), _pThreadsCpuManager, _pAppDomainStore.get(), @@ -269,7 +241,7 @@ bool CorProfilerCallback::InitializeServices() _pConfiguration.get() ); _pGarbageCollectionProvider = RegisterService( - valuesOffset, + valueTypeProvider, _pFrameStore.get(), _pThreadsCpuManager, _pAppDomainStore.get(), @@ -277,7 +249,6 @@ bool CorProfilerCallback::InitializeServices() _pConfiguration.get(), _metricsRegistry ); - valuesOffset += static_cast(valueTypes.size()); } else { @@ -311,6 +282,7 @@ bool CorProfilerCallback::InitializeServices() // Avoid iterating twice on all providers in order to inject this value in each constructor // and store it in CollectorBase so it can be used in TransformRawSample (where the sample is created) + auto const& sampleTypeDefinitions = valueTypeProvider.GetValueTypes(); Sample::ValuesCount = sampleTypeDefinitions.size(); // compute enabled profilers based on configuration and receivable CLR events @@ -333,7 +305,7 @@ bool CorProfilerCallback::InitializeServices() // The different elements of the libddprof pipeline are created and linked together // i.e. the exporter is passed to the aggregator and each provider is added to the aggregator. _pExporter = std::make_unique( - std::move(sampleTypeDefinitions), + sampleTypeDefinitions, _pConfiguration.get(), _pApplicationStore, _pRuntimeInfo.get(), diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.cpp index f0f9dfbf0c2d..ebd0f1c0c9a1 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.cpp @@ -17,7 +17,7 @@ std::vector CpuTimeProvider::SampleTypeDefinitions( CpuTimeProvider::CpuTimeProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, IThreadsCpuManager* pThreadsCpuManager, IFrameStore* pFrameStore, IAppDomainStore* pAppDomainStore, @@ -25,6 +25,6 @@ CpuTimeProvider::CpuTimeProvider( IConfiguration* pConfiguration ) : - CollectorBase("CpuTimeProvider", valueOffset, SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) + CollectorBase("CpuTimeProvider", valueTypeProvider.GetOrRegister(SampleTypeDefinitions), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) { } \ No newline at end of file diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.h index af959b94a3de..9ccc0a294c82 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/CpuTimeProvider.h @@ -11,22 +11,23 @@ class IConfiguration; class IFrameStore; class IAppDomainStore; class IRuntimeIdStore; +class SampleValueTypeProvider; class CpuTimeProvider : public CollectorBase // accepts cputime samples { -public: - static std::vector SampleTypeDefinitions; - public: CpuTimeProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, IThreadsCpuManager* pThreadsCpuManager, IFrameStore* pFrameStore, IAppDomainStore* pAppDomainStore, IRuntimeIdStore* pRuntimeIdStore, IConfiguration* pConfiguration ); + +private: + static std::vector SampleTypeDefinitions; }; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj index eda87d74d3eb..3d2ae1bc0876 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj @@ -257,6 +257,7 @@ + @@ -365,6 +366,7 @@ + diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj.filters b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj.filters index ae49fae1ad82..7e90ae29f9fe 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj.filters +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/Datadog.Profiler.Native.vcxproj.filters @@ -45,6 +45,9 @@ {c07c8635-c021-4796-bfac-48f7ee78bf65} + + {9343cd1c-a26e-4f7d-a006-85e4612d0813} + @@ -71,9 +74,6 @@ Metrics - - Profiler-Driver - Profiler-Driver @@ -328,10 +328,22 @@ - - - + + Threads + + + Threads + + + Threads + + + Threads + + + Profiler-Driver + @@ -358,9 +370,6 @@ Metrics - - Profiler-Driver - Profiler-Driver @@ -418,9 +427,6 @@ - - Utils - Utils @@ -496,12 +502,18 @@ Utils - - Utils - Utils + + Threads + + + Threads + + + Profiler-Driver + diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.cpp index 0d1785e3794a..283952e53478 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.cpp @@ -10,6 +10,7 @@ #include "Log.h" #include "OsSpecificApi.h" #include "ScopeFinalizer.h" +#include "SampleValueTypeProvider.h" #include "shared/src/native-src/com_ptr.h" #include "shared/src/native-src/string.h" @@ -20,7 +21,7 @@ std::vector ExceptionsProvider::SampleTypeDefinitions( }); ExceptionsProvider::ExceptionsProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo4* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -30,7 +31,7 @@ ExceptionsProvider::ExceptionsProvider( IRuntimeIdStore* pRuntimeIdStore, MetricsRegistry& metricsRegistry) : - CollectorBase("ExceptionsProvider", valueOffset, SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration), + CollectorBase("ExceptionsProvider", valueTypeProvider.GetOrRegister(SampleTypeDefinitions), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration), _pCorProfilerInfo(pCorProfilerInfo), _pManagedThreadList(pManagedThreadList), _pFrameStore(pFrameStore), diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.h index eb419ea1fbcf..9535268b46df 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/ExceptionsProvider.h @@ -18,17 +18,15 @@ #include "IUpscaleProvider.h" class IConfiguration; +class SampleValueTypeProvider; class ExceptionsProvider : public CollectorBase, public IUpscaleProvider { -public: - static std::vector SampleTypeDefinitions; - public: ExceptionsProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo4* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -56,6 +54,8 @@ class ExceptionsProvider : private: + static std::vector SampleTypeDefinitions; + ICorProfilerInfo4* _pCorProfilerInfo; IManagedThreadList* _pManagedThreadList; IFrameStore* _pFrameStore; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GCBaseRawSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GCBaseRawSample.h index c3be6c7ab21e..2443b6e91af1 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GCBaseRawSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GCBaseRawSample.h @@ -35,9 +35,10 @@ class GCBaseRawSample : public RawSample // This base class is in charge of storing garbage collection number and generation as labels // and fill up the callstack based on generation. // The default value is the Duration field; derived class could override by implementing GetValue() - inline void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const override + inline void OnTransform(std::shared_ptr& sample, std::vector const& valueOffsets) const override { - uint32_t durationIndex = valueOffset; + assert(valueOffsets.size() == 1); + auto durationIndex = valueOffsets[0]; sample->AddValue(GetValue(), durationIndex); @@ -47,7 +48,7 @@ class GCBaseRawSample : public RawSample BuildCallStack(sample, Generation); // let child classes transform additional fields if needed - DoAdditionalTransform(sample, valueOffset); + DoAdditionalTransform(sample, valueOffsets); } // Each derived class provides the duration to store as the value for this sample @@ -58,7 +59,7 @@ class GCBaseRawSample : public RawSample } // Derived classes are expected to set the event type + any additional field as label - virtual void DoAdditionalTransform(std::shared_ptr sample, uint32_t valueOffset) const = 0; + virtual void DoAdditionalTransform(std::shared_ptr sample, std::vector const& valueOffset) const = 0; public: int32_t Number; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.cpp index 6ee5d03658ba..ad608d9e18b7 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.cpp @@ -3,6 +3,7 @@ #include "GarbageCollectionProvider.h" +#include "SampleValueTypeProvider.h" std::vector GarbageCollectionProvider::SampleTypeDefinitions( { @@ -10,7 +11,7 @@ std::vector GarbageCollectionProvider::SampleTypeDefinitions( }); GarbageCollectionProvider::GarbageCollectionProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, IFrameStore* pFrameStore, IThreadsCpuManager* pThreadsCpuManager, IAppDomainStore* pAppDomainStore, @@ -18,7 +19,7 @@ GarbageCollectionProvider::GarbageCollectionProvider( IConfiguration* pConfiguration, MetricsRegistry& metricsRegistry) : - CollectorBase("GarbageCollectorProvider", valueOffset, SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) + CollectorBase("GarbageCollectorProvider", valueTypeProvider.GetOrRegister(SampleTypeDefinitions), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) { _gen0CountMetric = metricsRegistry.GetOrRegister("dotnet_gc_gen0"); _gen1CountMetric = metricsRegistry.GetOrRegister("dotnet_gc_gen1"); diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.h index 0fdcf5d1d10c..d96c43766cdd 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/GarbageCollectionProvider.h @@ -4,12 +4,14 @@ #pragma once #include "CollectorBase.h" + #include "IGarbageCollectionsListener.h" #include "RawGarbageCollectionSample.h" #include "MetricsRegistry.h" #include "CounterMetric.h" #include "MeanMaxMetric.h" +class SampleValueTypeProvider; class GarbageCollectionProvider : public CollectorBase, @@ -20,7 +22,7 @@ class GarbageCollectionProvider public: GarbageCollectionProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, IFrameStore* pFrameStore, IThreadsCpuManager* pThreadsCpuManager, IAppDomainStore* pAppDomainStore, diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IUpscaleProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IUpscaleProvider.h index d828e4a7d6bf..0cef2d3960ae 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IUpscaleProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/IUpscaleProvider.h @@ -8,13 +8,14 @@ #include #include "GroupSampler.h" +#include "SampleValueTypeProvider.h" using UpscaleStringGroup = UpscaleGroupInfo; struct UpscalingInfo { public: - std::vector const& Offsets; + std::vector const& Offsets; std::string LabelName; std::vector UpscaleGroups; }; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.cpp index 765a67e276a1..b551cbda0bd9 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.cpp @@ -63,7 +63,7 @@ std::string const LibddprofExporter::ProfileExtension = ".pprof"; std::string const LibddprofExporter::AllocationsExtension = ".balloc"; LibddprofExporter::LibddprofExporter( - std::vector&& sampleTypeDefinitions, + std::vector sampleTypeDefinitions, IConfiguration* configuration, IApplicationStore* applicationStore, IRuntimeInfo* runtimeInfo, diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.h index 4d4fc16132dc..c9029a6a17cf 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LibddprofExporter.h @@ -36,7 +36,7 @@ class LibddprofExporter : public IExporter { public: LibddprofExporter( - std::vector&& sampleTypeDefinitions, + std::vector sampleTypeDefinitions, IConfiguration* configuration, IApplicationStore* applicationStore, IRuntimeInfo* runtimeInfo, diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.cpp index f63ac5ea4cbd..be20add08a62 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.cpp @@ -9,6 +9,7 @@ #include "LiveObjectsProvider.h" #include "OpSysTools.h" #include "Sample.h" +#include "SampleValueTypeProvider.h" std::vector LiveObjectsProvider::SampleTypeDefinitions( { @@ -23,7 +24,7 @@ const std::string LiveObjectsProvider::Gen2("2"); LiveObjectsProvider::LiveObjectsProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo13* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -33,7 +34,6 @@ LiveObjectsProvider::LiveObjectsProvider( IConfiguration* pConfiguration, MetricsRegistry& metricsRegistry) : - _valueOffset(valueOffset), _pCorProfilerInfo(pCorProfilerInfo), _pFrameStore(pFrameStore), _pAppDomainStore(pAppDomainStore), @@ -41,7 +41,7 @@ LiveObjectsProvider::LiveObjectsProvider( _isTimestampsAsLabelEnabled(pConfiguration->IsTimestampsAsLabelEnabled()) { _pAllocationsProvider = std::make_unique( - valueOffset, // the values (allocation count and size are stored in the live object values, not tha allocation values) + valueTypeProvider.GetOrRegister(SampleTypeDefinitions), pCorProfilerInfo, pManagedThreadList, pFrameStore, diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.h index 3024d11fe5e6..1549656b00e2 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/LiveObjectsProvider.h @@ -23,18 +23,16 @@ class IAppDomainStore; class IRuntimeIdStore; class IConfiguration; class ISampledAllocationsListener; +class SampleValueTypeProvider; class LiveObjectsProvider : public IService, public IBatchedSamplesProvider, public ISampledAllocationsListener, public IGarbageCollectionsListener { -public: - static std::vector SampleTypeDefinitions; - public: LiveObjectsProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, ICorProfilerInfo13* pCorProfilerInfo, IManagedThreadList* pManagedThreadList, IFrameStore* pFrameStore, @@ -78,7 +76,8 @@ class LiveObjectsProvider : public IService, bool IsAlive(ObjectHandleID handle) const; private: - uint32_t _valueOffset = 0; + static std::vector SampleTypeDefinitions; + ICorProfilerInfo13* _pCorProfilerInfo = nullptr; IFrameStore* _pFrameStore = nullptr; IAppDomainStore* _pAppDomainStore = nullptr; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawAllocationSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawAllocationSample.h index 38d5ea6e2269..7436b16bf6cd 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawAllocationSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawAllocationSample.h @@ -34,10 +34,11 @@ class RawAllocationSample : public RawSample return *this; } - inline void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const override + inline void OnTransform(std::shared_ptr& sample, std::vector const& valueOffsets) const override { - uint32_t allocationCountIndex = valueOffset; - uint32_t allocationSizeIndex = valueOffset + 1; + assert(valueOffsets.size() == 2); + auto allocationCountIndex = valueOffsets[0]; + auto allocationSizeIndex = valueOffsets[1]; sample->AddValue(1, allocationCountIndex); sample->AddValue(AllocationSize, allocationSizeIndex); diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawContentionSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawContentionSample.h index ce70305989cc..3213c829bcbe 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawContentionSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawContentionSample.h @@ -35,10 +35,11 @@ class RawContentionSample : public RawSample return *this; } - void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const override + void OnTransform(std::shared_ptr& sample, std::vector const& valueOffsets) const override { - uint32_t contentionCountIndex = valueOffset; - uint32_t contentionDurationIndex = valueOffset + 1; + assert(valueOffsets.size() == 2); + auto contentionCountIndex = valueOffsets[0]; + auto contentionDurationIndex = valueOffsets[1]; sample->AddLabel(Label{BucketLabelName, std::move(Bucket)}); sample->AddValue(1, contentionCountIndex); diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawCpuSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawCpuSample.h index b613b87ae850..cf48135a2d82 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawCpuSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawCpuSample.h @@ -28,9 +28,10 @@ class RawCpuSample : public RawSample return *this; } - inline void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const override + inline void OnTransform(std::shared_ptr& sample, std::vector const& valueOffsets) const override { - sample->AddValue(Duration * 1000000, valueOffset); + assert(valueOffsets.size() == 1); + sample->AddValue(Duration * 1000000, valueOffsets[0]); } std::uint64_t Duration; // in milliseconds diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawExceptionSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawExceptionSample.h index 208823cf222f..42cb21e82dd7 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawExceptionSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawExceptionSample.h @@ -26,9 +26,10 @@ class RawExceptionSample : public RawSample return *this; } - inline void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const override + inline void OnTransform(std::shared_ptr& sample, std::vector const& valueOffsets) const override { - sample->AddValue(1, valueOffset); + assert(valueOffsets.size() == 1); + sample->AddValue(1, valueOffsets[0]); sample->AddLabel(Label(Sample::ExceptionMessageLabel, ExceptionMessage)); sample->AddLabel(Label(Sample::ExceptionTypeLabel, ExceptionType)); } diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawGarbageCollectionSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawGarbageCollectionSample.h index e768c6268db2..06c4a54a3d1a 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawGarbageCollectionSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawGarbageCollectionSample.h @@ -46,7 +46,7 @@ class RawGarbageCollectionSample : public GCBaseRawSample return TotalDuration; } - inline void DoAdditionalTransform(std::shared_ptr sample, uint32_t valueOffset) const override + inline void DoAdditionalTransform(std::shared_ptr sample, std::vector const& valueOffsets) const override { sample->AddLabel(Label(Sample::GarbageCollectionReasonLabel, GetReasonText())); sample->AddLabel(Label(Sample::GarbageCollectionTypeLabel, GetTypeText())); diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawSample.h index b198c7308fd0..95cd31da503a 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawSample.h @@ -10,7 +10,7 @@ #include "cor.h" #include "corprof.h" #include "ManagedThreadInfo.h" - +#include "SampleValueTypeProvider.h" class Sample; @@ -27,7 +27,7 @@ class RawSample RawSample& operator=(RawSample&& other) noexcept; // set values and additional labels on target sample - virtual void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const = 0; + virtual void OnTransform(std::shared_ptr& sample, std::vector const& valueOffset) const = 0; public: std::uint64_t Timestamp; // _unixTimeUtc; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawStopTheWorldSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawStopTheWorldSample.h index 03a009ce342e..2973235a22f6 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawStopTheWorldSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawStopTheWorldSample.h @@ -16,7 +16,7 @@ class RawStopTheWorldSample : public GCBaseRawSample RawStopTheWorldSample& operator=(RawStopTheWorldSample&& other) noexcept = default; // Duration is the suspension time so default sample value - void DoAdditionalTransform(std::shared_ptr sample, uint32_t valueOffset) const override + void DoAdditionalTransform(std::shared_ptr sample, std::vector const& valueOffsets) const override { // set event type sample->AddLabel(Label(Sample::TimelineEventTypeLabel, Sample::TimelineEventTypeStopTheWorld)); diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawWallTimeSample.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawWallTimeSample.h index c49bc16a0a95..a05edb3b462e 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawWallTimeSample.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/RawWallTimeSample.h @@ -30,9 +30,10 @@ class RawWallTimeSample : public RawSample return *this; } - inline void OnTransform(std::shared_ptr& sample, uint32_t valueOffset) const override + inline void OnTransform(std::shared_ptr& sample, std::vector const& valueOffsets) const override { - sample->AddValue(Duration, valueOffset); + assert(valueOffsets.size() == 1); + sample->AddValue(Duration, valueOffsets[0]); } std::uint64_t Duration; // in nanoseconds diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.cpp new file mode 100644 index 000000000000..3481aa7002c8 --- /dev/null +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.cpp @@ -0,0 +1,54 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc. + +#include "SampleValueTypeProvider.h" + +#include +#include + + +SampleValueTypeProvider::SampleValueTypeProvider() +{ + _sampleTypeDefinitions.reserve(16); +} + +std::vector SampleValueTypeProvider::GetOrRegister(std::vector const& valueTypes) +{ + std::vector offsets; + offsets.reserve(valueTypes.size()); + + for (auto const& valueType : valueTypes) + { + auto idx = GetOffset(valueType); + if (idx == -1) + { + idx = _sampleTypeDefinitions.size(); + _sampleTypeDefinitions.push_back(valueType); + } + offsets.push_back(idx); + } + return offsets; +} + +std::vector const& SampleValueTypeProvider::GetValueTypes() +{ + return _sampleTypeDefinitions; +} + +std::int8_t SampleValueTypeProvider::GetOffset(SampleValueType const& valueType) +{ + for (auto i = 0; i < _sampleTypeDefinitions.size(); i++) + { + auto const& current = _sampleTypeDefinitions[i]; + if (valueType.Name == current.Name) + { + if (valueType.Unit != current.Unit) + { + throw std::runtime_error("Cannot have the value type name with different unit: " + valueType.Unit + " != " + current.Unit); + } + return i; + } + } + return -1; +} + diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.h new file mode 100644 index 000000000000..2e4d16864c9e --- /dev/null +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/SampleValueTypeProvider.h @@ -0,0 +1,29 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc. + +#pragma once + +#include "Sample.h" + +#include +#include +#include + +// Non-thread-safe class +// Must be called underlock or make it thread-safe if needed. +// For now, keep it simple +class SampleValueTypeProvider +{ +public: + using Offset = std::uintptr_t; // Use std::uintptr_t to make it work with with libdatadog which is expected int32 or int64 indices type + + SampleValueTypeProvider(); + + std::vector GetOrRegister(std::vector const& valueType); + std::vector const& GetValueTypes(); + +private: + std::int8_t GetOffset(SampleValueType const& valueType); + + std::vector _sampleTypeDefinitions; +}; diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.cpp index 04a3515a9c64..3878834796f7 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.cpp @@ -13,19 +13,21 @@ #include "IThreadsCpuManager.h" #include "Log.h" #include "OsSpecificApi.h" +#include "SampleValueTypeProvider.h" + #include "shared/src/native-src/com_ptr.h" #include "shared/src/native-src/string.h" StopTheWorldGCProvider::StopTheWorldGCProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, IFrameStore* pFrameStore, IThreadsCpuManager* pThreadsCpuManager, IAppDomainStore* pAppDomainStore, IRuntimeIdStore* pRuntimeIdStore, IConfiguration* pConfiguration) : - CollectorBase("StopTheWorldGCProvider", valueOffset, GarbageCollectionProvider::SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) + CollectorBase("StopTheWorldGCProvider", valueTypeProvider.GetOrRegister(GarbageCollectionProvider::SampleTypeDefinitions), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) { } diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.h index 58d81d0fd230..79cfcd55a1b6 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StopTheWorldGCProvider.h @@ -12,18 +12,17 @@ class IThreadsCpuManager; class IAppDomainStore; class IRuntimeIdStore; class IConfiguration; - +class SampleValueTypeProvider; class StopTheWorldGCProvider - : - public CollectorBase, - public IGCSuspensionsListener + : public CollectorBase, + public IGCSuspensionsListener { -// use the same sample type definition as the GarbageCollectorProvider + // use the same sample type definition as the GarbageCollectorProvider public: StopTheWorldGCProvider( - uint32_t valueOffset, + SampleValueTypeProvider& valueTypeProvider, IFrameStore* pFrameStore, IThreadsCpuManager* pThreadsCpuManager, IAppDomainStore* pAppDomainStore, diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.cpp index 13c138320398..65373f6f4790 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.cpp @@ -9,6 +9,7 @@ #include "IThreadsCpuManager.h" #include "RawWallTimeSample.h" +class SampleValueTypeProvider; std::vector WallTimeProvider::SampleTypeDefinitions( { @@ -17,7 +18,7 @@ std::vector WallTimeProvider::SampleTypeDefinitions( ); WallTimeProvider::WallTimeProvider( - uint32_t valueOffset, + SampleValueTypeProvider& sampleValueTypeProvider, IThreadsCpuManager* pThreadsCpuManager, IFrameStore* pFrameStore, IAppDomainStore* pAppDomainStore, @@ -25,6 +26,6 @@ WallTimeProvider::WallTimeProvider( IConfiguration* pConfiguration ) : - CollectorBase("WallTimeProvider", valueOffset, SampleTypeDefinitions.size(), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) + CollectorBase("WallTimeProvider", sampleValueTypeProvider.GetOrRegister(SampleTypeDefinitions), pThreadsCpuManager, pFrameStore, pAppDomainStore, pRuntimeIdStore, pConfiguration) { } diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.h index 73f7c6457775..3331760b0047 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/WallTimeProvider.h @@ -13,22 +13,23 @@ class IAppDomainStore; class IRuntimeIdStore; class IThreadsCpuManager; class IConfiguration; +class SampleValueTypeProvider; class WallTimeProvider : public CollectorBase // accepts raw walltime samples { -public: - static std::vector SampleTypeDefinitions; - public: WallTimeProvider( - uint32_t valueOffset, + SampleValueTypeProvider& sampleValueTypeProvider, IThreadsCpuManager* pThreadsCpuManager, IFrameStore* pFrameStore, IAppDomainStore* pAppDomainStore, IRuntimeIdStore* pRuntimeIdStore, IConfiguration* pConfiguration ); + +private: + static std::vector SampleTypeDefinitions; }; diff --git a/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj b/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj index 312dfc0d1e9c..15ea6cde4b38 100644 --- a/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj +++ b/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj @@ -66,6 +66,7 @@ + diff --git a/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj.filters b/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj.filters index 5b3c9d648ce6..3969c4cad8b0 100644 --- a/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj.filters +++ b/profiler/test/Datadog.Profiler.Native.Tests/Datadog.Profiler.Native.Tests.vcxproj.filters @@ -13,6 +13,9 @@ {300fa0f9-3712-4092-bb4a-382f97cb0c58} + + {ecc1aba4-974c-43eb-99cb-8a79be4132ea} + @@ -110,11 +113,24 @@ Tests - - - - - + + Tests + + + External Sources + + + External Sources + + + External Sources + + + External Sources + + + External Sources + diff --git a/profiler/test/Datadog.Profiler.Native.Tests/ProviderTest.cpp b/profiler/test/Datadog.Profiler.Native.Tests/ProviderTest.cpp index 94c690cf3ff7..adc319a897db 100644 --- a/profiler/test/Datadog.Profiler.Native.Tests/ProviderTest.cpp +++ b/profiler/test/Datadog.Profiler.Native.Tests/ProviderTest.cpp @@ -18,6 +18,7 @@ #include "CpuTimeProvider.h" #include "RawCpuSample.h" #include "RawWallTimeSample.h" +#include "SampleValueTypeProvider.h" #include "ThreadsCpuManagerHelper.h" using namespace std::chrono_literals; @@ -86,13 +87,14 @@ TEST(WallTimeProviderTest, CheckNoMissingSample) auto frameStore = FrameStoreHelper(true, "Frame", 1); auto appDomainStore = AppDomainStoreHelper(2); auto threadscpuManager = ThreadsCpuManagerHelper(); + auto valueTypeProvider = SampleValueTypeProvider(); MockRuntimeIdStore runtimeIdStore; auto [configuration, mockConfiguration] = CreateConfiguration(); std::string expectedRuntimeId = "MyRid"; EXPECT_CALL(runtimeIdStore, GetId(::testing::_)).WillRepeatedly(::testing::Return(expectedRuntimeId.c_str())); - WallTimeProvider provider(0, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); + WallTimeProvider provider(valueTypeProvider, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); Sample::ValuesCount = 1; provider.Start(); @@ -115,6 +117,7 @@ TEST(WallTimeProviderTest, CheckAppDomainInfoAndRuntimeId) auto appDomainStore = AppDomainStoreHelper(2); auto [configuration, mockConfiguration] = CreateConfiguration(); auto threadscpuManager = ThreadsCpuManagerHelper(); + auto valueTypeProvider = SampleValueTypeProvider(); MockRuntimeIdStore runtimeIdStore; std::string firstExpectedRuntimeId = "MyRid"; @@ -123,7 +126,7 @@ TEST(WallTimeProviderTest, CheckAppDomainInfoAndRuntimeId) std::string secondExpectedRuntimeId = "OtherRid"; EXPECT_CALL(runtimeIdStore, GetId(static_cast(2))).WillRepeatedly(::testing::Return(secondExpectedRuntimeId.c_str())); - WallTimeProvider provider(0, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); + WallTimeProvider provider(valueTypeProvider, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); Sample::ValuesCount = 1; provider.Start(); @@ -198,12 +201,13 @@ TEST(WallTimeProviderTest, CheckFrames) auto appDomainStore = AppDomainStoreHelper(1); auto [configuration, mockConfiguration] = CreateConfiguration(); auto threadscpuManager = ThreadsCpuManagerHelper(); + auto valueTypeProvider = SampleValueTypeProvider(); MockRuntimeIdStore runtimeIdStore; std::string expectedRuntimeId = "MyRid"; EXPECT_CALL(runtimeIdStore, GetId(static_cast(1))).WillRepeatedly(::testing::Return(expectedRuntimeId.c_str())); - WallTimeProvider provider(0, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); + WallTimeProvider provider(valueTypeProvider, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); Sample::ValuesCount = 1; provider.Start(); @@ -256,12 +260,13 @@ TEST(WallTimeProviderTest, CheckValuesAndTimestamp) auto appDomainStore = AppDomainStoreHelper(1); auto [configuration, mockConfiguration] = CreateConfiguration(); auto threadscpuManager = ThreadsCpuManagerHelper(); + auto valueTypeProvider = SampleValueTypeProvider(); MockRuntimeIdStore runtimeIdStore; std::string expectedRuntimeId = "MyRid"; EXPECT_CALL(runtimeIdStore, GetId(::testing::_)).WillRepeatedly(::testing::Return(expectedRuntimeId.c_str())); - WallTimeProvider provider(0, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); + WallTimeProvider provider(valueTypeProvider, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); Sample::ValuesCount = 1; provider.Start(); @@ -299,10 +304,11 @@ TEST(CpuTimeProviderTest, CheckValuesAndTimestamp) auto frameStore = FrameStoreHelper(true, "Frame", 1); auto appDomainStore = AppDomainStoreHelper(1); auto threadscpuManager = ThreadsCpuManagerHelper(); + auto valueTypeProvider = SampleValueTypeProvider(); RuntimeIdStoreHelper runtimeIdStore; auto [configuration, mockConfiguration] = CreateConfiguration(); - CpuTimeProvider provider(0, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); + CpuTimeProvider provider(valueTypeProvider, &threadscpuManager, &frameStore, &appDomainStore, &runtimeIdStore, &mockConfiguration); Sample::ValuesCount = 1; provider.Start(); diff --git a/profiler/test/Datadog.Profiler.Native.Tests/SampleValueTypeProviderTest.cpp b/profiler/test/Datadog.Profiler.Native.Tests/SampleValueTypeProviderTest.cpp new file mode 100644 index 000000000000..684c7ec82a90 --- /dev/null +++ b/profiler/test/Datadog.Profiler.Native.Tests/SampleValueTypeProviderTest.cpp @@ -0,0 +1,97 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc. + +#include "SampleValueTypeProvider.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +static const SampleValueType CpuValueType = {"cpu", "nanoseconds"}; +static const SampleValueType WallTimeValueType = {"walltime", "nanoseconds"}; +static const SampleValueType ExceptionValueType = {"exception", "count"}; + +testing::AssertionResult AreSampleValueTypeEqual(SampleValueType const& v1, SampleValueType const& v2) +{ + if (v1.Name == v2.Name && v1.Unit == v2.Unit) + return testing::AssertionSuccess(); + else + return testing::AssertionFailure() << " sample value type differs"; +} + +#define ASSERT_DEFINITIONS(expectedDefinitions, definitions) \ + ASSERT_EQ(expectedDefinitions.size(), definitions.size()); \ + for (auto i = 0; i < expectedDefinitions.size(); i++) \ + ASSERT_PRED2(AreSampleValueTypeEqual, expectedDefinitions[i], definitions[i]); + +#define ASSERT_OFFSETS(expectedOffsets, offsets) \ + ASSERT_EQ(expectedOffsets.size(), offsets.size()); \ + for (auto i = 0; i < expectedOffsets.size(); i++) \ + ASSERT_EQ(expectedOffsets[i], offsets[i]); + +using ValueOffsets = std::vector; + +TEST(SampleValueTypeProvider, RegisterValueTypes) +{ + SampleValueTypeProvider provider; + auto const valueTypes = std::vector{CpuValueType, WallTimeValueType}; + + auto offsets = provider.GetOrRegister(valueTypes); + + ASSERT_OFFSETS((ValueOffsets{0, 1}), offsets); + // cpu offset --^ ^-- walltime offset + + ASSERT_DEFINITIONS(valueTypes, provider.GetValueTypes()); +} + +TEST(SampleValueTypeProvider, RegisterTwiceSameValueType) +{ + SampleValueTypeProvider provider; + auto valueTypes = std::vector{CpuValueType, WallTimeValueType}; + + auto offsets = provider.GetOrRegister(valueTypes); + + ASSERT_OFFSETS((ValueOffsets{0, 1}), offsets); + // cpu offset --^ ^-- walltime offset + + ASSERT_DEFINITIONS(valueTypes, provider.GetValueTypes()); + + // Register walltime a second time + + auto alreadyRegisteredValueType = std::vector{WallTimeValueType}; + auto offsets2 = provider.GetOrRegister(alreadyRegisteredValueType); + + ASSERT_OFFSETS((ValueOffsets{1}), offsets2); // walltime offset did not changed + + ASSERT_DEFINITIONS(valueTypes, provider.GetValueTypes()); +} + +TEST(SampleValueTypeProvider, CheckSequentialRegistration) +{ + SampleValueTypeProvider provider; + auto valueTypes = std::vector{CpuValueType, WallTimeValueType}; + + auto offsets = provider.GetOrRegister(valueTypes); + ASSERT_DEFINITIONS(valueTypes, provider.GetValueTypes()); + + // Register ExceptionValueType + auto anotherValuetype = std::vector{ExceptionValueType}; + + auto offsets2 = provider.GetOrRegister(anotherValuetype); + ASSERT_OFFSETS((ValueOffsets{2}), offsets2); + + ASSERT_DEFINITIONS((std::vector{CpuValueType, WallTimeValueType, ExceptionValueType}), provider.GetValueTypes()); +} + +TEST(SampleValueTypeProvider, EnsureThrowIfAddValueTypeSameNameButDifferentUnit) +{ + SampleValueTypeProvider provider; + auto valueTypes = std::vector{CpuValueType}; + + auto offsets = provider.GetOrRegister(valueTypes); + ASSERT_DEFINITIONS(valueTypes, provider.GetValueTypes()); + + // Register a cpu value but with different unit + auto anotherValuetype = std::vector{{"cpu", "non-sense-unit"}}; + + EXPECT_THROW(provider.GetOrRegister(anotherValuetype), std::runtime_error); +} \ No newline at end of file