Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debugging a dll being loaded by another application. #91

Open
ppebb opened this issue May 9, 2022 · 18 comments
Open

Debugging a dll being loaded by another application. #91

ppebb opened this issue May 9, 2022 · 18 comments

Comments

@ppebb
Copy link

ppebb commented May 9, 2022

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
Copy link
Member

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

@ppebb
Copy link
Author

ppebb commented May 9, 2022

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
Copy link
Member

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):

  • by default JMC is disabled, you could enable it by set just-my-code 1 (See help for set command for more info).
  • provide PDB file only for DLL you want debug.

@ppebb
Copy link
Author

ppebb commented May 9, 2022

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
Copy link
Member

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
Copy link
Author

ppebb commented May 9, 2022

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
Copy link
Member

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
Copy link
Author

ppebb commented May 12, 2022

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
Copy link
Member

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).

var data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry);

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
Copy link
Author

ppebb commented Jul 20, 2022

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
Copy link
Member

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

As I already explained in #91 (comment)
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
Copy link
Author

ppebb commented Jul 20, 2022

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
Copy link
Member

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
Copy link
Author

ppebb commented Jul 27, 2022

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
Copy link
Contributor

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
Copy link
Author

ppebb commented Aug 2, 2022

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
Copy link

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
Copy link
Member

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?

granitrocky pushed a commit to OrdinaryGizmos/godot that referenced this issue Jan 25, 2024
This change replaces `LoadFromStream` with a direct
`LoadFromAssemblyPath` to allow the C# module to interface with
`netcoredbg`

This solution comes from this issue:
Samsung/netcoredbg#91 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants