dotnet / diagnostics

This repository contains the source code for various .NET Core runtime diagnostic tools and documents.
MIT License
1.18k stars 356 forks source link

Improve the usability of SOS around open/closed generic types #1573

Open tmds opened 3 years ago

tmds commented 3 years ago

I can't figure out how to see the contents of a Span<byte> using lldb and sos.

For example:

using System;

namespace console
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            Span<byte> span1 = new byte[] { 1, 2, 3 };
            Span<byte> span2 = stackalloc byte[] { 4, 5, 6 };
            Foo(span1, span2);
        }

        public static void Foo(Span<byte> span1, Span<byte> span2)
        {
            Console.WriteLine(span1.Length + span2.Length);
        }
    }
}
$ lldb bin/Debug/net5.0/console
(lldb) target create "bin/Debug/net5.0/console"
Current executable set to '/tmp/console/bin/Debug/net5.0/console' (x86_64).
(lldb) bpmd Program.cs:16
(lldb) process launch
Process 46873 launched: '/tmp/console/bin/Debug/net5.0/console' (x86_64)
1 location added to breakpoint 1
JITTED console!console.Program.Foo(System.Span`1<Byte>, System.Span`1<Byte>)
Setting breakpoint: breakpoint set --address 0x00007FFF7DD16280 [console.Program.Foo(System.Span`1<Byte>, System.Span`1<Byte>)]
Process 46873 stopped
* thread #1, name = 'console', stop reason = breakpoint 3.1
    frame #0: 0x00007fff7dd16280
->  0x7fff7dd16280: pushq  %rbp
    0x7fff7dd16281: subq   $0x30, %rsp
    0x7fff7dd16285: leaq   0x30(%rsp), %rbp
    0x7fff7dd1628a: movq   %rdi, -0x10(%rbp)
(lldb) clrstack -a
OS Thread Id: 0xb719 (1)
        Child SP               IP Call Site
00007FFFFFFFC6B8 00007FFF7DD16280 console.Program.Foo(System.Span`1<Byte>, System.Span`1<Byte>) [/tmp/console/Program.cs @ 15]
    PARAMETERS:
        span1 (<CLR reg>) = 0x00007fff5000b728
        span2 (<CLR reg>) = 0x00007fffffffc6c0

00007FFFFFFFC6C0 00007FFF7DD15E31 console.Program.Main(System.String[]) [/tmp/console/Program.cs @ 11]
    PARAMETERS:
        args (0x00007FFFFFFFC748) = 0x00007fff5000a358
    LOCALS:
        0x00007FFFFFFFC738 = 0x00007fff5000b728
        0x00007FFFFFFFC728 = 0x00007fffffffc6c0
        0x00007FFFFFFFC718 = 0x00007fffffffc6c0

(lldb) 

What commands can I use to print the content of these Spans?

cc @mikem8361

mikem8361 commented 3 years ago

I’ve never done this before either, but did you try running dumpobj on the span? And then I assume span has a field with pointer to the memory buffer that you can dump.

hoyosjs commented 3 years ago

There's a part of this that I don't quite know how to do cleanly from SOS. Since Span is a struct it would probably be something like:

  1. Get the method table for System.Span.
    • Usually I get the table using name2ee (e.g. name2ee System.Private.CoreLib.dll System.Span1`), but I couldn't get it to resolve any generic classes. Is this expected @mikem8361?
    • For now I had to find the token using ILDasm, get the token and then use Token2EE (i.e sos Token2EE System.Private.CoreLib.dll 0200015E).
  2. Get the address of the span, as you showed with clrstack -a
  3. Use dumpvc <MethodTable> <Address>
  4. There's a pointer to either native memory or object there as well as the length.

I tried going through these steps for your sample and I got some interesting (read invalid) results, I believe because I was using the method table for the uninstantiated Span. I'll try to look tomorrow.

tmds commented 3 years ago

@hoyosjs I was trying the same steps, but got stuck at 1 when name2ee doesn't return the method table.

hoyosjs commented 3 years ago

Yeah, I was poking around but I can't find a good way to translate an open MT to the closed versions of it. After talking to @davmason we believe there's a painful gap here and I think it'd be worth investing some effort to be able to get both these functionalities (MT translation from open generics to bound/closed ones, and better name2ee support for generics).

tmds commented 3 years ago

@hoyosjs do you want to create a new issue to implement those features, or do you want to re-purpose this one?