Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[repo/Runtime] Prepare to .NET9 #2281

Merged
merged 10 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 24 additions & 29 deletions src/OpenTelemetry.Instrumentation.Runtime/RuntimeMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ internal sealed class RuntimeMetrics
#endif
private const int NumberOfGenerations = 3;

private static readonly string[] GenNames = new string[] { "gen0", "gen1", "gen2", "loh", "poh" };
private static readonly string[] GenNames = ["gen0", "gen1", "gen2", "loh", "poh"];
#if NET
Copy link
Contributor

@xiang17 xiang17 Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this #if NET added by some automation tool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, it was manual modification. isGcInfoAvailable was marked as unused on some target frameworks. I manually modified it.

private static bool isGcInfoAvailable;
#endif

static RuntimeMetrics()
{
MeterInstance.CreateObservableCounter(
"process.runtime.dotnet.gc.collections.count",
() => GetGarbageCollectionCounts(),
GetGarbageCollectionCounts,
description: "Number of garbage collections that have occurred since process start.");

MeterInstance.CreateObservableUpDownCounter(
Expand All @@ -51,19 +53,14 @@ static RuntimeMetrics()
"process.runtime.dotnet.gc.committed_memory.size",
() =>
{
if (!IsGcInfoAvailable)
{
return Array.Empty<Measurement<long>>();
}

return new Measurement<long>[] { new(GC.GetGCMemoryInfo().TotalCommittedBytes) };
return !IsGcInfoAvailable ? Array.Empty<Measurement<long>>() : [new(GC.GetGCMemoryInfo().TotalCommittedBytes)];
},
unit: "bytes",
description: "The amount of committed virtual memory for the managed GC heap, as observed during the latest garbage collection. Committed virtual memory may be larger than the heap size because it includes both memory for storing existing objects (the heap size) and some extra memory that is ready to handle newly allocated objects in the future. The value will be unavailable until at least one garbage collection has occurred.");

// GC.GetGCMemoryInfo().GenerationInfo[i].SizeAfterBytes is better but it has a bug in .NET 6. See context in https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/496
Func<int, ulong>? getGenerationSize = null;
bool isCodeRunningOnBuggyRuntimeVersion = Environment.Version.Major == 6;
var isCodeRunningOnBuggyRuntimeVersion = Environment.Version.Major == 6;
if (isCodeRunningOnBuggyRuntimeVersion)
{
var mi = typeof(GC).GetMethod("GetGenerationSize", BindingFlags.NonPublic | BindingFlags.Static);
Expand All @@ -82,22 +79,17 @@ static RuntimeMetrics()
{
if (!IsGcInfoAvailable)
{
return Array.Empty<Measurement<long>>();
return [];
}

var generationInfo = GC.GetGCMemoryInfo().GenerationInfo;
Measurement<long>[] measurements = new Measurement<long>[generationInfo.Length];
int maxSupportedLength = Math.Min(generationInfo.Length, GenNames.Length);
for (int i = 0; i < maxSupportedLength; ++i)
var measurements = new Measurement<long>[generationInfo.Length];
var maxSupportedLength = Math.Min(generationInfo.Length, GenNames.Length);
for (var i = 0; i < maxSupportedLength; ++i)
{
if (isCodeRunningOnBuggyRuntimeVersion)
{
measurements[i] = new((long)getGenerationSize!(i), new KeyValuePair<string, object?>("generation", GenNames[i]));
}
else
{
measurements[i] = new(generationInfo[i].SizeAfterBytes, new KeyValuePair<string, object?>("generation", GenNames[i]));
}
measurements[i] = isCodeRunningOnBuggyRuntimeVersion
? new((long)getGenerationSize!(i), new KeyValuePair<string, object?>("generation", GenNames[i]))
: new(generationInfo[i].SizeAfterBytes, new KeyValuePair<string, object?>("generation", GenNames[i]));
}

return measurements;
Expand All @@ -115,13 +107,13 @@ static RuntimeMetrics()
{
if (!IsGcInfoAvailable)
{
return Array.Empty<Measurement<long>>();
return [];
}

var generationInfo = GC.GetGCMemoryInfo().GenerationInfo;
Measurement<long>[] measurements = new Measurement<long>[generationInfo.Length];
int maxSupportedLength = Math.Min(generationInfo.Length, GenNames.Length);
for (int i = 0; i < maxSupportedLength; ++i)
var measurements = new Measurement<long>[generationInfo.Length];
var maxSupportedLength = Math.Min(generationInfo.Length, GenNames.Length);
for (var i = 0; i < maxSupportedLength; ++i)
{
measurements[i] = new(generationInfo[i].FragmentationAfterBytes, new KeyValuePair<string, object?>("generation", GenNames[i]));
}
Expand Down Expand Up @@ -201,15 +193,17 @@ static RuntimeMetrics()
exceptionCounter.Add(1);
};
}

#pragma warning disable SA1313
/// <summary>
/// Initializes a new instance of the <see cref="RuntimeMetrics"/> class.
/// </summary>
/// <param name="options">The options to define the metrics.</param>
public RuntimeMetrics(RuntimeInstrumentationOptions options)
/// <param name="_1">The options to define the metrics.</param>
public RuntimeMetrics(RuntimeInstrumentationOptions _1)
#pragma warning restore SA1313
Comment on lines +196 to +202
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit odd. Why not just remove the parameter?

-public RuntimeMetrics(RuntimeInstrumentationOptions _1)
+public RuntimeMetrics()

Could make the whole class static. But need some way to make sure it is bootstrapped.

Like instead of...

        var instrumentation = new RuntimeMetrics(options);
        builder.AddMeter(RuntimeMetrics.MeterInstance.Name);
        return builder.AddInstrumentation(() => instrumentation);

Do something like...

        GC.KeepAlive(RuntimeMetrics.SomethingAccessibleToTriggetStaticCtor)
        return builder.AddMeter(RuntimeMetrics.MeterInstance.Name);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #2289

The problem is with public contract if extension method. We need to keep this as the package is released as stable. Then we need to suppress warning there. I am not against, but lets do it seprately

    public static MeterProviderBuilder AddRuntimeInstrumentation(
        this MeterProviderBuilder builder,
        Action<RuntimeInstrumentationOptions>? configure)

{
}

#if NET
private static bool IsGcInfoAvailable
{
get
Expand All @@ -227,12 +221,13 @@ private static bool IsGcInfoAvailable
return isGcInfoAvailable;
}
}
#endif

private static IEnumerable<Measurement<long>> GetGarbageCollectionCounts()
{
long collectionsFromHigherGeneration = 0;

for (int gen = NumberOfGenerations - 1; gen >= 0; --gen)
for (var gen = NumberOfGenerations - 1; gen >= 0; --gen)
{
long collectionsFromThisGeneration = GC.CollectionCount(gen);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace OpenTelemetry.Instrumentation.Runtime.Tests;

public class RuntimeInstrumentationOptionsTests
internal class RuntimeInstrumentationOptionsTests
{
/*
[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ public async Task ThreadingRelatedMetricsTest()
.Build();

// Bump the count for `thread_pool.completed_items.count` metric
int taskCount = 50;
List<Task> tasks = new List<Task>();
for (int i = 0; i < taskCount; i++)
var taskCount = 50;
var tasks = new List<Task>();
for (var i = 0; i < taskCount; i++)
{
tasks.Add(Task.Run(() => { }));
}
Expand All @@ -137,15 +137,19 @@ public async Task ThreadingRelatedMetricsTest()
var queueLengthMetric = exportedItems.FirstOrDefault(i => i.Name == "process.runtime.dotnet.thread_pool.queue.length");
Assert.NotNull(queueLengthMetric);

List<Timer> timers = new List<Timer>();
var timers = new List<Timer>();
try
{
// Create 10 timers to bump timer.count metrics.
int timerCount = 10;
TimerCallback timerCallback = _ => { };
for (int i = 0; i < timerCount; i++)
var timerCount = 10;
#pragma warning disable SA1313
static void TimerCallback(object? _)
{
Timer timer = new Timer(timerCallback, null, 1000, 250);
}
#pragma warning restore SA1313
for (var i = 0; i < timerCount; i++)
{
var timer = new Timer(TimerCallback, null, 1000, 250);
timers.Add(timer);
}

Expand All @@ -157,7 +161,7 @@ public async Task ThreadingRelatedMetricsTest()
}
finally
{
for (int i = 0; i < timers.Count; i++)
for (var i = 0; i < timers.Count; i++)
{
timers[i].Dispose();
}
Expand Down
Loading