Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Span: Add debugger viewers to Span<T> and ReadOnlySpan<T> #19629

Closed
KrzysztofCwalina opened this issue Dec 9, 2016 · 88 comments
Closed

Span: Add debugger viewers to Span<T> and ReadOnlySpan<T> #19629

KrzysztofCwalina opened this issue Dec 9, 2016 · 88 comments
Assignees
Labels
area-System.Memory enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@KrzysztofCwalina
Copy link
Member

It's difficult to debug programs that use spans because the span items cannot be easily inspected in the debugger.

@AlexGhiondea
Copy link
Contributor

@ahsonkhan is this something you are working on?

@ahsonkhan
Copy link
Contributor

Not atm. Would you like me to tackle it?

@AlexGhiondea
Copy link
Contributor

I marked it as up for grabs. If you have time to work on it, then let me know and I will assign it to you!

@karelz karelz changed the title Override ToString or add debugger viewers to Span<T> and ReadOnlySpan<T> Span: Override ToString or add debugger viewers to Span<T> and ReadOnlySpan<T> Dec 18, 2016
@felipepessoto
Copy link
Contributor

Hi @karelz, I plan to work on it with a colleague. Have you defined if we need to Override ToString, add debugger viewer or both?

Thanks

@karelz
Copy link
Member

karelz commented Dec 19, 2016

@KrzysztofCwalina @jkotas can comment which solution is better ...

@jkotas
Copy link
Member

jkotas commented Dec 20, 2016

The debugger viewer is the important one to have.

I am not sure what ToString would do to be useful. Span is essentially a collection type. We do not have ToString overrides on collection types in general.

@felipepessoto
Copy link
Contributor

@mramosMS will fix this issue. Thanks

@karelz
Copy link
Member

karelz commented Dec 21, 2016

@mramosMS I sent you collaboration invite. Once you accept (ping me when you do it), I will be able to assign the issue to you (GitHub restrictions/policy).

@karelz
Copy link
Member

karelz commented Jan 29, 2017

@mramosMS are you still working on it?

@mramosMS
Copy link

mramosMS commented Jan 29, 2017 via email

@felipepessoto
Copy link
Contributor

I was trying to use my own build of System.Memory (unsuccessfully btw) and figure out that we have two implementations: https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs and https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Span.cs.

How does it work?

Thanks

@felipepessoto
Copy link
Contributor

Hi guys, after building CoreFX, I can't find System.Memory package. Do you know how to generate it?

I just found the DLL (at "corefx\bin\runtime\netcoreapp-Windows_NT-Debug-x64\System.Memory.dll"). So, I'm copying it manually to System.Memory package cache folder "C:\Users\myuser.nuget\packages\System.Memory\4.4.0-beta-24913-01\lib\netstandard1.0".

It works and I can use it to debug on Visual Studio and see how the debug view works. But it is ugly :)

@karelz
Copy link
Member

karelz commented Feb 22, 2017

@ahsonkhan @jkotas can you please help?

@ahsonkhan
Copy link
Contributor

ahsonkhan commented Feb 22, 2017

I was trying to use my own build of System.Memory (unsuccessfully btw) and figure out that we have two implementations: https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs and https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Span.cs.

How does it work?

For a netcoreapp, the System.Memory project contains some Span APIs that type forward to the implementation in mscorlib (Fast Span).
For a netstandard, the Span APIs are implemented within System.Memory/.../Span.cs (Slow Span).

The System.Memory csproj file should help clarify since it is a partial facade: https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System.Memory.csproj#L29

If you look at the Manifest of the System.Memory.dll that gets built, you will see the type forwarding:
image

@ahsonkhan
Copy link
Contributor

@fujiy, @mramosMS, does this unblock you to get further? Feel free to ask more questions.

@felipepessoto
Copy link
Contributor

I sent my workaround to @mramosMS, I think it worked for him.

@mramosMS
Copy link

mramosMS commented Mar 3, 2017 via email

@ahsonkhan
Copy link
Contributor

@KrzysztofCwalina, do you have any insights on implementing DebuggerTypeProxy?

@KrzysztofCwalina
Copy link
Member Author

I implemented such proxy some time ago. It did not work. There was an issue in the debugger that prevented it from working. I don't think the issues have been resolved.

@KrzysztofCwalina
Copy link
Member Author

KrzysztofCwalina commented Mar 3, 2017

@fujiy, which particular part are you thinking about implementing? the proxy? If yes, I do understand how proxies work. The particular implementation of the proxy for Span<T> that I implemented simply called this.ToArray(), and it did not work. The reason is that the debugger uses an interpreter in some scenarios and it cannot interpret something about span (which is a very bizarre type).

