Skip to content

Commit ff0c8b1

Browse files
authored
[dotnet-trace][collect-linux] Reintroduce process specifier (#5623)
With microsoft/one-collect#209, we can now allow specifying a particular process to be traced.
1 parent ace088c commit ff0c8b1

File tree

4 files changed

+91
-8
lines changed

4 files changed

+91
-8
lines changed

src/Tools/Common/Commands/Utils.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,57 @@ public static bool ValidateArgumentsForChildProcess(int processId, string name,
7272
return true;
7373
}
7474

75+
/// <summary>
76+
/// A helper method for validating --process-id, --name options for collect commands and resolving the process ID and name.
77+
/// Only one of these options can be specified, so it checks for duplicate options specified and if there is
78+
/// such duplication, it prints the appropriate error message.
79+
/// </summary>
80+
/// <param name="processId">process ID</param>
81+
/// <param name="name">name</param>
82+
/// <param name="resolvedProcessId">resolvedProcessId</param>
83+
/// <param name="resolvedProcessName">resolvedProcessName</param>
84+
/// <returns></returns>
85+
public static bool ResolveProcess(int processId, string name, out int resolvedProcessId, out string resolvedProcessName)
86+
{
87+
resolvedProcessId = -1;
88+
resolvedProcessName = name;
89+
if (processId == 0 && string.IsNullOrEmpty(name))
90+
{
91+
Console.Error.WriteLine("Must specify either --process-id or --name.");
92+
return false;
93+
}
94+
else if (processId < 0)
95+
{
96+
Console.Error.WriteLine($"{processId} is not a valid process ID");
97+
return false;
98+
}
99+
else if ((processId != 0) && !string.IsNullOrEmpty(name))
100+
{
101+
Console.Error.WriteLine("Only one of the --name or --process-id options may be specified.");
102+
return false;
103+
}
104+
try
105+
{
106+
if (processId != 0)
107+
{
108+
Process process = Process.GetProcessById(processId);
109+
resolvedProcessId = processId;
110+
resolvedProcessName = process.ProcessName;
111+
}
112+
else
113+
{
114+
resolvedProcessId = FindProcessIdWithName(name);
115+
}
116+
}
117+
catch (ArgumentException)
118+
{
119+
Console.Error.WriteLine($"No process with ID {processId} is currently running.");
120+
return false;
121+
}
122+
123+
return resolvedProcessId != -1;
124+
}
125+
75126
/// <summary>
76127
/// A helper method for validating --process-id, --name, --diagnostic-port, --dsrouter options for collect commands and resolving the process ID.
77128
/// Only one of these options can be specified, so it checks for duplicate options specified and if there is

src/Tools/dotnet-trace/CommandLine/Commands/CollectLinuxCommand.cs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ internal sealed record CollectLinuxArgs(
3131
string[] PerfEvents,
3232
string[] Profiles,
3333
FileInfo Output,
34-
TimeSpan Duration);
34+
TimeSpan Duration,
35+
string Name,
36+
int ProcessId);
3537

3638
public CollectLinuxCommandHandler(IConsole console = null)
3739
{
@@ -51,6 +53,15 @@ internal int CollectLinux(CollectLinuxArgs args)
5153
return (int)ReturnCode.PlatformNotSupportedError;
5254
}
5355

56+
if (args.ProcessId != 0 || !string.IsNullOrEmpty(args.Name))
57+
{
58+
if (!CommandUtils.ResolveProcess(args.ProcessId, args.Name, out int resolvedProcessId, out string resolvedProcessName))
59+
{
60+
return (int)ReturnCode.ArgumentError;
61+
}
62+
args = args with { Name = resolvedProcessName, ProcessId = resolvedProcessId };
63+
}
64+
5465
Console.WriteLine("==========================================================================================");
5566
Console.WriteLine("The collect-linux verb is a new preview feature and relies on an updated version of the");
5667
Console.WriteLine(".nettrace file format. The latest PerfView release supports these trace files but other");
@@ -117,6 +128,8 @@ public static Command CollectLinuxCommand()
117128
CommonOptions.ProfileOption,
118129
CommonOptions.OutputPathOption,
119130
CommonOptions.DurationOption,
131+
CommonOptions.NameOption,
132+
CommonOptions.ProcessIdOption
120133
};
121134
collectLinuxCommand.TreatUnmatchedTokensAsErrors = true; // collect-linux currently does not support child process tracing.
122135
collectLinuxCommand.Description = "Collects diagnostic traces using perf_events, a Linux OS technology. collect-linux requires admin privileges to capture kernel- and user-mode events, and by default, captures events from all processes. This Linux-only command includes the same .NET events as dotnet-trace collect, and it uses the kernel’s user_events mechanism to emit .NET events as perf events, enabling unification of user-space .NET events with kernel-space system events.";
@@ -135,7 +148,9 @@ public static Command CollectLinuxCommand()
135148
PerfEvents: perfEventsValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries),
136149
Profiles: profilesValue.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries),
137150
Output: parseResult.GetValue(CommonOptions.OutputPathOption) ?? new FileInfo(CommonOptions.DefaultTraceName),
138-
Duration: parseResult.GetValue(CommonOptions.DurationOption)));
151+
Duration: parseResult.GetValue(CommonOptions.DurationOption),
152+
Name: parseResult.GetValue(CommonOptions.NameOption) ?? string.Empty,
153+
ProcessId: parseResult.GetValue(CommonOptions.ProcessIdOption)));
139154
return Task.FromResult(rc);
140155
});
141156

