Skip to content

Commit ae5de05

Browse files
authored
feat: implement dynamic & static tags (#16)
* implement dynamic tags * implement static tags * reuse bits from upstream for profile_id implementation * fix github actions, add new actions for managed helper
1 parent b55f107 commit ae5de05

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1399
-919
lines changed

.github/workflows/build_linux.yml renamed to .github/workflows/build_linux_profiler.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
name: Build linux
1+
name: Build linux profiler
22

33
on:
44
push:
5-
branches: [master]
5+
branches: [main]
66
pull_request:
7-
branches: [master]
7+
branches: [main]
88

99
jobs:
10-
build-linux:
10+
build-linux-profiler:
1111
runs-on: ubuntu-latest
1212
steps:
1313
-
@@ -22,7 +22,7 @@ jobs:
2222
tags: test-image:latest
2323
push: false
2424
file: ./Pyroscope.Dockerfile
25-
- name: Build profiler
25+
- name: Build linux profiler
2626
uses: addnab/docker-run-action@v3
2727
with:
2828
image: test-image:latest
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Build managed helper
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build-managed-helper:
11+
runs-on: ubuntu-latest
12+
steps:
13+
-
14+
name: Checkout
15+
uses: actions/checkout@v2
16+
with:
17+
submodules: 'true'
18+
- uses: actions/setup-dotnet@v3
19+
with:
20+
dotnet-version: '6.0'
21+
- run: dotnet build -c Release
22+
working-directory: Pyroscope

.github/workflows/tag_linux.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
name: Release linux
1+
name: Release linux profiler
22

33
on:
44
push:
55
tags:
66
- v0.**-pyroscope
77

88
jobs:
9-
release-linux:
9+
release-linux-profiler:
1010
runs-on: ubuntu-latest
1111
steps:
1212
-
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Release managed helper
2+
3+
on:
4+
push:
5+
tags:
6+
- v0.**-pyroscope
7+
8+
jobs:
9+
release-managed-helper:
10+
runs-on: ubuntu-latest
11+
steps:
12+
-
13+
name: Checkout
14+
uses: actions/checkout@v2
15+
with:
16+
submodules: 'true'
17+
- uses: actions/setup-dotnet@v3
18+
with:
19+
dotnet-version: '6.0'
20+
- run: dotnet build -c Release
21+
working-directory: Pyroscope
22+
- name: Publish the package to nuget.org
23+
run: dotnet nuget push Pyroscope/bin/Release/*.nupkg -k $NUGET_AUTH_TOKEN -s https://api.nuget.org/v3/index.json
24+
working-directory: Pyroscope
25+
env:
26+
NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY }}
27+
- name: Release
28+
uses: softprops/action-gh-release@v1
29+
if: startsWith(github.ref, 'refs/tags/')
30+
with:
31+
files: |
32+
./Pyroscope/Pyroscope/bin/Release/net6.0/Pyroscope.dll
33+
./Pyroscope/Pyroscope/bin/Release/Pyroscope*.nupkg

Pyroscope/Pyroscope.sln

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pyroscope", "Pyroscope\Pyroscope.csproj", "{7DA9EFD3-9B8D-4DF4-A1D1-108D43B53BBD}"
4+
EndProject
5+
Global
6+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7+
Debug|Any CPU = Debug|Any CPU
8+
Release|Any CPU = Release|Any CPU
9+
EndGlobalSection
10+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
11+
{7DA9EFD3-9B8D-4DF4-A1D1-108D43B53BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12+
{7DA9EFD3-9B8D-4DF4-A1D1-108D43B53BBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
13+
{7DA9EFD3-9B8D-4DF4-A1D1-108D43B53BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
14+
{7DA9EFD3-9B8D-4DF4-A1D1-108D43B53BBD}.Release|Any CPU.Build.0 = Release|Any CPU
15+
EndGlobalSection
16+
EndGlobal
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// <copyright file="ContextTracker.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
8+
9+
namespace Pyroscope
10+
{
11+
internal class ContextTracker
12+
{
13+
14+
15+
private readonly ProfilerStatus _status;
16+
17+
18+
/// <summary>
19+
/// _traceContextPtr points to a structure with this layout
20+
/// The structure is as follow:
21+
/// offset size(bytes)
22+
/// |--------------------|
23+
/// 0 8 | WriteGuard | // 8-byte for the alignment
24+
/// |--------------------|
25+
/// 8 8 | Local Root Span Id |
26+
/// |--------------------|
27+
/// 16 8 | Span Id |
28+
/// |--------------------|
29+
/// This allows us to inform the profiler sampling thread when we are writing or not the data
30+
/// and avoid torn read/write (Using memory barriers).
31+
/// We take advantage of this layout in SpanContext.Write
32+
/// </summary>
33+
private readonly ThreadLocal<IntPtr> _traceContextPtr;
34+
35+
36+
public ContextTracker(ProfilerStatus status)
37+
{
38+
_status = status;
39+
_traceContextPtr = new ThreadLocal<IntPtr>();
40+
}
41+
42+
public bool IsEnabled
43+
{
44+
get
45+
{
46+
return _status.IsProfilerReady;
47+
}
48+
}
49+
50+
public void Set(ulong localRootSpanId, ulong spanId)
51+
{
52+
WriteToNative(new SpanContext(localRootSpanId, spanId));
53+
}
54+
55+
56+
public void Reset()
57+
{
58+
WriteToNative(SpanContext.Zero);
59+
}
60+
61+
private void EnsureIsInitialized()
62+
{
63+
if (_traceContextPtr.IsValueCreated)
64+
{
65+
return;
66+
}
67+
68+
try
69+
{
70+
_traceContextPtr.Value = NativeInterop.GetTraceContextNativePointer();
71+
}
72+
catch (Exception)
73+
{
74+
_traceContextPtr.Value = IntPtr.Zero;
75+
}
76+
}
77+
78+
private void WriteToNative(in SpanContext ctx)
79+
{
80+
if (!IsEnabled)
81+
{
82+
return;
83+
}
84+
85+
EnsureIsInitialized();
86+
87+
var ctxPtr = _traceContextPtr.Value;
88+
89+
if (ctxPtr == IntPtr.Zero)
90+
{
91+
return;
92+
}
93+
94+
try
95+
{
96+
ctx.Write(ctxPtr);
97+
}
98+
catch (Exception)
99+
{
100+
101+
}
102+
}
103+
104+
// See the description and the layout depicted above
105+
private readonly struct SpanContext
106+
{
107+
public static readonly SpanContext Zero = new(0, 0);
108+
109+
public readonly ulong LocalRootSpanId;
110+
public readonly ulong SpanId;
111+
112+
public SpanContext(ulong localRootSpanId, ulong spanId)
113+
{
114+
LocalRootSpanId = localRootSpanId;
115+
SpanId = spanId;
116+
}
117+
118+
[MethodImpl(MethodImplOptions.NoInlining)]
119+
public void Write(IntPtr ptr)
120+
{
121+
// Set the WriteGuard
122+
Marshal.WriteInt64(ptr, 1);
123+
Thread.MemoryBarrier();
124+
125+
// Using WriteInt64 to write 2 long values is ~8x faster than using Marshal.StructureToPtr
126+
// For the offset, we follow the layout depicted above
127+
Marshal.WriteInt64(ptr + 8, (long)LocalRootSpanId);
128+
// Marshal.WriteInt64(ptr + 16, (long)SpanId);
129+
130+
// Reset the WriteGuard
131+
Thread.MemoryBarrier();
132+
Marshal.WriteInt64(ptr, 0);
133+
}
134+
}
135+
}
136+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// <copyright file="EnvironmentHelpers.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
namespace Pyroscope
7+
{
8+
/// <summary>
9+
/// Helpers to access environment variables
10+
/// </summary>
11+
internal static class EnvironmentHelpers
12+
{
13+
14+
15+
/// <summary>
16+
/// Safe wrapper around Environment.GetEnvironmentVariable
17+
/// </summary>
18+
/// <param name="key">Name of the environment variable to fetch</param>
19+
/// <param name="defaultValue">Value to return in case of error</param>
20+
/// <returns>The value of the environment variable, or the default value if an error occured</returns>
21+
public static string? GetEnvironmentVariable(string key, string? defaultValue = null)
22+
{
23+
var keys = new List<string>();
24+
keys.Add(key);
25+
if (key.StartsWith("DD_"))
26+
{
27+
var baseName = key.Substring(3);
28+
keys.Add(baseName);
29+
keys.Add("PYROSCOPE_" + baseName);
30+
}
31+
32+
var error = false;
33+
for (var i = 0; i < keys.Count; i++)
34+
{
35+
try
36+
{
37+
var val = Environment.GetEnvironmentVariable(keys[i]);
38+
if (val != null)
39+
{
40+
return val;
41+
}
42+
}
43+
catch (Exception)
44+
{
45+
error = true;
46+
}
47+
}
48+
if (error)
49+
{
50+
return defaultValue;
51+
}
52+
return null;
53+
}
54+
55+
56+
}
57+
}

Pyroscope/Pyroscope/LabelSet.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
namespace Pyroscope;
2+
3+
public class LabelSet
4+
{
5+
private readonly Dictionary<string, string> _labels;
6+
internal readonly LabelSet? Previous;
7+
8+
public static readonly LabelSet Empty = new(new Dictionary<string, string>());
9+
10+
private LabelSet(Dictionary<string, string> labels)
11+
{
12+
_labels = labels;
13+
Previous = null;
14+
}
15+
16+
17+
private LabelSet(LabelSet previous, Dictionary<string, string> labels)
18+
{
19+
_labels = labels;
20+
Previous = previous;
21+
}
22+
23+
public void Activate()
24+
{
25+
Profiler.Instance.ClearDynamicTags();
26+
foreach (var (key, value) in _labels)
27+
{
28+
Profiler.Instance.SetDynamicTag(key, value);
29+
}
30+
}
31+
32+
public Builder BuildUpon()
33+
{
34+
return new Builder(this);
35+
}
36+
37+
public class Builder
38+
{
39+
private readonly LabelSet _prev;
40+
private readonly Dictionary<string, string> _labels;
41+
42+
public Builder(LabelSet prev)
43+
{
44+
_prev = prev;
45+
_labels = new Dictionary<string, string>(_prev._labels);
46+
}
47+
48+
public Builder Add(string k, string v)
49+
{
50+
_labels[k] = v;
51+
return this;
52+
}
53+
54+
public LabelSet Build()
55+
{
56+
return new LabelSet(_prev, _labels);
57+
}
58+
}
59+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace Pyroscope;
2+
3+
public static class LabelsWrapper
4+
{
5+
public static void Do(LabelSet labels, Action a)
6+
{
7+
try
8+
{
9+
labels.Activate();
10+
a.Invoke();
11+
}
12+
finally
13+
{
14+
if (labels.Previous == null)
15+
{
16+
Profiler.Instance.ClearDynamicTags();
17+
}
18+
else
19+
{
20+
labels.Previous.Activate();
21+
}
22+
}
23+
}
24+
}

0 commit comments

Comments
 (0)