Possibly there is a different way to implement the proxy, but I am not sure what would make the interpreter work.

@felipepessoto
Copy link
Contributor

Got it. We can try some different ways to retrieve the collection.
Besides the Items and the Length, there is something else we need to expose?

@felipepessoto
Copy link
Contributor

felipepessoto commented Mar 5, 2017

@ahsonkhan for netcoreapp 1.0 the Slow Span is also used?
To test the netcoreapp 2.0 implementation I think we need to follow these steps: https://github.com/dotnet/coreclr/blob/master/Documentation/workflow/UsingYourBuild.md, right?

I can't figure out how to generate the System.Memory package

Thanks

@felipepessoto
Copy link
Contributor

felipepessoto commented Mar 6, 2017

@mramosMS, as @KrzysztofCwalina said, most of methods in Span class don't work when called by the debugger interpreter, so I implemented the proxy exposing the _pinnable variable with success. (System.Memory version, the netcoreapp uses mscorlib and the implementation is different)

I found a way to test it more easily, instead of changing CoreFX source code, I implemented it with a class in my Console project using a Global Attribute:

[assembly: DebuggerTypeProxy(typeof(MySpanDebugView<>), Target = typeof(Span<>))]

[assembly: DebuggerTypeProxy(typeof(MySpanDebugView<>), Target = typeof(Span<>))]
[assembly: DebuggerDisplay("My Count = {Length}", Target = typeof(Span<>))]
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] src = { 10, 2, 3 };

            Span<int> srcSpan = new Span<int>(src);

            Console.ReadLine();
        }
    }

    public class MySpanDebugView<T>
    {
        private readonly Span<T> _collection;

        public MySpanDebugView(Span<T> collection)
        {
            _collection = collection;
        }

        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public object Items
        {
            get
            {
                var field = typeof(Span<T>).GetField("_pinnable", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);
                var value = field.GetValue(_collection);
                return value;
            }
        }
    }
}

So I can build/test/debug quickly.

When implementing inside Span class you could remove the Reflection code:

/// <summary>
   /// Span debug view
   /// </summary>
    public class MySpanDebugView<U>
    {
        private readonly Span<U> _collection;

		/// <summary>
        /// Span debug view
        /// </summary>
        public MySpanDebugView(Span<U> collection)
        {
            _collection = collection;
        }

		/// <summary>
        /// Span debug view
        /// </summary>
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public object Items
        {
            get
            {
                return _collection.Pinnable;
            }
        }
    }

/// <summary>
/// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
/// </summary>
[DebuggerDisplay("My Length = {Length}")]
[DebuggerTypeProxy(typeof(MySpanDebugView<>))]
public struct Span<T>
{	
....

Let me know if it works for you.

@plnelson
Copy link

Thanks @jkotas. Opened internal bug 402754.

@felipepessoto
Copy link
Contributor

Hi guys, I'm afraid I can't make it work for Fast as I thought.

I worked in my tests, where I write de Debug Type proxy in my console app. But when I copy the same code to CoreClr and build it, i doesn't work. VS just says that can't evaluate it.

@ahsonkhan
Copy link
Contributor

ahsonkhan commented Apr 8, 2017

Hi guys, I'm still trying to make FastSpan Debug Viewer works.
How do I create the type forward for new types? I added a SpanDebugView (that ends up at System.Private.CoreLib.ni.dll), but can't reference it my code to debug, because corefx\bin\Windows_NT.AnyCPU.Debug\System.Memory\netcoreapp\System.Memory.dll doesn't contain that type or the type forward.
I also didn't find the NuGet Packages for System.Memory

The package is on myget currently:
https://dotnet.myget.org/feed/dotnet-core/package/nuget/System.Memory

For type forwarding, look at the csproj and ref assembly:
https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System.Memory.csproj
https://github.com/dotnet/corefx/blob/master/src/System.Memory/ref/System.Memory.cs

You will likely need to add the type in coreclr as well:
https://github.com/dotnet/coreclr/tree/master/src/mscorlib/src/System

@felipepessoto
Copy link
Contributor

felipepessoto commented Apr 8, 2017

Hi @ahsonkhan. I did that to Slow Span. But I didn't figure out how System.Memory.dll for Fast Span is built (the one used at runtime). How it generates the type forward to System.Private.CoreLib. It currently contains type-forward just for Span and ReadOnlySpan

The best guess I have is that it generates autommatically. But I still didn't figure out how to make corefx build consume the packages generated by my coreclr build.

Here is explained (a bit outdated) how to do that, but I don't want to (and won't be able) to send a PR to CoreClr and wait you merge it, before I know if it will work. https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/adding_new_public_apis.md

