Skip to content

ICorDebugILFrame::GetIP() returns not accurate result #116143

@eterekhin

Description

@eterekhin

Hello, folks!

I encountered a situation when ICorDebugILFrame::GetIP() returns NO_MAPPING, even though there was enough data to return an approximate mapping. I investigated the issue and would like to know your thoughts on it.
I can reproduce it on Mac ARM and Win x64 on .net 8+ on the following code snippet. I call ICorDebugILFrame::GetIP() for the Method's frame (second frame in callstack when debugger is stopped in GenericMethod):

public static class Program
{
    public static void Main()
    {
        ReproClass<object>.Method();
    }
}

public class ReproClass<TModel>
{
    public static void Method()
    {
        GenericMethod<TModel>();
    }

    private static object GenericMethod<TModel>(int a = 1, int? @enum = null)
    { //please set a breakpoint here
        return new object();
    }
}

I checked that SequencePoints::m_map has enough data to return a valid result (the dump below is from the latest main):

PROLOG     start native offset: 0x0,  end native offset: 0x1c, source: STACK_EMPTY
0x0        start native offset: 0x2a, end native offset: 0x2b, source: STACK_EMPTY
0x1        start native offset: 0x2b, end native offset: 0x31, source: STACK_EMPTY
0x10       start native offset: 0x82, end native offset: 0x83, source: SOURCE_TYPE_INVALID
0x11       start native offset: 0x83, end native offset: 0x84, source: STACK_EMPTY
EPILOG     start native offset: 0x84, end native offset: 0x0,  source: NATIVE_END_OFFSET_UNKNOWN  | STACK_EMPTY
NO_MAPPING start native offset: 0x1c, end native offset: 0x2a, source: STACK_EMPTY
NO_MAPPING start native offset: 0x31, end native offset: 0x82, source: STACK_EMPTY 
0xb        start native offset: 0x79, end native offset: 0x82, source: CALL_INSTRUCTION

When I call ICorDebugILFrame::GetIP() on Method's frame it looks for an IL offset corresponding to the native offset 0x7a, it seems like the IL offset 0xb would be a valid approximate mapping. But the code treats CALL_INSTRUCTION's differently. It tries not to take them into account when native to IL offset mapping. SequencePoints::CopyAndSortSequencePoints builds ranges by including CALL_INSTRUCTION range in a previous record. As can be seen from the m_map's dump above, NO_MAPPING(0x31, 0x82) record fully overlaps the CALL_INSTRUCTION.

Here is how the mappings were generated by JIT:

*************** In genIPmappingGen()
IP mapping count : 9
IL offs PROLOG : 0x00000000 ( STACK_EMPTY )
IL offs NO_MAP : 0x0000001C ( STACK_EMPTY )
IL offs 0x0000 : 0x0000002A ( STACK_EMPTY )
IL offs 0x0001 : 0x0000002B ( STACK_EMPTY )
IL offs NO_MAP : 0x00000031 ( STACK_EMPTY )
IL offs 0x000B : 0x00000079 ( CALL_INSTRUCTION )
IL offs 0x0010 : 0x00000082
IL offs 0x0011 : 0x00000083 ( STACK_EMPTY )
IL offs EPILOG : 0x00000084 ( STACK_EMPTY )

Secondly, CALL_INSTRUCTIONs are placed at the end of the map in SequencePoints::MapSortILMap::Compare. That's why we get NO_MAP result when calling SequencePoints::MapNativeOffsetToIL with dwNativeOffset=0x7e

I am wondering if you knew why we try to ignore CALL_INSTRUCTION's? Could it be a relic of the past that it is not needed anymore?

Thank you!

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions