none
File name and line number in StackSnapshotCallback (unmanaged API for profiling) RRS feed

  • Question

  • I'm trying to make my profiler output how a profiled method was called, from a FunctionEnter hook.  With the DoStackSnapshot and StackSnapshotCallback combo, getting the class and method names in the call stack is easy enough.  However, getting the file names or the line numbers seems to be quite hard.

    The only information I found is from a forum thread and related Stack Overflow question:

    • https://social.msdn.microsoft.com/Forums/en-US/ee47a207-5ee6-4e24-a89f-e2134a8eb7c8/how-do-i-resolve-managed-symbol-names-using-the-unmanged-profiling-api-inprocess?forum=netfxtoolsdev
    • https://stackoverflow.com/questions/198754/how-do-you-map-a-native-to-il-instruction-pointer-in-process

    The following steps are suggested:

    1. Get native code offset from the instruction pointer with `GetCodeInfo2`.
    2. Convert it to an IL offset with `GetILToNativeMapping`.
    3. Get symbols from that IL offset.

    While steps (1) and (2) are complicated but clear enough, step (3) is a mystery to me. My questions are thus:

    • Is a profiler supposed to have access to file names and line numbers for given stack frames?
    • If yes, are those steps a good way of obtaining the file name or line number for a given stack frame?
    • If yes, how to do step (3)?
    • Is there a better way?
    Tuesday, September 4, 2018 3:17 PM

Answers

    • Is a profiler supposed to have access to file names and line numbers for given stack frames?

    Not so much that it can't, but that you might want to defer the lookup until a post processing step or move it into a monitoring process so as to reduce the performance impact.

    • If yes, are those steps a good way of obtaining the file name or line number for a given stack frame?

    ... sure ... the profiler should get the module information, metadata token and il offset. You can then use the symbol store api to translate those things into the source location (potentially at a later time or in another process).

    • If yes, how to do step (3)?
    1. Get an instance of ISymUnmanagedBinder (CoCreateInstance CLSID_CorSymBinder).
    2. Use ISymUnmanagedBinder::GetReaderForFile to get an ISymUnmanagedReader
    3. Use ISymUnmanagedReader::GetMethod to get the ISymUnmanagedMethod
    4. Use ISymUnmanagedMethod::GetSequencePoints to get the IL <--> source mappings for the method.

    GetReaferForFile will require an IMetaDataImport instance for the module, if you are not doing this in the profiler then you can still get the instance using the following process.

    1. Get an instance of ICLRMetaHost by CoCreateInstance CLSID_CLRMetaHost
    2. Use ICLRMetaHost::GetRuntime (or other methods on that interface) to get an instance of ICLRRuntimeInfo for the desired runtime version.
    3. Use ICLRRuntimeInfo::GetInterface to get the IMetaDataDispenser
    4. Use IMetaDataDispenser::OpenScope to get the IMetaDataImport


    Wednesday, September 5, 2018 11:29 AM