@jkotas
Copy link
Member

jkotas commented Apr 9, 2017

The best guess I have is that it generates autommatically

Correct. IsPartialFacadeAssembly in .csproj file triggers it.

But I still didn't figure out how to make corefx build consume the packages generated by my coreclr build.

https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/developer-guide.md#testing-with-private-coreclr-bits

@ahsonkhan
Copy link
Contributor

@fujiy, do you have any updates for fast span or maybe some more questions?

@felipepessoto
Copy link
Contributor

felipepessoto commented Apr 20, 2017

I'm still trying to build coreclr/corefx with 2017 without success, my computer has just 100GB so I can't install both 2015 and 2017.
https://github.com/dotnet/coreclr/issues/10890

@ahsonkhan
Copy link
Contributor

ahsonkhan commented Apr 20, 2017

Are you able to build using the command line instead?

Edit: Ignore, I just saw the issue you linked to.

@felipepessoto
Copy link
Contributor

Do you know why when running the application it loads the System.Private.CoreLib.ni.dll from

packages\runtime.win-x64.microsoft.netcore.app\2.0.0-preview2-002066-00\runtimes\win-x64\native

Instead of

packages\runtime.win7-x64.microsoft.netcore.runtime.coreclr\2.0.0-preview2-25210-0\runtimes\win7-x64\native

I didn't find out how to generate the Microsoft.NerCore.App package

@felipepessoto
Copy link
Contributor

@ahsonkhan I guess I can't implement it to Fast Span, because VIL doesn't support calling methods from Unsafe class, they are implemented by EE, e.g.:

    /// <summary>
    /// Returns a pointer to the given by-ref parameter.
    /// </summary>
    [NonVersionable]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void* AsPointer<T>(ref T value)
    {
        // The body of this function will be replaced by the EE with unsafe code!!!
        // See getILIntrinsicImplementationForUnsafe for how this happens.  
        throw new InvalidOperationException();
    }

I also tried to use GCHandle without success

@ahsonkhan
Copy link
Contributor

I didn't find out how to generate the Microsoft.NerCore.App package

@mellinoe, can you help with clarifying this?

I guess I can't implement it to Fast Span, because VIL doesn't support calling methods from Unsafe class, they are implemented by EE.

@jkotas, @plnelson, is this something that we need the VS debugger team to help with?

@felipepessoto
Copy link
Contributor

felipepessoto commented Apr 26, 2017

@ahsonkhan, @plnelson created an internal bug 286592, I don't know if it includes the methods generated by EE.

I'm facing some issues using my coreclr build, when I run dotnet publish and execute the result, I receive:

Failed to initialize CoreCLR, HRESULT: 0x80004005

I could make it work copying the content of my coreclr\bin\Product\Windows_NT.x64.Debug to myapp\bin\Debug\netcoreapp2.0\win7-x64\publish folder.

I also couldn't find out how to generate System.Memory package, so I'm manually replacing the dll in Nuget cache folder and publish folder.

@jkotas
Copy link
Member

jkotas commented Apr 26, 2017

@jkotas, @plnelson, is this something that we need the VS debugger team to help with?

Yes. I have said above: "I doubt that you will be able to make this work by just writing a debugger display proxy. I expect that the VS debugger expression eval part will need some fixes to make this work."

@ahsonkhan
Copy link
Contributor

From @KrzysztofCwalina

@ahsonkhan, what's the plan if we cannot fix the debugger before we ship System.Memory? Would we live or remove the proxy? If we leave it (hoping that the debugger will catch up), should/could we convert the exception to something more explanatory?

From @ahsonkhan

I don't know if we can convert the exception to something more explanatory, if we leave the SpanDebugView as is.
If we explicitly throw a meaningful exception in the SpanDebugView then we would have to revert that when the VS debugger catches up.
Calling ToArray calls the JIT intrinsics and that is resulting in the debugger view error.
https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/ReadOnlySpan.cs#L316

Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length);

cc @caslan

@ianhays ianhays self-assigned this Jan 11, 2018
@ianhays
Copy link
Contributor

ianhays commented Jan 11, 2018

