Skip to content

Commit 153a9e5

Browse files
author
gc46
authored
User Story 1565289: [MIEngine Natvis] Support multiple natvis files in visualizerFile launch configuration (#1381)
1 parent e34f879 commit 153a9e5

13 files changed

Lines changed: 263 additions & 65 deletions

File tree

src/AndroidDebugLauncher/Launcher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ private LaunchOptions SetupForDebuggingWorker(CancellationToken token)
459459

460460
launchOptions.DebuggerMIMode = MIMode.Gdb;
461461

462-
launchOptions.VisualizerFile = "Microsoft.Android.natvis";
462+
launchOptions.VisualizerFiles.Add("Microsoft.Android.natvis");
463463
launchOptions.WaitDynamicLibLoad = _launchOptions.WaitDynamicLibLoad;
464464

465465
return launchOptions;

src/MICore/JsonLaunchOptions.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ public abstract partial class BaseOptions
3939
public string TargetArchitecture { get; set; }
4040

4141
/// <summary>
42-
/// .natvis file to be used when debugging this process. This option is not compatible with GDB pretty printing. Please also see "showDisplayString" if using this setting.
42+
/// .natvis files to be used when debugging this process. This option is not compatible with GDB pretty printing. Please also see "showDisplayString" if using this setting.
4343
/// </summary>
4444
[JsonProperty("visualizerFile", DefaultValueHandling = DefaultValueHandling.Ignore)]
45-
public string VisualizerFile { get; set; }
45+
[JsonConverter(typeof(VisualizerFileConverter))]
46+
public List<string> VisualizerFile { get; set; }
4647

4748
/// <summary>
4849
/// When a visualizerFile is specified, showDisplayString will enable the display string. Turning this option on can cause slower performance during debugging.
@@ -123,6 +124,37 @@ public abstract partial class BaseOptions
123124
public UnknownBreakpointHandling? UnknownBreakpointHandling { get; set; }
124125
}
125126

127+
internal class VisualizerFileConverter : JsonConverter
128+
{
129+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
130+
{
131+
List<string> visualizerFile = new List<string>();
132+
if (reader.TokenType == JsonToken.StartArray)
133+
{
134+
visualizerFile = serializer.Deserialize<List<string>>(reader);
135+
}
136+
else if (reader.TokenType == JsonToken.String)
137+
{
138+
visualizerFile.Add(reader.Value.ToString());
139+
}
140+
else
141+
{
142+
throw new JsonReaderException(MICoreResources.Error_IncorrectVisualizerFileFormat);
143+
}
144+
return visualizerFile;
145+
}
146+
147+
public override bool CanConvert(Type objectType)
148+
{
149+
throw new NotImplementedException();
150+
}
151+
152+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
153+
{
154+
throw new NotImplementedException();
155+
}
156+
}
157+
126158
public partial class AttachOptions : BaseOptions
127159
{
128160
#region Public Properties for Serialization
@@ -144,7 +176,7 @@ public AttachOptions(
144176
int processId,
145177
string type = null,
146178
string targetArchitecture = null,
147-
string visualizerFile = null,
179+
List<string> visualizerFile = null,
148180
bool? showDisplayString = null,
149181
string additionalSOLibSearchPath = null,
150182
string MIMode = null,
@@ -408,7 +440,7 @@ public LaunchOptions(
408440
List<SetupCommand> postRemoteConnectCommands = null,
409441
List<SetupCommand> customLaunchSetupCommands = null,
410442
LaunchCompleteCommand? launchCompleteCommand = null,
411-
string visualizerFile = null,
443+
List<string> visualizerFile = null,
412444
bool? showDisplayString = null,
413445
List<Environment> environment = null,
414446
string additionalSOLibSearchPath = null,

src/MICore/LaunchOptions.cs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -960,19 +960,10 @@ public string AdditionalSOLibSearchPath
960960
}
961961
}
962962

963-
private string _visualizerFile;
964963
/// <summary>
965-
/// [Optional] Natvis file name - from install location
964+
/// Collection of natvis files to use when evaluating
966965
/// </summary>
967-
public string VisualizerFile
968-
{
969-
get { return _visualizerFile; }
970-
set
971-
{
972-
VerifyCanModifyProperty(nameof(VisualizerFile));
973-
_visualizerFile = value;
974-
}
975-
}
966+
public List<string> VisualizerFiles { get; } = new List<string>();
976967

977968
private bool _waitDynamicLibLoad = true;
978969
/// <summary>
@@ -1579,9 +1570,9 @@ private void Merge(AttachOptionsForConnection suppOptions)
15791570
{
15801571
DebugChildProcesses = suppOptions.DebugChildProcesses;
15811572
}
1582-
if (string.IsNullOrWhiteSpace(VisualizerFile))
1573+
if (!this.VisualizerFiles.Contains(suppOptions.VisualizerFile))
15831574
{
1584-
VisualizerFile = suppOptions.VisualizerFile;
1575+
this.VisualizerFiles.Add(suppOptions.VisualizerFile);
15851576
}
15861577
if (suppOptions.ShowDisplayStringSpecified)
15871578
{
@@ -1771,7 +1762,10 @@ protected void InitializeCommonOptions(Json.LaunchOptions.BaseOptions options)
17711762
this.TargetArchitecture = ConvertTargetArchitectureAttribute(options.TargetArchitecture);
17721763
}
17731764

1774-
this.VisualizerFile = options.VisualizerFile;
1765+
if (options.VisualizerFile != null && options.VisualizerFile.Count > 0)
1766+
{
1767+
this.VisualizerFiles.AddRange(options.VisualizerFile);
1768+
}
17751769
this.ShowDisplayString = options.ShowDisplayString.GetValueOrDefault(false);
17761770

17771771
this.AdditionalSOLibSearchPath = String.IsNullOrEmpty(this.AdditionalSOLibSearchPath) ?
@@ -1841,8 +1835,8 @@ protected void InitializeCommonOptions(Xml.LaunchOptions.BaseLaunchOptions sourc
18411835
if (string.IsNullOrEmpty(this.WorkingDirectory))
18421836
this.WorkingDirectory = source.WorkingDirectory;
18431837

1844-
if (string.IsNullOrEmpty(this.VisualizerFile))
1845-
this.VisualizerFile = source.VisualizerFile;
1838+
if (!string.IsNullOrEmpty(source.VisualizerFile))
1839+
this.VisualizerFiles.Add(source.VisualizerFile);
18461840

18471841
this.ShowDisplayString = source.ShowDisplayString;
18481842
this.WaitDynamicLibLoad = source.WaitDynamicLibLoad;

src/MICore/MICoreResources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/MICore/MICoreResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,7 @@ Error: {1}</value>
326326
<data name="Error_SourceFileMapInvalidEditorPath" xml:space="preserve">
327327
<value>'editorPath' can not be null or empty</value>
328328
</data>
329+
<data name="Error_IncorrectVisualizerFileFormat" xml:space="preserve">
330+
<value>'visualizerFile' must be a string or array of strings.</value>
331+
</data>
329332
</root>

src/MICoreUnitTests/BasicLaunchOptionsTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ internal void VerifyCoreApisPresent()
263263
launchOptions.WorkingDirectory = "/home/user";
264264
launchOptions.DebuggerMIMode = MIMode.Gdb;
265265
launchOptions.WaitDynamicLibLoad = false;
266-
launchOptions.VisualizerFile = @"c:\myproject\file.natvis";
266+
launchOptions.VisualizerFiles.Add(@"c:\myproject\file.natvis");
267267
launchOptions.SourceMap = new ReadOnlyCollection<SourceMapEntry>(new List<SourceMapEntry>());
268268
launchOptions.Environment = new ReadOnlyCollection<EnvironmentEntry>(new List<EnvironmentEntry>());
269269
Microsoft.DebugEngineHost.HostConfigurationStore configStore = null;

src/MIDebugEngine/Engine.Impl/DebuggedProcess.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ private async Task EnsureModulesLoaded()
543543
public async Task Initialize(HostWaitLoop waitLoop, CancellationToken token)
544544
{
545545
bool success = false;
546-
Natvis.Initialize(_launchOptions.VisualizerFile);
546+
Natvis.Initialize(_launchOptions.VisualizerFiles);
547547
int total = 1;
548548

549549
await this.WaitForConsoleDebuggerInitialize(token);

src/MIDebugEngine/Natvis.Impl/Natvis.cs

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ internal Natvis(DebuggedProcess process, bool showDisplayString, HostConfigurati
260260
_configStore = configStore;
261261
}
262262

263-
public void Initialize(string fileName)
263+
private void InitializeNatvisServices()
264264
{
265265
try
266266
{
@@ -271,52 +271,65 @@ public void Initialize(string fileName)
271271
{
272272
// failed to find the VS Service
273273
}
274+
}
274275

275-
if (!string.IsNullOrEmpty(fileName))
276+
/*
277+
* Handle multiple Natvis files
278+
*/
279+
public void Initialize(List<string> fileNames)
280+
{
281+
InitializeNatvisServices();
282+
if (fileNames != null && fileNames.Count > 0)
276283
{
277-
if (!Path.IsPathRooted(fileName))
284+
foreach (var fileName in fileNames)
278285
{
279-
string globalVisualizersDirectory = _process.Engine.GetMetric("GlobalVisualizersDirectory") as string;
280-
string globalNatVisPath = null;
281-
if (!string.IsNullOrEmpty(globalVisualizersDirectory) && !string.IsNullOrEmpty(fileName))
282-
{
283-
globalNatVisPath = Path.Combine(globalVisualizersDirectory, fileName);
284-
}
285-
286-
// For local launch, try and load natvis next to the target exe if it exists and if
287-
// the exe is rooted. If the file doesn't exist, and also doesn't exist in the global folder fail.
288-
if (_process.LaunchOptions is LocalLaunchOptions)
286+
if (!string.IsNullOrEmpty(fileName))
289287
{
290-
string exePath = (_process.LaunchOptions as LocalLaunchOptions).ExePath;
291-
if (Path.IsPathRooted(exePath))
288+
if (!Path.IsPathRooted(fileName))
292289
{
293-
string localNatvisPath = Path.Combine(Path.GetDirectoryName(exePath), fileName);
290+
string globalVisualizersDirectory = _process.Engine.GetMetric("GlobalVisualizersDirectory") as string;
291+
string globalNatVisPath = null;
292+
if (!string.IsNullOrEmpty(globalVisualizersDirectory) && !string.IsNullOrEmpty(fileName))
293+
{
294+
globalNatVisPath = Path.Combine(globalVisualizersDirectory, fileName);
295+
}
294296

295-
if (File.Exists(localNatvisPath))
297+
// For local launch, try and load natvis next to the target exe if it exists and if
298+
// the exe is rooted. If the file doesn't exist, and also doesn't exist in the global folder fail.
299+
if (_process.LaunchOptions is LocalLaunchOptions)
296300
{
297-
LoadFile(localNatvisPath);
298-
return;
301+
string exePath = (_process.LaunchOptions as LocalLaunchOptions).ExePath;
302+
if (Path.IsPathRooted(exePath))
303+
{
304+
string localNatvisPath = Path.Combine(Path.GetDirectoryName(exePath), fileName);
305+
306+
if (File.Exists(localNatvisPath))
307+
{
308+
LoadFile(localNatvisPath);
309+
return;
310+
}
311+
else if (globalNatVisPath == null || !File.Exists(globalNatVisPath))
312+
{
313+
// Neither local or global path exists, report an error.
314+
_process.WriteOutput(String.Format(CultureInfo.CurrentCulture, ResourceStrings.FileNotFound, localNatvisPath));
315+
return;
316+
}
317+
}
299318
}
300-
else if (globalNatVisPath == null || !File.Exists(globalNatVisPath))
319+
320+
// Local wasn't supported or the file didn't exist. Try and load from globally registered visualizer directory if local didn't work
321+
// or wasn't supported by the launch options
322+
if (!string.IsNullOrEmpty(globalNatVisPath))
301323
{
302-
// Neither local or global path exists, report an error.
303-
_process.WriteOutput(String.Format(CultureInfo.CurrentCulture, ResourceStrings.FileNotFound, localNatvisPath));
304-
return;
324+
LoadFile(globalNatVisPath);
305325
}
306326
}
327+
else
328+
{
329+
// Full path to the natvis file.. Just try the load
330+
LoadFile(fileName);
331+
}
307332
}
308-
309-
// Local wasn't supported or the file didn't exist. Try and load from globally registered visualizer directory if local didn't work
310-
// or wasn't supported by the launch options
311-
if (!string.IsNullOrEmpty(globalNatVisPath))
312-
{
313-
LoadFile(globalNatVisPath);
314-
}
315-
}
316-
else
317-
{
318-
// Full path to the natvis file.. Just try the load
319-
LoadFile(fileName);
320333
}
321334
}
322335
}

test/CppTests/CppTests.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,7 @@
4545
</ItemGroup>
4646

4747
<Target Name="Copy Config and Debuggees" BeforeTargets="DropFiles">
48-
<Copy
49-
SourceFiles="@(ContentFiles)"
50-
DestinationFolder="$(DropDir)\contentFiles\%(RecursiveDir)"
51-
/>
48+
<Copy SourceFiles="@(ContentFiles)" DestinationFolder="$(DropDir)\contentFiles\%(RecursiveDir)" />
5249
</Target>
5350

5451
<ItemGroup>

test/CppTests/OpenDebug/CrossPlatCpp/LaunchCommand.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Generic;
66
using System.IO;
77
using DebuggerTesting.OpenDebug.Commands;
8+
using DebuggerTesting.OpenDebug.CrossPlatCpp;
89
using DebuggerTesting.Utilities;
910
using Newtonsoft.Json;
1011

@@ -34,7 +35,8 @@ public sealed class CppLaunchCommandArgs : LaunchCommandArgs
3435
public string MIMode;
3536

3637
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
37-
public string VisualizerFile;
38+
public object VisualizerFile;
39+
3840

3941
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
4042
public bool ShowDisplayString;
@@ -50,8 +52,13 @@ public class LaunchCommand : LaunchCommand<CppLaunchCommandArgs>
5052
/// <param name="program">The full path to the program to launch</param>
5153
/// <param name="architecture">The architecture of the program</param>
5254
/// <param name="args">[OPTIONAL] Args to pass to the program</param>
53-
public LaunchCommand(IDebuggerSettings settings, string program, string visualizerFile = null, bool isAttach = false, params string[] args)
55+
public LaunchCommand(IDebuggerSettings settings, string program, object visualizerFile = null, bool isAttach = false, params string[] args)
5456
{
57+
if (!(visualizerFile == null || visualizerFile is string || visualizerFile is List<string>))
58+
{
59+
throw new ArgumentOutOfRangeException(nameof(visualizerFile));
60+
}
61+
5562
this.Timeout = TimeSpan.FromSeconds(15);
5663

5764
this.Args.name = CreateName(settings);
@@ -75,7 +82,7 @@ public LaunchCommand(IDebuggerSettings settings, string program, string visualiz
7582
this.Args.targetArchitecture = settings.DebuggeeArchitecture.ToArchitectureString();
7683
this.Args.MIMode = settings.MIMode;
7784
this.Args.VisualizerFile = visualizerFile;
78-
this.Args.ShowDisplayString = !string.IsNullOrEmpty(visualizerFile);
85+
this.Args.ShowDisplayString = visualizerFile != null;
7986
}
8087
}
8188

0 commit comments

Comments
 (0)