Skip to content

Commit 10b7e42

Browse files
committed
Add requirements check and upload trace
1 parent 7395930 commit 10b7e42

File tree

3 files changed

+235
-4
lines changed

3 files changed

+235
-4
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.IO;
7+
8+
namespace Tracing.Tests.UserEvents
9+
{
10+
internal static class UserEventsRequirements
11+
{
12+
private static readonly Version s_minKernelVersion = new(6, 4);
13+
private const string TracefsPath = "/sys/kernel/tracing";
14+
private const string UserEventsDataPath = "/sys/kernel/tracing/user_events_data";
15+
private static readonly Version s_minGlibcVersion = new(2, 35);
16+
17+
internal static bool IsSupported()
18+
{
19+
if (Environment.OSVersion.Version < s_minKernelVersion)
20+
{
21+
Console.WriteLine($"Kernel version '{Environment.OSVersion.Version}' is less than the minimum required '{s_minKernelVersion}'");
22+
return false;
23+
}
24+
25+
return IsGlibcAtLeast(s_minGlibcVersion) &&
26+
IsTracefsMounted() &&
27+
IsUserEventsDataAvailable();
28+
}
29+
30+
private static bool IsTracefsMounted()
31+
{
32+
ProcessStartInfo checkTracefsStartInfo = new();
33+
checkTracefsStartInfo.FileName = "sudo";
34+
checkTracefsStartInfo.Arguments = $"-n test -d {TracefsPath}";
35+
checkTracefsStartInfo.UseShellExecute = false;
36+
checkTracefsStartInfo.RedirectStandardOutput = true;
37+
checkTracefsStartInfo.RedirectStandardError = true;
38+
39+
using Process checkTracefs = new() { StartInfo = checkTracefsStartInfo };
40+
checkTracefs.OutputDataReceived += (_, e) =>
41+
{
42+
if (!string.IsNullOrEmpty(e.Data))
43+
{
44+
Console.WriteLine($"[tracefs-check] {e.Data}");
45+
}
46+
};
47+
checkTracefs.ErrorDataReceived += (_, e) =>
48+
{
49+
if (!string.IsNullOrEmpty(e.Data))
50+
{
51+
Console.Error.WriteLine($"[tracefs-check] {e.Data}");
52+
}
53+
};
54+
55+
checkTracefs.Start();
56+
checkTracefs.BeginOutputReadLine();
57+
checkTracefs.BeginErrorReadLine();
58+
checkTracefs.WaitForExit();
59+
if (checkTracefs.ExitCode == 0)
60+
{
61+
return true;
62+
}
63+
64+
Console.WriteLine($"Tracefs not mounted at '{TracefsPath}'.");
65+
return false;
66+
}
67+
68+
private static bool IsUserEventsDataAvailable()
69+
{
70+
ProcessStartInfo checkUserEventsDataStartInfo = new();
71+
checkUserEventsDataStartInfo.FileName = "sudo";
72+
checkUserEventsDataStartInfo.Arguments = $"-n test -e {UserEventsDataPath}";
73+
checkUserEventsDataStartInfo.UseShellExecute = false;
74+
checkUserEventsDataStartInfo.RedirectStandardOutput = true;
75+
checkUserEventsDataStartInfo.RedirectStandardError = true;
76+
77+
using Process checkUserEventsData = new() { StartInfo = checkUserEventsDataStartInfo };
78+
checkUserEventsData.OutputDataReceived += (_, e) =>
79+
{
80+
if (!string.IsNullOrEmpty(e.Data))
81+
{
82+
Console.WriteLine($"[user-events-check] {e.Data}");
83+
}
84+
};
85+
checkUserEventsData.ErrorDataReceived += (_, e) =>
86+
{
87+
if (!string.IsNullOrEmpty(e.Data))
88+
{
89+
Console.Error.WriteLine($"[user-events-check] {e.Data}");
90+
}
91+
};
92+
93+
checkUserEventsData.Start();
94+
checkUserEventsData.BeginOutputReadLine();
95+
checkUserEventsData.BeginErrorReadLine();
96+
checkUserEventsData.WaitForExit();
97+
if (checkUserEventsData.ExitCode == 0)
98+
{
99+
return true;
100+
}
101+
102+
Console.WriteLine($"User events data not available at '{UserEventsDataPath}'.");
103+
return false;
104+
}
105+
106+
private static bool IsGlibcAtLeast(Version minVersion)
107+
{
108+
ProcessStartInfo lddStartInfo = new();
109+
lddStartInfo.FileName = "ldd";
110+
lddStartInfo.Arguments = "--version";
111+
lddStartInfo.UseShellExecute = false;
112+
lddStartInfo.RedirectStandardOutput = true;
113+
lddStartInfo.RedirectStandardError = true;
114+
115+
using Process lddProcess = new() { StartInfo = lddStartInfo };
116+
string? detectedVersionLine = null;
117+
118+
lddProcess.OutputDataReceived += (_, e) =>
119+
{
120+
if (!string.IsNullOrEmpty(e.Data) && detectedVersionLine is null)
121+
{
122+
detectedVersionLine = e.Data;
123+
}
124+
};
125+
lddProcess.ErrorDataReceived += (_, e) =>
126+
{
127+
if (!string.IsNullOrEmpty(e.Data))
128+
{
129+
Console.Error.WriteLine($"[ldd] {e.Data}");
130+
}
131+
};
132+
133+
try
134+
{
135+
lddProcess.Start();
136+
}
137+
catch (Exception ex)
138+
{
139+
Console.WriteLine($"Failed to start 'ldd --version': {ex.Message}");
140+
return false;
141+
}
142+
143+
lddProcess.BeginOutputReadLine();
144+
lddProcess.BeginErrorReadLine();
145+
lddProcess.WaitForExit();
146+
147+
if (lddProcess.ExitCode != 0)
148+
{
149+
Console.WriteLine($"'ldd --version' exited with code {lddProcess.ExitCode}.");
150+
return false;
151+
}
152+
153+
if (string.IsNullOrEmpty(detectedVersionLine))
154+
{
155+
Console.WriteLine("Could not read glibc version from 'ldd --version' output.");
156+
return false;
157+
}
158+
159+
string[] tokens = detectedVersionLine.Split(' ', StringSplitOptions.RemoveEmptyEntries);
160+
Version? glibcVersion = null;
161+
foreach (string token in tokens)
162+
{
163+
if (Version.TryParse(token, out Version? parsedVersion))
164+
{
165+
glibcVersion = parsedVersion;
166+
break;
167+
}
168+
}
169+
170+
if (glibcVersion is null)
171+
{
172+
Console.WriteLine($"Failed to parse glibc version from 'ldd --version' output line: {detectedVersionLine}");
173+
return false;
174+
}
175+
176+
if (glibcVersion < minVersion)
177+
{
178+
Console.WriteLine($"glibc version '{glibcVersion}' is less than required '{minVersion}'.");
179+
return false;
180+
}
181+
182+
return true;
183+
}
184+
}
185+
}

