diff --git a/build/package_versions.settings.targets b/build/package_versions.settings.targets
index 5add95409..c1eee41bb 100644
--- a/build/package_versions.settings.targets
+++ b/build/package_versions.settings.targets
@@ -33,7 +33,7 @@
15.0.392
15.0.392
17.0.0-previews-1-31410-258
- 17.0.1600
+ 17.3.2093
4.3.0
\ No newline at end of file
diff --git a/build/version.settings.targets b/build/version.settings.targets
index 6e2da2b5c..7f0008e95 100644
--- a/build/version.settings.targets
+++ b/build/version.settings.targets
@@ -3,7 +3,7 @@
17
- 1
+ 4
2020
diff --git a/src/MIDebugEngine/Engine.Impl/Variables.cs b/src/MIDebugEngine/Engine.Impl/Variables.cs
index d420beb3d..f087bd593 100644
--- a/src/MIDebugEngine/Engine.Impl/Variables.cs
+++ b/src/MIDebugEngine/Engine.Impl/Variables.cs
@@ -431,7 +431,13 @@ private string StripFormatSpecifier(string exp, out string formatSpecifier)
// TODO: could return '(T(*)[n])(exp)' but requires T
var m = Regex.Match(trimmed, @"^\[?(\d+)\]?$");
if (m.Success)
+ {
+ if (_engine.DebuggedProcess.MICommandFactory.Mode == MIMode.Gdb)
+ return "*" + exp.Substring(0, lastComma) + "@" + trimmed; // this does not work for lldb
+
return exp.Substring(0, lastComma);
+ }
+
// array with dynamic size
if (Regex.Match(trimmed, @"^\[([a-zA-Z_][a-zA-Z_\d]*)\]$").Success)
@@ -518,13 +524,8 @@ internal async Task Eval(uint radix, enum_EVALFLAGS dwFlags = 0, DAPEvalFlags dw
string consoleResults = null;
consoleResults = await MIDebugCommandDispatcher.ExecuteCommand(consoleCommand, _debuggedProcess, ignoreFailures: true);
- Value = String.Empty;
+ Value = consoleResults;
this.TypeName = null;
-
- if (!String.IsNullOrEmpty(consoleResults))
- {
- _debuggedProcess.WriteOutput(consoleResults);
- }
}
else
{
diff --git a/src/MIDebugEngine/Natvis.Impl/Natvis.cs b/src/MIDebugEngine/Natvis.Impl/Natvis.cs
index 9c201d0e0..1abb2d780 100755
--- a/src/MIDebugEngine/Natvis.Impl/Natvis.cs
+++ b/src/MIDebugEngine/Natvis.Impl/Natvis.cs
@@ -72,7 +72,7 @@ public uint Size()
}
}
- internal sealed class VisualizerWrapper : SimpleWrapper
+ internal class VisualizerWrapper : SimpleWrapper
{
public readonly Natvis.VisualizerInfo Visualizer;
private readonly bool _isVisualizerView;
@@ -90,6 +90,60 @@ public override string FullName()
return _isVisualizerView ? Parent.Name + ",viz" : Name;
}
}
+ ///
+ /// Represents a VisualizedWrapper that starts at an offset.
+ ///
+ internal class PaginatedVisualizerWrapper : VisualizerWrapper
+ {
+ public readonly uint StartIndex;
+
+ public PaginatedVisualizerWrapper(string name, AD7Engine engine, IVariableInformation underlyingVariable, Natvis.VisualizerInfo viz, bool isVisualizerView, uint startIndex=0)
+ : base(name, engine, underlyingVariable, viz, isVisualizerView)
+ {
+ StartIndex = startIndex;
+ }
+ }
+ ///
+ /// Represents the continuation of a LinkedListItemsType.
+ ///
+ internal sealed class LinkedListContinueWrapper : PaginatedVisualizerWrapper
+ {
+ public readonly IVariableInformation ContinueNode;
+ public LinkedListContinueWrapper(string name, AD7Engine engine, IVariableInformation underlyingVariable, Natvis.VisualizerInfo viz, bool isVisualizerView, IVariableInformation continueNode, uint startIndex)
+ : base(name, engine, underlyingVariable, viz, isVisualizerView, startIndex)
+ {
+ ContinueNode = continueNode;
+ }
+ }
+
+ internal class Node
+ {
+ public enum ScanState
+ {
+ left, value, right
+ }
+ public ScanState State { get; set; }
+ public IVariableInformation Content { get; private set; }
+ public Node(IVariableInformation v)
+ {
+ Content = v;
+ State = ScanState.left;
+ }
+ }
+ ///
+ /// Represents the continuation of a TreeItemsType.
+ ///
+ internal sealed class TreeContinueWrapper : PaginatedVisualizerWrapper
+ {
+ public readonly Node ContinueNode;
+ public readonly Stack Nodes;
+ public TreeContinueWrapper(string name, AD7Engine engine, IVariableInformation underlyingVariable, Natvis.VisualizerInfo viz, bool isVisualizerView, Node continueNode, Stack nodes, uint startIndex)
+ : base (name, engine, underlyingVariable, viz, isVisualizerView, startIndex)
+ {
+ ContinueNode = continueNode;
+ Nodes = nodes;
+ }
+ }
internal struct VisualizerId
{
@@ -506,7 +560,7 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
}
foreach (var i in expandType.Items)
{
- if (i is ItemType)
+ if (i is ItemType && !(variable is PaginatedVisualizerWrapper)) // we do not want to repeatedly display other ItemTypes when expanding the "[More...]" node
{
ItemType item = (ItemType)i;
if (!EvalCondition(item.Condition, variable, visualizer.ScopedNames))
@@ -526,7 +580,6 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
uint size = 0;
string val = GetExpressionValue(item.Size, variable, visualizer.ScopedNames);
size = MICore.Debugger.ParseUint(val, throwOnError: true);
- size = size > MAX_EXPAND ? MAX_EXPAND : size; // limit expansion
ValuePointerType[] vptrs = item.ValuePointer;
foreach (var vp in vptrs)
{
@@ -551,7 +604,23 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
arrayExpr.EnsureChildren();
if (arrayExpr.CountChildren != 0)
{
- children.AddRange(arrayExpr.Children);
+ uint currentIndex = 0;
+ if (variable is PaginatedVisualizerWrapper pvwVariable)
+ {
+ currentIndex = pvwVariable.StartIndex;
+ }
+ uint maxIndex = currentIndex + MAX_EXPAND > size ? size : currentIndex + MAX_EXPAND;
+ for (uint index = currentIndex; index < maxIndex; ++index)
+ {
+ children.Add(arrayExpr.Children[index]);
+ }
+
+ currentIndex += MAX_EXPAND;
+ if (size > currentIndex)
+ {
+ IVariableInformation moreVariable = new PaginatedVisualizerWrapper(ResourceStrings.MoreView, _process.Engine, variable, FindType(variable), isVisualizerView: true, currentIndex);
+ children.Add(moreVariable);
+ }
}
break;
}
@@ -575,8 +644,15 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
}
string val = GetExpressionValue(item.Size, variable, visualizer.ScopedNames);
uint size = MICore.Debugger.ParseUint(val, throwOnError: true);
- size = size > MAX_EXPAND ? MAX_EXPAND : size; // limit expansion
- IVariableInformation headVal = GetExpression(item.HeadPointer, variable, visualizer.ScopedNames);
+ IVariableInformation headVal;
+ if (variable is TreeContinueWrapper tcw)
+ {
+ headVal = tcw.ContinueNode.Content;
+ }
+ else
+ {
+ headVal = GetExpression(item.HeadPointer, variable, visualizer.ScopedNames);
+ }
ulong head = MICore.Debugger.ParseAddr(headVal.Value);
var content = new List();
if (head != 0 && size != 0)
@@ -601,7 +677,18 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
{
continue;
}
- TraverseTree(headVal, goLeft, goRight, getValue, children, size);
+ uint startIndex = 0;
+ IVariableInformation parent = variable;
+ if (variable is PaginatedVisualizerWrapper visualizerWrapper)
+ {
+ startIndex = visualizerWrapper.StartIndex;
+ parent = visualizerWrapper.Parent;
+ }
+ else if (variable is SimpleWrapper simpleWrapper)
+ {
+ parent = simpleWrapper.Parent;
+ }
+ TraverseTree(headVal, goLeft, goRight, getValue, children, size, variable, parent, startIndex);
}
}
else if (i is LinkedListItemsType)
@@ -632,9 +719,16 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
{
string val = GetExpressionValue(item.Size, variable, visualizer.ScopedNames);
size = MICore.Debugger.ParseUint(val);
- size = size > MAX_EXPAND ? MAX_EXPAND : size; // limit expansion
}
- IVariableInformation headVal = GetExpression(item.HeadPointer, variable, visualizer.ScopedNames);
+ IVariableInformation headVal;
+ if (variable is LinkedListContinueWrapper llcw)
+ {
+ headVal = llcw.ContinueNode;
+ }
+ else
+ {
+ headVal = GetExpression(item.HeadPointer, variable, visualizer.ScopedNames);
+ }
ulong head = MICore.Debugger.ParseAddr(headVal.Value);
var content = new List();
if (head != 0 && size != 0)
@@ -662,7 +756,18 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
{
continue;
}
- TraverseList(headVal, goNext, getValue, children, size, item.NoValueHeadPointer);
+ uint startIndex = 0;
+ IVariableInformation parent = variable;
+ if (variable is PaginatedVisualizerWrapper visualizerWrapper)
+ {
+ startIndex = visualizerWrapper.StartIndex;
+ parent = visualizerWrapper.Parent;
+ }
+ else if (variable is SimpleWrapper simpleWrapper)
+ {
+ parent = simpleWrapper.Parent;
+ }
+ TraverseList(headVal, goNext, getValue, children, size, item.NoValueHeadPointer, parent, startIndex);
}
}
else if (i is IndexListItemsType)
@@ -691,7 +796,6 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
{
string val = GetExpressionValue(s.Value, variable, visualizer.ScopedNames);
size = MICore.Debugger.ParseUint(val);
- size = size > MAX_EXPAND ? MAX_EXPAND : size; // limit expansion
break;
}
}
@@ -708,7 +812,13 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
{
string processedExpr = ReplaceNamesInExpression(v.Value, variable, visualizer.ScopedNames);
Dictionary indexDic = new Dictionary();
- for (uint index = 0; index < size; ++index)
+ uint currentIndex = 0;
+ if (variable is PaginatedVisualizerWrapper pvwVariable)
+ {
+ currentIndex = pvwVariable.StartIndex;
+ }
+ uint maxIndex = currentIndex + MAX_EXPAND > size ? size : currentIndex + MAX_EXPAND;
+ for (uint index = currentIndex; index < maxIndex; ++index) // limit expansion to first 50 elements
{
indexDic["$i"] = index.ToString(CultureInfo.InvariantCulture);
string finalExpr = ReplaceNamesInExpression(processedExpr, null, indexDic);
@@ -716,6 +826,14 @@ private IVariableInformation[] ExpandVisualized(IVariableInformation variable)
expressionVariable.SyncEval();
children.Add(expressionVariable);
}
+
+ currentIndex += MAX_EXPAND;
+ if (size > currentIndex)
+ {
+ IVariableInformation moreVariable = new PaginatedVisualizerWrapper(ResourceStrings.MoreView, _process.Engine, variable, visualizer, isVisualizerView: true, currentIndex);
+ children.Add(moreVariable);
+ }
+
break;
}
}
@@ -788,27 +906,35 @@ private Traverse GetTraverse(string direction, IVariableInformation node)
return go;
}
- private class Node
+ ///
+ /// Traverse tree based on specified startIndex.
+ /// Then add wrappers for Natvis tree visualizations.
+ ///
+ /// Root of the tree
+ /// Traverse callback to retrieve left child of root
+ /// Traverse callback to retrieve right child of root
+ /// Callback to retrieve value of root
+ /// List of variables to display given current variable
+ /// Number of nodes in tree
+ /// Tree to traverse if size <= 50. Otherwise, expandable continue wrapper.
+ /// The tree to traverse
+ /// Index to start traversing from
+ ///
+ private void TraverseTree(IVariableInformation root, Traverse goLeft, Traverse goRight, Traverse getValue, List content, uint size, IVariableInformation variable, IVariableInformation parent, uint startIndex)
{
- public enum ScanState
+ uint i = startIndex;
+ var nodes = new Stack();
+ if (variable is TreeContinueWrapper tcwVariable)
{
- left, value, right
+ nodes = tcwVariable.Nodes;
}
- public ScanState State { get; set; }
- public IVariableInformation Content { get; private set; }
- public Node(IVariableInformation v)
+ else
{
- Content = v;
- State = ScanState.left;
+ nodes.Push(new Node(root));
}
- }
- private void TraverseTree(IVariableInformation root, Traverse goLeft, Traverse goRight, Traverse getValue, List content, uint size)
- {
- uint i = 0;
- var nodes = new Stack();
- nodes.Push(new Node(root));
- while (nodes.Count > 0 && i < size)
+ uint maxIndex = i + MAX_EXPAND > size ? size : i + MAX_EXPAND;
+ while (nodes.Count > 0 && i < maxIndex)
{
switch (nodes.Peek().State)
{
@@ -847,15 +973,22 @@ private void TraverseTree(IVariableInformation root, Traverse goLeft, Traverse g
break;
}
}
+ if (size > i)
+ {
+ IVariableInformation tcw = new TreeContinueWrapper(ResourceStrings.MoreView, _process.Engine, parent, FindType(parent), isVisualizerView: true, nodes.Peek(), nodes, i);
+ content.Add(tcw);
+ }
}
- private void TraverseList(IVariableInformation root, Traverse goNext, Traverse getValue, List content, uint size, bool noValueInRoot)
+ private void TraverseList(IVariableInformation root, Traverse goNext, Traverse getValue, List content, uint size, bool noValueInRoot, IVariableInformation parent, uint startIndex)
{
- uint i = 0;
+ uint i = startIndex;
IVariableInformation node = root;
ulong rootAddr = MICore.Debugger.ParseAddr(node.Value);
ulong nextAddr = rootAddr;
- while (node != null && nextAddr != 0 && i < size)
+
+ uint maxIndex = i + MAX_EXPAND > size ? size : i + MAX_EXPAND;
+ while (node != null && nextAddr != 0 && i < maxIndex)
{
if (!noValueInRoot || nextAddr != rootAddr)
{
@@ -866,7 +999,7 @@ private void TraverseList(IVariableInformation root, Traverse goNext, Traverse g
i++;
}
}
- if (i < size)
+ if (i < maxIndex)
{
node = goNext(node);
}
@@ -877,6 +1010,11 @@ private void TraverseList(IVariableInformation root, Traverse goNext, Traverse g
break;
}
}
+ if (size > i)
+ {
+ IVariableInformation llcw = new LinkedListContinueWrapper(ResourceStrings.MoreView, _process.Engine, parent, FindType(parent), isVisualizerView: true, goNext(node), i);
+ content.Add(llcw);
+ }
}
private static string BaseName(string type)
diff --git a/src/MIDebugEngine/ResourceStrings.Designer.cs b/src/MIDebugEngine/ResourceStrings.Designer.cs
index a084c9a27..12d25ce8d 100755
--- a/src/MIDebugEngine/ResourceStrings.Designer.cs
+++ b/src/MIDebugEngine/ResourceStrings.Designer.cs
@@ -301,6 +301,15 @@ internal static string ModuleUnloadMessage {
}
}
+ ///
+ /// Looks up a localized string similar to [More...].
+ ///
+ internal static string MoreView {
+ get {
+ return ResourceManager.GetString("MoreView", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Explicit refresh required for visualized expressions.
///
diff --git a/src/MIDebugEngine/ResourceStrings.resx b/src/MIDebugEngine/ResourceStrings.resx
index fc9111daf..ea389d970 100755
--- a/src/MIDebugEngine/ResourceStrings.resx
+++ b/src/MIDebugEngine/ResourceStrings.resx
@@ -156,6 +156,9 @@
Symbols loaded.
+
+ [More...]
+
Symbols loaded - {0}
diff --git a/src/OpenDebugAD7/AD7DebugSession.cs b/src/OpenDebugAD7/AD7DebugSession.cs
index 5e0720b9f..425061684 100644
--- a/src/OpenDebugAD7/AD7DebugSession.cs
+++ b/src/OpenDebugAD7/AD7DebugSession.cs
@@ -811,7 +811,6 @@ private void StepInternal(int threadId, enum_STEPKIND stepKind, SteppingGranular
}
}
- BeforeContinue();
ErrorBuilder builder = new ErrorBuilder(() => errorMessage);
m_isStepping = true;
@@ -837,6 +836,9 @@ private void StepInternal(int threadId, enum_STEPKIND stepKind, SteppingGranular
m_isStopped = true;
throw;
}
+ // The program should now be stepping, so it is safe to discard the
+ // cached program state.
+ BeforeContinue();
}
private enum ClientId
diff --git a/test/CppTests/Tests/NatvisTests.cs b/test/CppTests/Tests/NatvisTests.cs
index d21b1b34f..0a91a3a8d 100644
--- a/test/CppTests/Tests/NatvisTests.cs
+++ b/test/CppTests/Tests/NatvisTests.cs
@@ -118,14 +118,13 @@ public void TestIndexListItems(ITestSettings settings)
this.Comment("Verifying IndexListItems natvis");
var arr = currentFrame.GetVariable("arr");
- Assert.Equal("{ size=15 }", arr.Value);
+ Assert.Equal("{ size=52 }", arr.Value);
// Index element for IndexListItems
// Natvis retrieves items in reverse order.
- Assert.Equal("196", arr.GetVariable("[0]").Value);
- Assert.Equal("16", arr.GetVariable("[10]").Value);
- Assert.Equal("0", arr.GetVariable("[14]").Value);
- // TODO: Add test below when we can support the [More..] expansion to handle >50 elements
+ Assert.Equal("2601", arr.GetVariable("[0]").Value);
+ Assert.Equal("1681", arr.GetVariable("[10]").Value);
+ Assert.Equal("0", arr.GetVariable("[More...]").GetVariable("[51]").Value);
}
runner.Expects.ExitedEvent(exitCode: 0).TerminatedEvent().AfterContinue();
@@ -168,14 +167,14 @@ public void TestArrayItems(ITestSettings settings)
this.Comment("Verifying ArrayItems natvis");
var ll = currentFrame.GetVariable("vec");
- Assert.Equal("{ size=10 }", ll.Value);
+ Assert.Equal("{ size=52 }", ll.Value);
// Custom Item in natvis
- Assert.Equal("10", ll.GetVariable("Size").Value);
+ Assert.Equal("52", ll.GetVariable("Size").Value);
// Index element for ArrayItems
Assert.Equal("20", ll.GetVariable("[5]").Value);
- // TODO: Add test below when we can support the [More..] expansion to handle >50 elements
+ Assert.Equal("0", ll.GetVariable("[More...]").GetVariable("[51]").Value);
}
runner.Expects.ExitedEvent(exitCode: 0).TerminatedEvent().AfterContinue();
@@ -220,8 +219,7 @@ public void TestLinkedListItems(ITestSettings settings)
// Index element for LinkedListItems
Assert.Equal("5", ll.GetVariable("[5]").Value);
- // TODO: Uncomment line below when we can support the [More..] expansion to handle >50 elements
- // Assert.Equal("75", ll.GetVariable("[More...]").GetVariable("[75]").Value);
+ Assert.Equal("75", ll.GetVariable("[More...]").GetVariable("[75]").Value);
}
runner.Expects.ExitedEvent(exitCode: 0).TerminatedEvent().AfterContinue();
@@ -330,6 +328,45 @@ public void TestThisConditional(ITestSettings settings)
}
}
+ [Theory]
+ [DependsOnTest(nameof(CompileNatvisDebuggee))]
+ [RequiresTestSettings]
+ // Disable on macOS
+ [UnsupportedDebugger(SupportedDebugger.Lldb, SupportedArchitecture.x64 | SupportedArchitecture.x86)]
+ public void TestArrayPointer(ITestSettings settings)
+ {
+ this.TestPurpose("This test checks if the comma format specifier is visualized.");
+ this.WriteSettings(settings);
+
+ IDebuggee debuggee = Debuggee.Open(this, settings.CompilerSettings, NatvisName, DebuggeeMonikers.Natvis.Default);
+
+ this.Comment("Run the debuggee, check argument count");
+ using (IDebuggerRunner runner = CreateDebugAdapterRunner(settings))
+ {
+ this.Comment("Configure launch");
+ LaunchCommand launch = new LaunchCommand(settings.DebuggerSettings, debuggee.OutputPath);
+ runner.RunCommand(launch);
+
+ this.Comment("Set Breakpoint");
+ SourceBreakpoints writerBreakpoints = debuggee.Breakpoints(NatvisSourceName, ReturnSourceLine);
+ runner.SetBreakpoints(writerBreakpoints);
+
+ runner.Expects.StoppedEvent(StoppedReason.Breakpoint).AfterConfigurationDone();
+
+ using (IThreadInspector threadInspector = runner.GetThreadInspector())
+ {
+ IFrameInspector currentFrame = threadInspector.Stack.First();
+
+ this.Comment("Verifying comma format specifier");
+ int[] expected = { 0, 1, 4, 9 };
+ currentFrame.AssertEvaluateAsIntArray("arr._array,4", EvaluateContext.Watch, expected);
+ }
+
+ runner.Expects.ExitedEvent(exitCode: 0).TerminatedEvent().AfterContinue();
+ runner.DisconnectAndVerify();
+ }
+ }
+
#endregion
}
}
diff --git a/test/CppTests/debuggees/natvis/src/main.cpp b/test/CppTests/debuggees/natvis/src/main.cpp
index 191a646bd..4f37b46b7 100644
--- a/test/CppTests/debuggees/natvis/src/main.cpp
+++ b/test/CppTests/debuggees/natvis/src/main.cpp
@@ -14,7 +14,7 @@ int main(int argc, char** argv)
{
SimpleDisplayObject obj_1;
- SimpleVector vec(10);
+ SimpleVector vec(52);
vec.Set(5, 20);
SimpleLinkedList ll;
@@ -32,8 +32,8 @@ int main(int argc, char** argv)
map.Insert(4);
map.Insert(-72);
- SimpleArray arr(15);
- for (int i = 0 ; i < 15; i++)
+ SimpleArray arr(52);
+ for (int i = 0 ; i < 52; i++)
{
arr[i] = i * i;
}
diff --git a/test/DebugAdapterRunner/DebugAdapterRunner.nuspec b/test/DebugAdapterRunner/DebugAdapterRunner.nuspec
index 541bac89b..92e356863 100644
--- a/test/DebugAdapterRunner/DebugAdapterRunner.nuspec
+++ b/test/DebugAdapterRunner/DebugAdapterRunner.nuspec
@@ -14,7 +14,7 @@
https://go.microsoft.com/fwlink/?LinkID=746387
© Microsoft Corporation. All rights reserved.
-
+