All replies

    • Is a profiler supposed to have access to file names and line numbers for given stack frames?

    Not so much that it can't, but that you might want to defer the lookup until a post processing step or move it into a monitoring process so as to reduce the performance impact.

    • If yes, are those steps a good way of obtaining the file name or line number for a given stack frame?

    ... sure ... the profiler should get the module information, metadata token and il offset. You can then use the symbol store api to translate those things into the source location (potentially at a later time or in another process).

    • If yes, how to do step (3)?
    1. Get an instance of ISymUnmanagedBinder (CoCreateInstance CLSID_CorSymBinder).
    2. Use ISymUnmanagedBinder::GetReaderForFile to get an ISymUnmanagedReader
    3. Use ISymUnmanagedReader::GetMethod to get the ISymUnmanagedMethod
    4. Use ISymUnmanagedMethod::GetSequencePoints to get the IL <--> source mappings for the method.

    GetReaferForFile will require an IMetaDataImport instance for the module, if you are not doing this in the profiler then you can still get the instance using the following process.

    1. Get an instance of ICLRMetaHost by CoCreateInstance CLSID_CLRMetaHost
    2. Use ICLRMetaHost::GetRuntime (or other methods on that interface) to get an instance of ICLRRuntimeInfo for the desired runtime version.
    3. Use ICLRRuntimeInfo::GetInterface to get the IMetaDataDispenser
    4. Use IMetaDataDispenser::OpenScope to get the IMetaDataImport


    Wednesday, September 5, 2018 11:29 AM
  • Thank you for your reply!  This look very promising but, unfortunately, I haven't been able to make it work for now.

    Despite corguids.lib being in the library path, either from "C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\Lib\um\x64" or a recent .NET Core build, I get the linker error: "unresolved external symbol IID_ISymUnmanagedBinder" (VS 2017).  Same for CLSID_CorSymBinder, CLSID_CorSymBinder_deprecated and CLSID_CorSymBinder_SxS.

    I tried to inline IID_ISymUnmanagedBinder from the value I got from .NET Core, but apparently that's not the right one because I then get REGDB_E_CLASSNOTREG at runtime.  With inlined CorSymBinder CLSIDs instead, I get E_NOINTERFACE.

    What I attempted in the profiler is the following (here with ISymUnmanagedBinder but this would also failed for CorSymBinder as explained above):

        IDispatch *dispatch;
        IID iid = {0xaa544d42, 0x28cb, 0x11d3, {0xbd, 0x22, 0x00, 0x00, 0xf8, 0x08, 0x49, 0xbd}};
        HRESULT result = CoInitialize(0);
        result = CoCreateInstance(
            __uuidof(IID_ISymUnmanagedBinder),
            nullptr,
            CLSCTX_INPROC_SERVER,
            iid,
            (void **) &dispatch);

    I'll keep looking for a solution so that I can confirm your suggestion worked for me, but any guidance would be welcome.

    By the way, ideally, I'd like it to work on both .NET Framework 4 and .NET Core 2.1 but just .NET Core would already be nice.

    Friday, September 7, 2018 6:05 PM
  • Did you add corguids.lib to the "Additional Dependencies" under the linker options?

    I typically do this lookup from a separate (managed) application using com interop, the CLSID I use is `0A29FF9E-7F9C-4437-8B11-F424491E3931` which is CLSID_CorSymBinder_SxS.

    I haven't tried any of this with .NET Core, but if it was going to be an issue, I would expect the problem to arise while getting the ISymUnmanagedReader or IMetaDataImport.

    Saturday, September 8, 2018 3:34 AM
  • Indeed, adding corguids.lib to the additional dependencies fixed the linker error.  Thanks!

    Also, I think I was using CoCreateInstance incorrectly.  Here is the invocation that works for me:

    ISymUnmanagedBinder *binder;
    HRESULT result = CoInitialize(0);
    result = CoCreateInstance(
        CLSID_CorSymBinder_SxS,
        0,
        CLSCTX_INPROC_SERVER,
        IID_ISymUnmanagedBinder,
        (void **) &binder
    );

    However, I haven't managed to get a symbol reader yet:

    ISymUnmanagedReader *reader;
    const WCHAR *file_name = L"foo.pdb";
    const WCHAR *search_path = L"E:\\foo\\bin\\Debug\\netcoreapp2.1";
    result = binder->GetReaderForFile((IUnknown *) meta, file_name, search_path, &reader);

    I get 806d0012, which I think means "E_PDB_FORMAT", but I don't really understand why.  The PDB starts with:

    00000000: 4253 4a42 0100 0100 0000 0000 0c00 0000  BSJB............
    00000010: 5044 4220 7631 2e30 0000 0000 0000 0600  PDB v1.0........
    00000020: 7c00 0000 6000 0000 2350 6462 0000 0000  |...`...#Pdb....
    00000030: dc00 0000 0401 0000 237e 0000 e001 0000  ........#~......
    This looks fine, no?

    Monday, September 10, 2018 12:16 PM
  • The reader retrieved in this way would be targeting .NET Framework, if .NET Core uses a different format then the E_PDB_FORMAT result would make sense. At this point, I would suggesting asking the guys at https://github.com/dotnet/coreclr since they seem to provide their own implementation.
    Monday, September 10, 2018 9:45 PM