There appear to be two distinct work items in this issue:

  • Implement ToString on Span/ReadOnlySpan and have it print out a string similar to what a collection would print out.
    • This is easy and should be done regardless of the debuggerdisplayproxy.
  • Add Debugger viewers for Span/ReadOnlySpan
    • The naive solution is to implement a DebuggerTypeProxy that uses ToArray() on the Span/ReadOnlySpan. This is currently failing due to internal bug #286592 in the VS debugger.
    • It looks like we spent a bunch of time trying to work around ToArray() failing in the DebuggerTypeProxy only to end up with a complex workaround that also requires improved VS support. Is that accurate?
    • If so, then we should go back to our original naive solution and implement a DebuggerTypeProxy using ToArray. This is the correct approach and our time would be better spent fixing the VS bug than trying to work around it in a convoluted way.
    • The main concern then becomes what happens to people debugging on a version of VS that doesn't have the debugger fix. From the above comments, it appears to cause a hard crash. We need to decide whether:
      • this is acceptable
      • this needs to be worked around on our end somehow
      • older VS versions need to be patched with the debugger fix
      • breaking older VS versions in such a manner is worth fixing the display in the next version or if we should just do nothing.

@ahsonkhan @jkotas @fujiy does that sound like an accurate representation of the current state of this issue?

@ahsonkhan
Copy link
Contributor

It looks like we spent a bunch of time trying to work around ToArray() failing in the DebuggerTypeProxy only to end up with a complex workaround that also requires improved VS support. Is that accurate?

The workaround works without any update to the VS debugger for "portable" span. The internal bug in the VS debugger is blocking "fast span" debugging only which currently uses the naive solution:
https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/SpanDebugView.cs#L17

Implement ToString on Span/ReadOnlySpan and have it print out a string similar to what a collection would print out.

I am not sure if we can implement ToString on Span. See related issue: https://github.com/dotnet/corefx/issues/21629#issuecomment-311789157

@jkotas
Copy link
Member

jkotas commented Jan 11, 2018

Implement ToString on Span/ReadOnlySpan and have it print out a string similar to what a collection would print out.

I do not think framework collections override ToString. E.g. List<T> does not have ToString over-ridden. Also, Span is more equivalent to array - and array does not override ToString either.

@ianhays
Copy link
Contributor

ianhays commented Jan 11, 2018

I am not sure if we can implement ToString on Span. See related issue: dotnet/corefx#21629 (comment)
I do not think framework collections override ToString. E.g. List does not have ToString over-ridden. Also, Span is more equivalent to array - and array does not override ToString either.

Looks like dotnet/coreclr#15745 made the error here more helpful at least, so we can leave that as-is.

The workaround works without any update to the VS debugger for "portable" span. The internal bug in the VS debugger is blocking "fast span" debugging only which currently uses the naive solution:

In that case, what else is there that needs to be done in CoreFX?

@KrzysztofCwalina
Copy link
Member Author

Looks like dotnet/coreclr#15745 made the error here more helpful at least, so we can leave that as-is.

and

I do not think framework collections override ToString. E.g. List does not have ToString over-ridden. Also, Span is more equivalent to array - and array does not override ToString either.

I don't understand why we would not override ToString. For the sake of consistency with arrays and List? Is this consistency useful? Does it add value?

On the other hand, it's close to impossible to debug code using spans. ToString override would not fix the overall issue (we still need to fix the debugger), but it would improve the situation a lot. I really worry that the current situation will seriously hamper the adoption of Span.

cc: @terrajobst, @joshfree

@jkotas
Copy link
Member

jkotas commented Jan 12, 2018

For the sake of consistency with arrays and List? Is this consistency useful? Does it add value?

I think that the main problem is that there is no decent way to implement universal formatter for lists if you do not know what the items are. E.g. what would you use for seperators? Spaces, commas, new-lines, ...?

but it would improve the situation a lot.

How would it improve the situation? To make Console.Write debugging easier?

@ianhays
Copy link
Contributor

ianhays commented Jan 12, 2018

I'm splitting this issue so the DebuggerTypeProxy discussion doesn't get lost in the ToString discussion.

Please continue discussion on ToString over in https://github.com/dotnet/corefx/issues/26295

@ianhays ianhays changed the title Span: Override ToString or add debugger viewers to Span<T> and ReadOnlySpan<T> Span: Add debugger viewers to Span<T> and ReadOnlySpan<T> Jan 12, 2018
@ianhays
Copy link
Contributor

ianhays commented Jan 29, 2018

On the latest version of the VS preview this is now fixed for the fast span using its current implementation.

image

@ianhays ianhays closed this as completed Jan 29, 2018
@felipepessoto
Copy link
Contributor

After the fix been released, should we "simplify" the slow span debugger proxy version?

https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/SpanDebugView.cs

@ahsonkhan
Copy link
Contributor

After the fix been released, should we "simplify" the slow span debugger proxy version?

Yes! Ian and I discussed this already and he plans to simplify it to just a ToArray call, similar to what Fast Span does atm.
https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/SpanDebugView.cs#L26

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.1.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Memory enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

No branches or pull requests