Samsung / netcoredbg

NetCoreDbg is a managed code debugger with MI interface for CoreCLR.
MIT License
780 stars 101 forks source link

Debugging a dll being loaded by another application. #91

Open ppebb opened 2 years ago

ppebb commented 2 years ago

I have a dll, which is loaded by another application at runtime. How would I go about debugging my dll while it's being run from the other application?

viewizard commented 2 years ago

Could you please clarify, another managed application create new child process with your dll?

ppebb commented 2 years ago

It doesn't create a child process (not that I know of), it's another dotnet application that loads the dll at runtime and runs code from it.

viewizard commented 2 years ago

You could start debug session for application that loads the dll at runtime and use Just My Code feature (https://docs.microsoft.com/en-us/visualstudio/debugger/just-my-code?view=vs-2022#BKMK__NET_Framework_Just_My_Code):

ppebb commented 2 years ago

Is there a way to explicitly pass it certain PDBs? And also, I'm attempting to use this with a tool called vimspector, and it only uses DAP. Is there a way to enable just-my-code with DAP or some command argument?

viewizard commented 2 years ago

Is there a way to explicitly pass it certain PDBs?

No, you can't do this, since this is process-related feature. We can't mark some executable managed code with JMC in runtime and some just ignore, runtime will not understand this correctly and, for example, will be not able to setup stepper in proper way or detect user-unhandled exception.

And also, I'm attempting to use this with a tool called vimspector, and it only uses DAP. Is there a way to enable just-my-code with DAP or some command argument?

As I know, vimspector support netcoredbg usage with VSCode protocol, we support JMC feature setup for VSCode protocol too with justMyCode option in launch command. I don't know how you could setup your application launch in vimspector in order to provide justMyCode option, probably, you better ask vimspector developers.

ppebb commented 2 years ago

Where should the pdb of the dll I want to debug be? I've put it in the working directory, same directory as the dll I'm actually running and it doesn't seem to be working (I am quite sure I have justMyCode enabled).

For context, the dll being loaded by the application is one that's zipped alongside other files, which is then loaded. Everything is in completely different directories from my actual code so I'm not sure where the pdb should go.

viewizard commented 2 years ago

PDB file should be in same directory as DLL. In case CLI you could check output into terminal from debugger during DLL load (this output have info about is DLL loaded with PDB/symbols or not). Note, VSCode protocol also provide information about symbols (PDB) load, but I don't know how and where you could see this log in vimspector.

ppebb commented 2 years ago

Looking at the logs, I believe symbols aren't being loaded properly.

library loaded: tConfigWrapper
no symbols loaded, base address: 0x7f464210d000, size: 11776(0x2e00)

I'll refer to the dll I'm running as the parent dll and the dll being loaded by the parent that I actually want to debug as the child dll just for clarity.

It seems no symbols are being found, which is strange since I put the pdb of the child dll both in the working directory of the parent dll and next to the child dll. In the event that you meant that it goes next to the child dll, there's a small issue where the child dll is within a zip file when it's loaded, so I don't think a pdb can be gotten from there.

I'm also not sure why there's no path listed or why it's not a .dll, and I'm wondering if the working directory is maybe changing? I doubt the debugger directory should be changing though.

Some other dlls (not the parent or child) are in release mode, and I'm getting a message that Using Just My Code with Release builds using compiler optimizations results in a degraded debugging experience (e.g. breakpoints will not be hit). Should that cause any issues with debugging the child dll?

viewizard commented 2 years ago

Note, DLL file on disk should be DLL, since debugger will open this file with PE Reader and read CoreView section in order to get PDB file name (DLL have this data stored inside during build). https://github.com/Samsung/netcoredbg/blob/a8bd3b95328f19dfe5519973b8176f40d3b4f509/src/managed/SymbolReader.cs#L1147 https://docs.microsoft.com/en-us/dotnet/api/system.reflection.portableexecutable.pereader?view=net-6.0 https://docs.microsoft.com/en-us/dotnet/api/system.reflection.portableexecutable.codeviewdebugdirectorydata?view=net-6.0

If you have DLL file archived on disk - this will not work for sure.

Some other dlls (not the parent or child) are in release mode, and I'm getting a message that Using Just My Code with Release builds using compiler optimizations results in a degraded debugging experience (e.g. breakpoints will not be hit). Should that cause any issues with debugging the child dll?

No.

ppebb commented 2 years ago

I've come back to this recently, and I've found something that "works".

By default, the parent loads my dll and pdb using AssemblyLoadContext.LoadFromStream(Stream, Stream) (AssemblyLoadContext docs), where it passes in all of the bytes of the dll and pdb. This prevents symbols from being loaded in netcoredbg even though the pdb is loaded into the runtime. In visual studio, symbols are still loaded from the pdb even if it was fetched with LoadFromStream().

image The dlls+pdbs loaded this way show up as (dynamic).

Changing to the parent loading assemblies with AssemblyLoadContext.LoadFromAssemblyPath(String) allows symbols to load just fine in netcoredbg though.

Is there any reason why netcoredbg cannot use the pdb when loaded using LoadFromStream()?

viewizard commented 2 years ago

Is there any reason why netcoredbg cannot use the pdb when loaded using LoadFromStream()?

As I already explained in https://github.com/Samsung/netcoredbg/issues/91#issuecomment-1124715331 Note, DLL file on disk should be DLL, since debugger will open this file with PE Reader and read CoreView section in order to get PDB file name (DLL have this data stored inside during build). In case you don't provide DLL file name to runtime, runtime don't provide DLL name to debugger by debug API, debugger have no idea what DLL file it should analyze for PDB file name.

ppebb commented 2 years ago

How difficult would it be to implement loading the pdb when using LoadFromStream() where it doesn't have the path? It seems like it should be possible given that other debuggers exist which can do this, but I don't know much about debuggers so I'm not sure how much effort it would be to get that working.

viewizard commented 2 years ago

This require some time for investigation, but for now we don't have it. I have no idea for now how this could be implemented.

ppebb commented 2 years ago

This probably isn't a great idea but a temporary solution could be some config file where you can define where some assembly loaded from stream has a pdb file.

alpencolt commented 2 years ago

When assembly loaded from stream it doesn't have file name and many internal logic based on it will not work. I believe It's possible to implement debugging if assembly and PDB loaded by LoadFromStream (System.IO.Stream assembly, System.IO.Stream? assemblySymbols);. But no sure that will work if assembly loaded from stream and pdb located on file system.

For now for debugging you can load dlls from file system and for release build from streams:

#if DEBUG
  var dll = AssemblyLoadContext.LoadFromAssemblyPath(path);
#else
  var dll = AssemblyLoadContext.LoadFromStream(dllStream);
#endif
ppebb commented 2 years ago

I believe It's possible to implement debugging if assembly and PDB loaded by LoadFromStream (System.IO.Stream assembly, System.IO.Stream? assemblySymbols);

Loading both from stream is what's currently happening so that's good.

For now for debugging you can load dlls from file system and for release build from streams

It's not my project that's loading the dlls, so I'm not able to make that change, sadly.

rogerfar commented 1 year ago

General question: if I attach the debugger to the host process, then load a separate DLL through AssemblyLoadContext, put a breakpoint in there, does this halt the complete host app? Or would it be isolated to the assembly context?

viewizard commented 1 year ago

Not sure our debugger work with Context related feature at all, at least we don't have "Context" related stuff implemented (and never tested this case). BTW, I am not sure is this is same "Context" or not, but as I could see Debug API still don't have implemented related interface: https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/debugging/icordebugcontext-interface https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/debugging/icordebugchain-getcontext-method is MS C# debugger work with Context related code?