Repository: dotnet/diagnostics

Issue #50: [Linux] How to view static fields of generics in SOS?
Created: 2018-07-30T19:09:12Z
@ayuckhulk commented on Mon May 29 2017
Consider the following code:
class Program
{
	public static void Main()
	{
        S1<int> s1 = new S1<int>();
        s1.bar = 2;
	}

    public class S1<T>
    {
        public static int fooInt = 99;
        public T bar;
    }
}


I'd like to view the value of s1.fooInt. However, SOS plugin for lldb does not show its value or native address:
(lldb) sos ClrStack -i s1



Dumping managed stack and managed variables using ICorDebug.
=============================================================================
Child SP         IP               Call Site
00007FFFFFFFC340 00007fff7d7408dd [DEFAULT] Void Program.Main() (/home/i.kulaychuk/work/overlay/Program.dll)

PARAMETERS: (none)

LOCALS:
  + Program+S1`1&lt;int&gt; s1 @ 0x7fff580337d0
     |- < unknown type > fooInt
     |- int bar  = 0

00007FFFFFFFC360 00007ffff62a1f87 [NativeStackFrame]
Stack walk complete.
=============================================================================

(lldb) sos DumpObj 0x00007fff580337d0
Name:        Program+S1`1[[System.Int32, System.Private.CoreLib]]
MethodTable: 00007fff7c196b00
EEClass:     00007fff7d792808
Size:        24(0x18) bytes
File:        /home/i.kulaychuk/work/overlay/Program.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007fff7d471740  4000002        8         System.Int32  1 instance                0 bar
00007fff7d471740  4000001       10         System.Int32  1   static dynamic statics NYI                 fooInt
(lldb) 

The command sos ClrStack -i s1 uses ICorDebug API ICorDebugType::GetStaticFieldValue to show static fields  (see https://github.com/dotnet/coreclr/blob/master/src/ToolBox/SOS/Strike/strike.cpp#L11734). But for some reason it does not work for dynamic statics.
Is it a bug? Is there any other way to get dynamic static fields?
CC @jkotas, @janvorli, @mikem8361, @Dmitri-Botcharnikov

@jkotas commented on Mon May 29 2017
I do not think that there is anything fundamental why this cannot work. It is just unimplemented.

@ayuckhulk commented on Mon May 29 2017
@jkotas The API ICorDebugType::GetStaticFieldValue actually returns CORDBG_E_STATIC_VAR_NOT_AVAILABLE. However, C# debugger (clrdbg) is always able to show static variable value. Does it force the runtime to initialize the dynamic static field of the class? Is it by design that ICorDebugType::GetStaticFieldValue does not initialize the static field of the class? Is there another API to force the initialization?
Sorry, I got confused with ICorDebur*, IDacDbiInterface* etc. APIs 😄

@0xd4d commented on Mon May 29 2017
You'll need to create an instance of the generic class, see this comment in rsclass.cpp:
// The debugger may chose to func-eval the creation of an instance of this type and try again.

@ayuckhulk commented on Mon May 29 2017
@0xd4d The instance of the class is already created (when I put a breakpoint on line s1.bar = 2;) but ICorDebugType::GetStaticFieldValue still returns CORDBG_E_STATIC_VAR_NOT_AVAILABLE. What API should I use to func-eval the creation of an instance of this type?

@ayuckhulk commented on Tue May 30 2017
@jkotas Is this https://blogs.msdn.microsoft.com/davidnotario/2005/02/08/jit-compiler-and-type-constructors-cctors/ the reason why static fields are not available? Then I guess I need to call the .cctor method using ICorDebugEval API.

@jkotas commented on Tue May 30 2017
The short answer is that many things in the runtime are done lazily, including allocation of storage for static fields.

Then I guess I need to call the .cctor method using ICorDebugEval API.

If you want to mimic the Visual Studio experience, then yes - you would need to run the .cctor. It is not just to allocate the storage for the fields, it is also to actually run the .cctor if it has not run already.

@ayuckhulk commented on Tue May 30 2017
@jkotas I tried to use ICorDebugEval for .cctor from SOS but it failed with E_NOTIMPL. I guess that's because SOS uses V4 debug API and in order for ICorDebugEval to work the API should be V2.
Is there any working API to actually run .cctor and other methods (like getters for properties)? Or am I missing something? 😄

@jkotas commented on Tue May 30 2017
It is because of funceval requires infrastructure that was never implemented for the low-level SOS debugger. I do not think it has anything to do with V2 vs. V4 debug APIs.

@ayuckhulk commented on Tue May 30 2017
@jkotas Could you please point me to the things that should be implemented to get funceval working in SOS?

@jkotas commented on Tue May 30 2017
The funceval is pretty complex. You can an idea about what is involved from https://blogs.msdn.microsoft.com/jmstall/tag/funceval/ .

@ayuckhulk commented on Tue May 30 2017
Thank you!

Duplicate issue #280: SOS!do fails to display static fields value for types defined in System.Private.CoreLib.dll 
Created: 2019-05-22T23:47:22Z
Moved from coreclr #https://github.com/dotnet/coreclr/issues/22248. Submitted by @chrisnas.
Repro:

create a .NET Core console application
use procdump -ma to generate a memory dump
open the dump in WinDBG and load sos.dll (.loadby sos coreclr)
enumerate type instances (!dumpheap -stat) and look for CultureInfo instances
dump a CultureInfo instance: each static field has NOINIT as value

00007fffc61fadf8  4000fa5      a30 ...ation.CultureInfo  0   shared           static s_userDefaultCulture
                                 >> Domain:Value  000001a5824a8010:NotInit  <<
00007fffc61fadf8  4000fa6      a38 ...ation.CultureInfo  0   shared           static s_userDefaultUICulture
                                 >> Domain:Value  000001a5824a8010:NotInit  <<
00007fffc61fadf8  4000fa7      a40 ...ation.CultureInfo  0   shared           static s_InvariantCultureInfo
                                 >> Domain:Value  000001a5824a8010:NotInit  <<


The same behavior occurs with string.Empty
Investigation:
The !do command ends up calling a few functions
Strike.cs|DumpObj
-> Strike.cs|PrintObj
-> Util.cs|DisplayFields
-> Util.cs|DisplaySharedStatic
This last function iterates on appdomains and calls ClrDataAccess::GetDomainLocalModuleDataFromAppDomain with a moduleID as an int (which is 32 bits even in x64)
This moduleID was an ULONG64 in DisplaySharedStatic but cast as an int
In .NET Framework, the moduleID seems to be an "index" like 1, 2 or 3 but in .NET Core, it looks like an address. The test on moduleID in GetDomainLocalModuleDataFromAppDomain will always fail:
if (appDomainAddr == 0 || moduleID < 0 || pLocalModuleData == NULL)
return E_INVALIDARG;
I have tried to change the interface to pass a CLRDATA_ADDRESS instead of an int
(this change impacts src/debug/daccess/dacimpl.h, src/debug/daccess/request.cpp and src/pal/prebuilt/inc/sospriv.h)
but it is not really better:
The code below the test does not make sense to me:
ModuleIndex index = Module::IDToIndex(moduleID);
static ModuleIndex IDToIndex(SIZE_T ModuleID)
{
ModuleIndex index(ModuleID >> 1);
return index;
}
Why dividing an "address" by 2 to get an "index"?
And the next line crashes when trying to GetModuleSlot
DomainLocalModule* pLocalModule = pAppDomain->GetDomainLocalBlock()->GetModuleSlot(index);
Note: this is the same problem as microsoft/clrmd#214 with ClrMD.

Closing comment (2019-05-22T23:47:47Z): Possibly related to #50