src/tests/tracing/eventpipe/userevents/userevents.cs

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ public static int TestEntryPoint()
3636
string scriptFilePath = Path.Combine(appBaseDir, "dotnet-common.script");
3737
const string userEventsDataPath = "/sys/kernel/tracing/user_events_data";
3838

39+
if (!UserEventsRequirements.IsSupported())
40+
{
41+
Console.WriteLine("Skipping test: environment does not support user events.");
42+
return 100;
43+
}
3944
if (!File.Exists(recordTracePath))
4045
{
4146
Console.Error.WriteLine($"record-trace not found at `{recordTracePath}`. Test cannot run.");
@@ -61,9 +66,21 @@ public static int TestEntryPoint()
6166

6267
Console.WriteLine($"Starting record-trace: {recordTraceStartInfo.FileName} {recordTraceStartInfo.Arguments}");
6368
using Process recordTraceProcess = Process.Start(recordTraceStartInfo);
64-
recordTraceProcess.OutputDataReceived += (_, args) => Console.WriteLine($"[record-trace] {args.Data}");
69+
recordTraceProcess.OutputDataReceived += (_, args) =>
70+
{
71+
if (!string.IsNullOrEmpty(args.Data))
72+
{
73+
Console.WriteLine($"[record-trace] {args.Data}");
74+
}
75+
};
6576
recordTraceProcess.BeginOutputReadLine();
66-
recordTraceProcess.ErrorDataReceived += (_, args) => Console.Error.WriteLine($"[record-trace] {args.Data}");
77+
recordTraceProcess.ErrorDataReceived += (_, args) =>
78+
{
79+
if (!string.IsNullOrEmpty(args.Data))
80+
{
81+
Console.Error.WriteLine($"[record-trace] {args.Data}");
82+
}
83+
};
6784
recordTraceProcess.BeginErrorReadLine();
6885
Console.WriteLine($"record-trace started with PID: {recordTraceProcess.Id}");
6986

@@ -77,9 +94,21 @@ public static int TestEntryPoint()
7794
Console.WriteLine($"Starting tracee process: {traceeStartInfo.FileName} {traceeStartInfo.Arguments}");
7895
using Process traceeProcess = Process.Start(traceeStartInfo);
7996
Console.WriteLine($"Tracee process started with PID: {traceeProcess.Id}");
80-
traceeProcess.OutputDataReceived += (_, args) => Console.WriteLine($"[tracee] {args.Data}");
97+
traceeProcess.OutputDataReceived += (_, args) =>
98+
{
99+
if (!string.IsNullOrEmpty(args.Data))
100+
{
101+
Console.WriteLine($"[tracee] {args.Data}");
102+
}
103+
};
81104
traceeProcess.BeginOutputReadLine();
82-
traceeProcess.ErrorDataReceived += (_, args) => Console.Error.WriteLine($"[tracee] {args.Data}");
105+
traceeProcess.ErrorDataReceived += (_, args) =>
106+
{
107+
if (!string.IsNullOrEmpty(args.Data))
108+
{
109+
Console.Error.WriteLine($"[tracee] {args.Data}");
110+
}
111+
};
83112
traceeProcess.BeginErrorReadLine();
84113

85114
Console.WriteLine($"Waiting for tracee process to exit...");
@@ -118,6 +147,7 @@ public static int TestEntryPoint()
118147
if (!ValidateTraceeEvents(traceFilePath))
119148
{
120149
Console.Error.WriteLine($"Trace file `{traceFilePath}` does not contain expected events.");
150+
UploadTraceFile(traceFilePath);
121151
return -1;
122152
}
123153

@@ -143,5 +173,20 @@ private static bool ValidateTraceeEvents(string traceFilePath)
143173
source.Process();
144174
return allocationSampledEventFound;
145175
}
176+
177+
private static void UploadTraceFile(string traceFilePath)
178+
{
179+
var helixWorkItemDirectory = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT");
180+
if (helixWorkItemDirectory != null && Directory.Exists(helixWorkItemDirectory))
181+
{
182+
var destPath = Path.Combine(helixWorkItemDirectory, Path.GetFileName(traceFilePath));
183+
Console.WriteLine($"Uploading trace file to Helix work item directory: {destPath}");
184+
File.Copy(traceFilePath, destPath, overwrite: true);
185+
}
186+
else
187+
{
188+
Console.WriteLine($"Helix work item directory not found. Trace file remains at: {traceFilePath}");
189+
}
190+
}
146191
}
147192
}

src/tests/tracing/eventpipe/userevents/userevents.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<ItemGroup>
1414
<Compile Include="$(MSBuildProjectName).cs" />
1515
<Compile Include="usereventstracee.cs" />
16+
<Compile Include="UserEventsRequirements.cs" />
1617
</ItemGroup>
1718

1819
<ItemGroup>

0 commit comments

Comments
 (0)