@@ -220,7 +235,14 @@ private byte[] BuildRecordTraceArgs(CollectLinuxArgs args, out string scriptPath
220235
}
221236
Console.WriteLine();
222237

223-
FileInfo resolvedOutput = ResolveOutputPath(args.Output);
238+
int pid = args.ProcessId;
239+
if (pid > 0)
240+
{
241+
recordTraceArgs.Add($"--pid");
242+
recordTraceArgs.Add($"{pid}");
243+
}
244+
245+
FileInfo resolvedOutput = ResolveOutputPath(args.Output, args.Name);
224246
recordTraceArgs.Add($"--out");
225247
recordTraceArgs.Add(resolvedOutput.FullName);
226248
Console.WriteLine($"Output File : {resolvedOutput.FullName}");
@@ -237,15 +259,21 @@ private byte[] BuildRecordTraceArgs(CollectLinuxArgs args, out string scriptPath
237259
return Encoding.UTF8.GetBytes(options);
238260
}
239261

240-
private static FileInfo ResolveOutputPath(FileInfo output)
262+
private static FileInfo ResolveOutputPath(FileInfo output, string processName)
241263
{
242264
if (!string.Equals(output.Name, CommonOptions.DefaultTraceName, StringComparison.OrdinalIgnoreCase))
243265
{
244266
return output;
245267
}
246268

269+
string traceName = "trace";
270+
if (!string.IsNullOrEmpty(processName))
271+
{
272+
traceName = processName;
273+
}
274+
247275
DateTime now = DateTime.Now;
248-
return new FileInfo($"trace_{now:yyyyMMdd}_{now:HHmmss}.nettrace");
276+
return new FileInfo($"{traceName}_{now:yyyyMMdd}_{now:HHmmss}.nettrace");
249277
}
250278

251279
private int OutputHandler(uint type, IntPtr data, UIntPtr dataLen)

src/Tools/dotnet-trace/dotnet-trace.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<ItemGroup>
1515
<PackageReference Include="System.CommandLine" Version="$(SystemCommandLineVersion)" />
1616
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="$(MicrosoftDiagnosticsTracingTraceEventVersion)" GeneratePathProperty="true" />
17-
<PackageReference Include="Microsoft.OneCollect.RecordTrace" Version="0.1.32151" PrivateAssets="All" GeneratePathProperty="true"
17+
<PackageReference Include="Microsoft.OneCollect.RecordTrace" Version="0.1.32221" PrivateAssets="All" GeneratePathProperty="true"
1818
Condition="'$(TargetRid)' != '' and $([System.String]::Copy('$(TargetRid)').StartsWith('linux'))" />
1919
</ItemGroup>
2020

src/tests/dotnet-trace/CollectLinuxCommandFunctionalTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ private static CollectLinuxCommandHandler.CollectLinuxArgs TestArgs(
2626
string[] perfEvents = null,
2727
string[] profile = null,
2828
FileInfo output = null,
29-
TimeSpan duration = default)
29+
TimeSpan duration = default,
30+
string name = "",
31+
int processId = 0)
3032
{
3133
return new CollectLinuxCommandHandler.CollectLinuxArgs(ct,
3234
providers ?? Array.Empty<string>(),
@@ -35,7 +37,9 @@ private static CollectLinuxCommandHandler.CollectLinuxArgs TestArgs(
3537
perfEvents ?? Array.Empty<string>(),
3638
profile ?? Array.Empty<string>(),
3739
output ?? new FileInfo("trace.nettrace"),
38-
duration);
40+
duration,
41+
name,
42+
processId);
3943
}
4044

4145
[Theory]

0 commit comments

Comments
 (0)