diff --git a/tracer/src/Datadog.Trace.ClrProfiler.Native/always_on_profiler.cpp b/tracer/src/Datadog.Trace.ClrProfiler.Native/always_on_profiler.cpp index e1730bcf1..298a3f66e 100644 --- a/tracer/src/Datadog.Trace.ClrProfiler.Native/always_on_profiler.cpp +++ b/tracer/src/Datadog.Trace.ClrProfiler.Native/always_on_profiler.cpp @@ -30,6 +30,11 @@ constexpr auto max_function_name_cache_size = 7000; constexpr auto param_name_max_len = 260; constexpr auto generic_params_max_len = 20; constexpr auto unknown = WStr("Unknown"); +constexpr auto params_separator = WStr(", "); +constexpr auto generic_params_opening_brace = WStr("["); +constexpr auto generic_params_closing_brace = WStr("]"); +constexpr auto function_params_opening_brace = WStr("("); +constexpr auto function_params_closing_brace = WStr(")"); // If you squint you can make out that the original bones of this came from sample code provided by the dotnet project: // https://github.com/dotnet/samples/blob/2cf486af936261b04a438ea44779cdc26c613f98/core/profiling/stacksampling/src/sampler.cpp @@ -363,11 +368,11 @@ class SamplingHelper result.append(unknown_list_of_arguments); return; } + hr = pIMDImport->EnumGenericParams(&functionGenParamsIter, functionToken, functionGenericParams, generic_params_max_len, &functionGenParamsCount); pIMDImport->CloseEnum(functionGenParamsIter); - if (FAILED(hr)) { Logger::Debug("Method generic parameters enumeration failed. HRESULT=0x", std::setfill('0'), std::setw(8), @@ -375,6 +380,34 @@ class SamplingHelper result.append(unknown_list_of_arguments); return; } + + if (functionGenParamsCount > 0) + { + result.append(generic_params_opening_brace); + for (ULONG i = 0; i < functionGenParamsCount; ++i) + { + if (i != 0) + { + result.append(params_separator); + } + + WCHAR param_type_name[param_name_max_len]{}; + ULONG pch_name = 0; + const auto hr = + pIMDImport->GetGenericParamProps(functionGenericParams[i], nullptr, nullptr, nullptr, nullptr, + param_type_name, param_name_max_len, &pch_name); + if (FAILED(hr)) + { + Logger::Debug("GetGenericParamProps failed. HRESULT=0x", std::setfill('0'), std::setw(8), std::hex, hr); + result.append(unknown); + } + else + { + result.append(param_type_name); + } + } + result.append(generic_params_closing_brace); + } // try to list arguments type FunctionMethodSignature functionMethodSignature = function_info.method_signature; @@ -387,19 +420,19 @@ class SamplingHelper else { const auto& arguments = functionMethodSignature.GetMethodArguments(); - result.append(WStr("(")); + result.append(function_params_opening_brace); for (ULONG i = 0; i < arguments.size(); i++) { if (i != 0) { - result.append(WStr(", ")); + result.append(params_separator); } auto& currentArg = arguments[i]; PCCOR_SIGNATURE pbCur = ¤tArg.pbBase[currentArg.offset]; result.append(GetSigTypeTokName(pbCur, pIMDImport, classGenericParams, functionGenericParams)); } - result.append(WStr(")")); + result.append(function_params_closing_brace); } } @@ -415,7 +448,7 @@ class SamplingHelper WCHAR param_type_name[param_name_max_len]{}; ULONG pch_name = 0; const auto hr = pImport->GetGenericParamProps(genericParameters[num], nullptr, nullptr, nullptr, nullptr, - param_type_name, param_name_max_len - 1, &pch_name); + param_type_name, param_name_max_len, &pch_name); if (FAILED(hr)) { Logger::Debug("GetGenericParamProps failed. HRESULT=0x", std::setfill('0'), std::setw(8), @@ -521,7 +554,7 @@ class SamplingHelper { pbCur++; tokenName = GetSigTypeTokName(pbCur, pImport, classParams, methodParams); - tokenName += WStr("["); + tokenName += generic_params_opening_brace; ULONG num = 0; pbCur += CorSigUncompressData(pbCur, &num); for (ULONG i = 0; i < num; i++) @@ -529,10 +562,10 @@ class SamplingHelper tokenName += GetSigTypeTokName(pbCur, pImport, classParams, methodParams); if (i != num - 1) { - tokenName += WStr(","); + tokenName += params_separator; } } - tokenName += WStr("]"); + tokenName += generic_params_closing_brace; break; } case ELEMENT_TYPE_MVAR: diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AlwaysOnProfilerTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AlwaysOnProfilerTests.cs index aedbb5166..19ce29263 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AlwaysOnProfilerTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AlwaysOnProfilerTests.cs @@ -104,11 +104,13 @@ private static bool ContainStackTraceForClassHierarchy(LogRecord logRecord) { return logRecord.Body.StringValue.Contains( "\tat System.Threading.Thread.Sleep(System.TimeSpan)\n" + - "\tat My.Custom.Test.Namespace.ClassD`1.GenericMethodDFromGenericClass(TClass, TMethod, TMethod2)\n" + + "\tat My.Custom.Test.Namespace.ClassE`1.GenericMethodDFromGenericClass[TMethod, TMethod2](TClass, TMethod, TMethod2)\n" + + "\tat My.Custom.Test.Namespace.ClassD`21.MethodD(T01, T02, T03, T04, T05, T06, T07, T08, T09, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Unknown)\n" + + "\tat My.Custom.Test.Namespace.GenericClassC`1.GenericMethodCFromGenericClass[T01, T02, T03, T04, T05, T06, T07, T08, T09, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20](T01, T02, T03, T04, T05, T06, T07, T08, T09, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Unknown)\n" + "\tat My.Custom.Test.Namespace.GenericClassC`1.GenericMethodCFromGenericClass(T)\n" + - "\tat My.Custom.Test.Namespace.ClassA.InternalClassB`2.DoubleInternalClassB.TripleInternalClassB`1.MethodB(System.String, TC[], TB, TD, System.Collections.Generic.IList`1[TA])\n" + - "\tat My.Custom.Test.Namespace.ClassA.g__Action|4_0(System.String)\n" + - "\tat My.Custom.Test.Namespace.ClassA.MethodAOthers(System.String, System.Object, My.Custom.Test.Namespace.CustomClass, My.Custom.Test.Namespace.CustomStruct, My.Custom.Test.Namespace.CustomClass[], My.Custom.Test.Namespace.CustomStruct[], System.Collections.Generic.List`1[T])\n" + + "\tat My.Custom.Test.Namespace.ClassA.InternalClassB`2.DoubleInternalClassB.TripleInternalClassB`1.MethodB[TB](System.String, TC[], TB, TD, System.Collections.Generic.IList`1[TA], System.Collections.Generic.IList`1[System.String])\n" + + "\tat My.Custom.Test.Namespace.ClassA.g__Action|4_0[T](System.String)\n" + + "\tat My.Custom.Test.Namespace.ClassA.MethodAOthers[T](System.String, System.Object, My.Custom.Test.Namespace.CustomClass, My.Custom.Test.Namespace.CustomStruct, My.Custom.Test.Namespace.CustomClass[], My.Custom.Test.Namespace.CustomStruct[], System.Collections.Generic.List`1[T])\n" + "\tat My.Custom.Test.Namespace.ClassA.MethodAFloats(System.Single, System.Double)\n" + "\tat My.Custom.Test.Namespace.ClassA.MethodAInts(System.UInt16, System.Int16, System.UInt32, System.Int32, System.UInt64, System.Int64, System.IntPtr, System.UIntPtr)\n" + "\tat My.Custom.Test.Namespace.ClassA.MethodABytes(System.Boolean, System.Char, System.SByte, System.Byte)\n" + diff --git a/tracer/test/test-applications/integrations/Samples.AlwaysOnProfiler/Program.cs b/tracer/test/test-applications/integrations/Samples.AlwaysOnProfiler/Program.cs index 005338d0a..b7e5355f9 100644 --- a/tracer/test/test-applications/integrations/Samples.AlwaysOnProfiler/Program.cs +++ b/tracer/test/test-applications/integrations/Samples.AlwaysOnProfiler/Program.cs @@ -78,7 +78,7 @@ public static void MethodAOthers( CustomStruct[] structArray, List genericList) { - void Action(string s) => InternalClassB.DoubleInternalClassB.TripleInternalClassB.MethodB(s, new int[] { 3 }, TimeSpan.Zero, 0, new List{"a"}); + void Action(string s) => InternalClassB.DoubleInternalClassB.TripleInternalClassB.MethodB(s, new int[] { 3 }, TimeSpan.Zero, 0, new List{"a"}, new List(0)); Action("test arg"); } @@ -88,7 +88,7 @@ internal static class DoubleInternalClassB { internal static class TripleInternalClassB { - public static void MethodB(string testArg, TC[] a, TB b, TD t, IList c) + public static void MethodB(string testArg, TC[] a, TB b, TD t, IList c, IList d) { GenericClassC.GenericMethodCFromGenericClass(testArg); } @@ -101,11 +101,28 @@ internal static class GenericClassC { public static void GenericMethodCFromGenericClass(T arg) { - ClassD.GenericMethodDFromGenericClass(TimeSpan.MaxValue, arg, 1); + GenericMethodCFromGenericClass(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21); + } + + public static void GenericMethodCFromGenericClass(T01 p01, T02 p02, T03 p03, T04 p04, T05 p05, T06 p06, T07 p07, T08 p08, T09 p09, T10 p10, T11 p11, T12 p12, T13 p13, T14 p14, T15 p15, T16 p16, T17 p17, T18 p18, T19 p19, T20 p20, T21 p21) + { + // Always on profiler supports fetching at most 20 generic arguments. This method covers scenario where there are more than 20 parameters. + ClassD.MethodD(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21); + } + + } + + internal static class ClassD + { + // Always on profiler supports fetching at most 20 generic arguments. This class covers scenario where there are more than 20 parameters. + public static void MethodD(T01 p01, T02 p02, T03 p03, T04 p04, T05 p05, T06 p06, T07 p07, T08 p08, T09 p09, T10 p10, T11 p11, T12 p12, T13 p13, T14 p14, T15 p15, T16 p16, T17 p17, T18 p18, T19 p19, T20 p20, T21 p21) + { + ClassE.GenericMethodDFromGenericClass(TimeSpan.MaxValue, p01, 1); + } } - internal static class ClassD + internal static class ClassE { public static void GenericMethodDFromGenericClass(TClass classArg, TMethod methodArg, TMethod2 additionalArg) {