Skip to content

Commit c106b65

Browse files
authored
chore: enable nullable annotation for Disassembler related code (#2939)
1 parent cd06388 commit c106b65

16 files changed

+141
-84
lines changed

src/BenchmarkDotNet/Diagnosers/DiagnoserActionParameters.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using System.Diagnostics;
1+
using System;
2+
using System.Diagnostics;
23
using BenchmarkDotNet.Configs;
34
using BenchmarkDotNet.Running;
45

6+
#nullable enable
7+
58
namespace BenchmarkDotNet.Diagnosers
69
{
710
public class DiagnoserActionParameters
@@ -13,7 +16,9 @@ public DiagnoserActionParameters(Process? process, BenchmarkCase benchmarkCase,
1316
BenchmarkId = benchmarkId;
1417
}
1518

16-
public Process Process { get; }
19+
public Process? Process { get; }
20+
21+
public int ProcessId => Process?.Id ?? throw new InvalidOperationException("The process instance is not set.");
1722

1823
public BenchmarkCase BenchmarkCase { get; }
1924

src/BenchmarkDotNet/Diagnosers/EventPipeProfiler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public void Handle(HostSignal signal, DiagnoserActionParameters parameters)
7474
if (signal != HostSignal.BeforeAnythingElse)
7575
return;
7676

77-
var diagnosticsClient = new DiagnosticsClient(parameters.Process.Id);
77+
var diagnosticsClient = new DiagnosticsClient(parameters.ProcessId);
7878

7979
EventPipeSession session = diagnosticsClient.StartEventPipeSession(eventPipeProviders, true);
8080

src/BenchmarkDotNet/Diagnosers/SnapshotProfilerBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
using BenchmarkDotNet.Running;
1414
using BenchmarkDotNet.Validators;
1515

16+
#nullable enable
17+
1618
namespace BenchmarkDotNet.Diagnosers;
1719

1820
public abstract class SnapshotProfilerBase : IProfiler
@@ -162,7 +164,7 @@ private void Stop(ILogger logger)
162164

163165
private void Attach(DiagnoserActionParameters parameters, string snapshotFile)
164166
{
165-
int pid = parameters.Process.Id;
167+
int pid = parameters.ProcessId;
166168
int currentPid = Process.GetCurrentProcess().Id;
167169
if (pid != currentPid)
168170
AttachToProcessByPid(pid, snapshotFile);

src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using System.Collections.Generic;
77
using System.Linq;
88

9+
#nullable enable
10+
911
namespace BenchmarkDotNet.Disassemblers
1012
{
1113
internal struct RegisterValueAccumulator
@@ -193,7 +195,7 @@ internal RuntimeSpecificData(State state)
193195

194196
protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, State state, int depth, ClrMethod currentMethod, DisassemblySyntax syntax)
195197
{
196-
if (!runtimeSpecificData.TryGetValue(state.RuntimeVersion, out RuntimeSpecificData data))
198+
if (!runtimeSpecificData.TryGetValue(state.RuntimeVersion, out var data))
197199
{
198200
runtimeSpecificData.Add(state.RuntimeVersion, data = new RuntimeSpecificData(state));
199201
}

src/BenchmarkDotNet/Disassemblers/Arm64InstructionFormatter.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Collections.Generic;
44
using System.Text;
55

6+
#nullable enable
7+
68
namespace BenchmarkDotNet.Disassemblers
79
{
810
internal static class Arm64InstructionFormatter
@@ -12,8 +14,11 @@ internal static class Arm64InstructionFormatter
1214
internal static string Format(Arm64Asm asm, FormatterOptions formatterOptions,
1315
bool printInstructionAddresses, uint pointerSize, IReadOnlyDictionary<ulong, string> symbols)
1416
{
17+
var instruction = asm.Instruction;
18+
if (instruction == null)
19+
return "";
20+
1521
StringBuilder output = new();
16-
Arm64Instruction instruction = asm.Instruction;
1722

1823
if (printInstructionAddresses)
1924
{
@@ -22,7 +27,7 @@ internal static string Format(Arm64Asm asm, FormatterOptions formatterOptions,
2227

2328
output.Append(instruction.Mnemonic.ToString().PadRight(formatterOptions.FirstOperandCharIndex));
2429

25-
if (asm.ReferencedAddress.HasValue && !asm.IsReferencedAddressIndirect && symbols.TryGetValue(asm.ReferencedAddress.Value, out string name))
30+
if (asm.ReferencedAddress.HasValue && !asm.IsReferencedAddressIndirect && symbols.TryGetValue(asm.ReferencedAddress.Value, out var name))
2631
{
2732
string partToReplace = $"#0x{asm.ReferencedAddress.Value:x}";
2833
output.Append(instruction.Operand.Replace(partToReplace, name));

src/BenchmarkDotNet/Disassemblers/ClrMdArgs.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
using System.Linq;
33
using SimpleJson;
44

5+
#nullable enable
6+
57
namespace BenchmarkDotNet.Disassemblers
68
{
7-
internal struct ClrMdArgs(int processId, string typeName, string methodName, bool printSource, int maxDepth, string syntax, string tfm, string[] filters, string resultsPath = null)
9+
internal struct ClrMdArgs(int processId, string typeName, string methodName, bool printSource, int maxDepth, string syntax, string tfm, string[] filters, string resultsPath = "")
810
{
911
internal int ProcessId = processId;
1012
internal string TypeName = typeName;
@@ -50,11 +52,14 @@ internal readonly string Serialize()
5052
return jsonObject.ToString();
5153
}
5254

53-
internal void Deserialize(string json)
55+
internal void Deserialize(string? json)
5456
{
5557
var jsonObject = SimpleJsonSerializer.DeserializeObject<JsonObject>(json);
56-
MethodName = (string) jsonObject[nameof(MethodName)];
57-
PrintSource = (bool) jsonObject[nameof(PrintSource)];
58+
if (jsonObject == null)
59+
return;
60+
61+
MethodName = (string)jsonObject[nameof(MethodName)];
62+
PrintSource = (bool)jsonObject[nameof(PrintSource)];
5863
MaxDepth = Convert.ToInt32(jsonObject[nameof(MaxDepth)]);
5964
Syntax = (string) jsonObject[nameof(Syntax)];
6065
TargetFrameworkMoniker = (string) jsonObject[nameof(TargetFrameworkMoniker)];

src/BenchmarkDotNet/Disassemblers/ClrMdDisassembler.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using Microsoft.Diagnostics.NETCore.Client;
1212
using BenchmarkDotNet.Extensions;
1313

14+
#nullable enable
15+
1416
namespace BenchmarkDotNet.Disassemblers
1517
{
1618
internal abstract class ClrMdDisassembler
@@ -100,7 +102,7 @@ internal DisassemblyResult AttachAndDisassemble(ClrMdArgs args)
100102
}
101103
else
102104
{
103-
ClrType typeWithBenchmark = state.Runtime.EnumerateModules().Select(module => module.GetTypeByName(args.TypeName)).First(type => type != null);
105+
var typeWithBenchmark = state.Runtime.EnumerateModules().Select(module => module.GetTypeByName(args.TypeName)).WhereNotNull().First();
104106

105107
state.Todo.Enqueue(
106108
new MethodInfo(
@@ -135,22 +137,22 @@ private static void FilterAndEnqueue(State state, ClrMdArgs args)
135137
Regex[] filters = GlobFilter.ToRegex(args.Filters);
136138

137139
foreach (ClrModule module in state.Runtime.EnumerateModules())
138-
foreach (ClrType type in module.EnumerateTypeDefToMethodTableMap().Select(map => state.Runtime.GetTypeByMethodTable(map.MethodTable)).Where(type => type is not null))
139-
foreach (ClrMethod method in type.Methods.Where(method => method.Signature != null))
140+
foreach (ClrType type in module.EnumerateTypeDefToMethodTableMap().Select(map => state.Runtime.GetTypeByMethodTable(map.MethodTable)).WhereNotNull())
141+
foreach (ClrMethod method in type.Methods.Where(method => method.Signature.IsNotBlank()))
140142
{
141143
if (method.NativeCode > 0)
142144
{
143145
if (!state.AddressToNameMapping.TryGetValue(method.NativeCode, out _))
144146
{
145-
state.AddressToNameMapping.Add(method.NativeCode, method.Signature);
147+
state.AddressToNameMapping.Add(method.NativeCode, method.Signature!);
146148
}
147149
}
148150

149151
if (CanBeDisassembled(method))
150152
{
151153
foreach (Regex filter in filters)
152154
{
153-
if (filter.IsMatch(method.Signature))
155+
if (filter.IsMatch(method.Signature!))
154156
{
155157
state.Todo.Enqueue(new MethodInfo(method,
156158
depth: args.MaxDepth)); // don't allow for recursive disassembling
@@ -225,7 +227,7 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state,
225227
return new DisassembledMethod
226228
{
227229
Maps = maps,
228-
Name = method.Signature,
230+
Name = method.Signature ?? "",
229231
NativeCode = method.NativeCode
230232
};
231233
}
@@ -277,7 +279,7 @@ private static ILToNativeMap[] GetCompleteNativeMap(ClrMethod method, ClrRuntime
277279
}
278280

279281
private static DisassembledMethod CreateEmpty(ClrMethod method, string reason)
280-
=> DisassembledMethod.Empty(method.Signature, method.NativeCode, reason);
282+
=> DisassembledMethod.Empty(method.Signature ?? "", method.NativeCode, reason);
281283

282284
protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, State state, int depth, ClrMethod currentMethod)
283285
{
@@ -289,7 +291,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
289291
var jitHelperFunctionName = runtime.GetJitHelperFunctionName(address);
290292
if (jitHelperFunctionName.IsNotBlank())
291293
{
292-
state.AddressToNameMapping.Add(address, jitHelperFunctionName);
294+
state.AddressToNameMapping.Add(address, jitHelperFunctionName!);
293295
return;
294296
}
295297

@@ -330,7 +332,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
330332
if (!state.HandledMethods.Contains(method))
331333
state.Todo.Enqueue(new MethodInfo(method, depth + 1));
332334

333-
var methodName = method.Signature;
335+
var methodName = method.Signature!;
334336
if (!methodName.Any(c => c == '.')) // the method name does not contain namespace and type name
335337
methodName = $"{method.Type.Name}.{method.Signature}";
336338
state.AddressToNameMapping.Add(address, methodName);
@@ -354,8 +356,13 @@ protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong a
354356

355357
private class SharpComparer : IEqualityComparer<Sharp>
356358
{
357-
public bool Equals(Sharp x, Sharp y)
359+
public bool Equals(Sharp? x, Sharp? y)
358360
{
361+
if (ReferenceEquals(x, y))
362+
return true;
363+
if (x is null || y is null)
364+
return false;
365+
359366
// sometimes some C# code lines are duplicated because the same line is the best match for multiple ILToNativeMaps
360367
// we don't want to confuse the users, so this must also be removed
361368
return x.FilePath == y.FilePath && x.LineNumber == y.LineNumber;

src/BenchmarkDotNet/Disassemblers/DataContracts.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@
55
using SimpleJson;
66
using System;
77
using System.Collections.Generic;
8-
using System.Diagnostics.CodeAnalysis;
98
using System.Linq;
109
using System.Xml.Serialization;
1110

11+
#if NET6_0_OR_GREATER
12+
using System.Diagnostics.CodeAnalysis;
13+
#endif
14+
15+
#nullable enable
16+
1217
namespace BenchmarkDotNet.Disassemblers;
1318

1419
public abstract class SourceCode
@@ -38,8 +43,8 @@ internal virtual void Deserialize(JsonObject json)
3843

3944
public sealed class Sharp : SourceCode
4045
{
41-
public string Text { get; set; }
42-
public string FilePath { get; set; }
46+
public string Text { get; set; } = default!;
47+
public string FilePath { get; set; } = default!;
4348
public int LineNumber { get; set; }
4449

4550
private protected override void Serialize(JsonObject json)
@@ -134,7 +139,7 @@ internal override void Deserialize(JsonObject json)
134139
foreach (var kvp in (JsonObject) json[nameof(Instruction)])
135140
{
136141
object value = kvp.Value;
137-
var property = typeof(Instruction).GetProperty(kvp.Key);
142+
var property = typeof(Instruction).GetProperty(kvp.Key)!;
138143
var propertyType = property.PropertyType;
139144
if (propertyType == typeof(ulong))
140145
{
@@ -154,7 +159,7 @@ internal override void Deserialize(JsonObject json)
154159
}
155160
property.SetValue(instruction, value);
156161
}
157-
Instruction = (Instruction) instruction;
162+
Instruction = (Instruction)instruction;
158163
}
159164
}
160165

@@ -164,10 +169,10 @@ public sealed class Arm64Asm : Asm
164169
private const string BytesKey = "Arm64Bytes";
165170
private const string SyntaxKey = "Arm64Syntax";
166171

167-
public Arm64Instruction Instruction { get; set; }
172+
public Arm64Instruction? Instruction { get; set; }
168173
internal DisassembleSyntax DisassembleSyntax { get; set; }
169174

170-
public override string ToString() => Instruction.ToString();
175+
public override string ToString() => Instruction?.ToString() ?? "";
171176

172177
private protected override void Serialize(JsonObject json)
173178
{
@@ -178,7 +183,7 @@ private protected override void Serialize(JsonObject json)
178183
{
179184
json[AddressKey] = Instruction.Address.ToString();
180185
json[BytesKey] = Convert.ToBase64String(Instruction.Bytes);
181-
json[SyntaxKey] = (int) DisassembleSyntax;
186+
json[SyntaxKey] = (int)DisassembleSyntax;
182187
}
183188
}
184189

@@ -191,16 +196,16 @@ internal override void Deserialize(JsonObject json)
191196
// Use the Capstone disassembler to recreate the instruction from the bytes.
192197
using var disassembler = CapstoneDisassembler.CreateArm64Disassembler(Arm64DisassembleMode.Arm);
193198
disassembler.EnableInstructionDetails = true;
194-
disassembler.DisassembleSyntax = (DisassembleSyntax) Convert.ToInt32(json[SyntaxKey]);
195-
byte[] bytes = Convert.FromBase64String((string) bytes64);
196-
Instruction = disassembler.Disassemble(bytes, long.Parse((string) json[AddressKey])).Single();
199+
disassembler.DisassembleSyntax = (DisassembleSyntax)Convert.ToInt32(json[SyntaxKey]);
200+
byte[] bytes = Convert.FromBase64String((string)bytes64);
201+
Instruction = disassembler.Disassemble(bytes, long.Parse((string)json[AddressKey])).Single();
197202
}
198203
}
199204
}
200205

201206
public sealed class MonoCode : SourceCode
202207
{
203-
public string Text { get; set; }
208+
public string Text { get; set; } = "";
204209

205210
private protected override void Serialize(JsonObject json)
206211
{
@@ -223,7 +228,7 @@ public sealed class Map
223228
[XmlArrayItem(nameof(SourceCode), typeof(SourceCode))]
224229
[XmlArrayItem(nameof(Sharp), typeof(Sharp))]
225230
[XmlArrayItem(nameof(IntelAsm), typeof(IntelAsm))]
226-
public SourceCode[] SourceCodes { get; set; }
231+
public SourceCode[] SourceCodes { get; set; } = [];
227232

228233
internal JsonObject Serialize()
229234
{
@@ -260,15 +265,15 @@ internal void Deserialize(JsonObject json)
260265

261266
public sealed class DisassembledMethod
262267
{
263-
public string Name { get; set; }
268+
public string Name { get; set; } = "";
264269

265270
public ulong NativeCode { get; set; }
266271

267-
public string Problem { get; set; }
272+
public string Problem { get; set; } = "";
268273

269274
public Map[] Maps { get; set; } = [];
270275

271-
public string CommandLine { get; set; }
276+
public string CommandLine { get; set; } = "";
272277

273278
public static DisassembledMethod Empty(string fullSignature, ulong nativeCode, string problem)
274279
=> new()
@@ -325,7 +330,7 @@ public Dictionary<ulong, string> AddressToNameMapping
325330
=> _addressToNameMapping ??= SerializedAddressToNameMapping.ToDictionary(x => x.Key, x => x.Value);
326331

327332
[XmlIgnore]
328-
private Dictionary<ulong, string> _addressToNameMapping;
333+
private Dictionary<ulong, string>? _addressToNameMapping = null;
329334

330335
// KeyValuePair is not serializable, because it has read-only properties
331336
// so we need to define our own...
@@ -411,7 +416,6 @@ internal State(ClrRuntime runtime, string targetFrameworkMoniker)
411416
}
412417

413418
internal ClrRuntime Runtime { get; }
414-
internal string TargetFrameworkMoniker { get; }
415419
internal Queue<MethodInfo> Todo { get; }
416420
internal HashSet<ClrMethod> HandledMethods { get; }
417421
internal Dictionary<ulong, string> AddressToNameMapping { get; }
@@ -444,7 +448,16 @@ internal static Version ParseVersion(string targetFrameworkMoniker)
444448

445449
private sealed class ClrMethodComparer : IEqualityComparer<ClrMethod>
446450
{
447-
public bool Equals(ClrMethod x, ClrMethod y) => x.NativeCode == y.NativeCode;
451+
public bool Equals(ClrMethod? x, ClrMethod? y)
452+
{
453+
if (ReferenceEquals(x, y))
454+
return true;
455+
456+
if (x is null || y is null)
457+
return false;
458+
459+
return x.NativeCode == y.NativeCode;
460+
}
448461

449462
public int GetHashCode(ClrMethod obj) => (int) obj.NativeCode;
450463
}

0 commit comments

Comments
 (0)