Skip to content

Conversation

@sonyps5201314
Copy link
Contributor

As mentioned in the title, the source code embedded in the PDB starts with the line // PDB and source generated by ICSharpCode.Decompiler. This causes a mismatch with the version in the source project generated during DLL decompilation.

Consequently, even when breakpoints are set in the source code, the debugger hits them in a temporary file (e.g., %TEMP%\.vsdbgsrc\...\ModelProviderApiBase.cs) instead of the file in the C# project directory (e.g., Microsoft.VisualStudio.Copilot.Core\...\ModelProviderApiBase.cs). This causes significant inconvenience for debugging and code analysis.

…ven after generating PDBs and the source project.
@christophwille
Copy link
Member

Humor me for a second - why not flip the noLogo default value?

@sonyps5201314
Copy link
Contributor Author

image

As shown in the screenshot above, I found the only two places in the code that needed modification. Since both were updated to noLogo: true, there is no longer any need to retain this parameter.

@siegfriedpammer
Copy link
Member

Can you please provide a reproducer/example? If the issue is an off by one error introduced by the additional line, then the fix is something else. Thanks!

@sonyps5201314
Copy link
Contributor Author

Using the master branch from my repository: https://github.com/sonyps5201314/ILSpy

With ILSpy compiled from the following commit (i.e., the version without the fix in this PR):

SHA-1: 812f8e65388805c664c73b2db5bc0e3568825b6d

* Explicitly declare that `ShellHelper.cs` is a module that allows Pinvoke.

If you decompile "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\Extensions\Microsoft\Copilot\Microsoft.VisualStudio.Copilot.Core.dll" into a PDB and a VS Project, then open the generated project, set a breakpoint in ModelProviderApiBase.cs, start debugging, and ensure the PDB is loaded correctly, you will see that the breakpoints jump to a file located in %TEMP%\.vsdbgsrc\...\ModelProviderApiBase.cs.

However, if you use the version from the following commit (which includes the fix from this PR):

SHA-1: b3303b8a735748e942dac744a6b0105d96ae555a

* Fix the issue where breakpoints in the source code could not be hit even after generating PDBs and the source project.

The issue where breakpoints jump to temporary files no longer occurs.

Here is a demonstration video:
https://www.youtube.com/watch?v=U4BA5sr4vkA

@siegfriedpammer
Copy link
Member

Consequently, even when breakpoints are set in the source code, the debugger hits them in a temporary file (e.g., %TEMP%\.vsdbgsrc\...\ModelProviderApiBase.cs) instead of the file in the C# project directory (e.g., Microsoft.VisualStudio.Copilot.Core\...\ModelProviderApiBase.cs). This causes significant inconvenience for debugging and code analysis.

This is the expected behavior of Visual Studio, of course, the debugger will use/extract the source code that matches the generated sequence points in the PDB. This is the reason why we embed the source code in the PDB in the first place.

Also, why would you want to use a PDB containing decompiled source code? You have the source code and should actually just use the PDB generated by the C# compiler, which would give you the best debugging experience.

The PDB generator is useful when you don't have the source code, but still need to debug something. The sequence points in the generated PDB are always inferior to the ones generated by the C# compiler, because we cannot rely on nops and extra local variables in the IL to make the debugging experience flawless.

@christophwille
Copy link
Member

@sonyps5201314 you might want to look up the various open issues on pdbgen that should give you an idea about the limitations.

@siegfriedpammer
Copy link
Member

As mentioned in the title, the source code embedded in the PDB starts with the line // PDB and source generated by ICSharpCode.Decompiler. This causes a mismatch with the version in the source project generated during DLL decompilation.

This not how this feature is supposed to be used. You either generate a PDB OR you export a project and RECOMPILE that WITH DEBUG INFO. These are two distinct features and the way you are using it is NOT the expected usecase.

@siegfriedpammer
Copy link
Member

If you want such behavior you can always write your own tool on top of the decompiler engine and use that. But this is NOT A BUG and the INTENDED/EXPECTED behavior of ILSpy as well as Visual Studio.

@sonyps5201314
Copy link
Contributor Author

@siegfriedpammer

This is the expected behavior of Visual Studio, of course, the debugger will use/extract the source code that matches the generated sequence points in the PDB. This is the reason why we embed the source code in the PDB in the first place.

You seem unfamiliar with the VS debugger. If a matching source file exists locally, it will not extract the file from inside the PDB. It only extracts from the PDB when the local file is missing or does not match.

Also, why would you want to use a PDB containing decompiled source code? You have the source code and should actually just use the PDB generated by the C# compiler, which would give you the best debugging experience.

If ILSpy were perfect, I wouldn't have needed to submit so many PRs recently. currently, the VS projects decompiled by ILSpy simply cannot be recompiled into DLLs. If they could, why would I bother settling for the next best thing and using these generated PDBs?

This not how this feature is supposed to be used. You either generate a PDB OR you export a project and RECOMPILE that WITH DEBUG INFO. These are two distinct features and the way you are using it is NOT the expected usecase.

Then how do you explain that the breakpoints work correctly after I delete that comment line? Please don't rush to answer—please watch my video first.

@sonyps5201314
Copy link
Contributor Author

image

As shown in the comparison above, the only difference is this extra line at the beginning.

@siegfriedpammer
Copy link
Member

The breakpoints work because the files are identical as soon as you delete the line, but as I said, this is not the expected usecase, you don't need to export a project, if you have a PDB with embedded source code. Why so complicated? Just tell Visual Studio to load the generated PDB and set the breakpoints in the correct file!

As I said: if you want custom behavior write a custom tool, but do not force your expectations onto everybody else!

@sonyps5201314
Copy link
Contributor Author

but as I said, this is not the expected usecase, you don't need to export a project, if you have a PDB with embedded source code. Why so complicated? Just tell Visual Studio to load the generated PDB and set the breakpoints in the correct file!

If I don't export the full VS project, how can I perform a global analysis of the entire codebase? If I set breakpoints in the temporary files embedded in the PDB, those files are generated in a temporary folder. Once the files are cleared, the breakpoints become invalid. This is also not conducive to system management or long-term maintenance.

@sonyps5201314
Copy link
Contributor Author

As I said: if you want custom behavior write a custom tool, but do not force your expectations onto everybody else!

My point is simply that this change leads to a better and more consistent debugging experience for users. I am honestly confused as to why you would disagree.

It essentially just amounts to removing a single line of advertisement. People who appreciate ILSpy won't stop loving this excellent product just because that line is missing. However, the presence of that extra line causes a disjointed experience, creating a disconnect between the code used for development and the code used for debugging.
To be honest, developers don't have many choices right now. Only ILSpy and dotPeek are still being actively maintained, and ILSpy stands out as the only option that is open-source and fully customizable.

@siegfriedpammer
Copy link
Member

siegfriedpammer commented Nov 26, 2025

If you are interested in getting a better experience, I believe there are other features in Visual Studio that will help you debug the project. Would you be willing to describe your setup in a separate discussion?

Once I understand how you actually set up your project, I am sure I will be able to tell you what feature in VS to use.

Please describe:

  • How are you attaching to the running process?
  • Why can't you use the "External Sources" feature?
grafik

In this screenshot I have a project with a reference to RestSharp.dll with a PDB generated by ILSpy. Visual Studio automatically detects it and I can browse the contents of the PDB and add breakpoints in advance. Note: the breakpoints persist across debugging sessions!

@sonyps5201314
Copy link
Contributor Author

Note: the breakpoints persist across debugging sessions!

Exactly, that is precisely where the inconvenience lies. These file and directory structures only exist during the debugging session. However, as developers, we spend the vast majority of our time analyzing code in a non-debugging state—we only debug when a bug actually appears.
It was because I could no longer tolerate the inconvenience caused by the non-persistence of this directory structure that I decided to investigate the root cause. Ultimately, I discovered that the issue stems entirely from that single extra comment line.

@sonyps5201314
Copy link
Contributor Author

The reason you might not feel bothered by this right now is likely because the DLLs you are debugging contain relatively few source files.

However, when you debug a massive application like Visual Studio 2022—which involves hundreds of DLLs, each containing thousands of C# source files—and rely on PDB-embedded sources, you will fully realize how chaotic and disorganized that file management becomes. It is truly disorienting/overwhelming.

Furthermore, Visual Studio's symbol search capabilities are significantly weaker during debugging than during normal development. Symbols within these temporary files—which are loaded only during debugging—are not indexed, so you cannot find them using Ctrl+T. This actually makes the debugging experience inferior to simply using DnSpy.

@sonyps5201314
Copy link
Contributor Author

sonyps5201314 commented Dec 14, 2025

I discovered that DecompilationOptions.EscapeInvalidIdentifiers is another cause of the mismatch. Furthermore, the call to decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); in WholeProjectDecompiler.CreateDecompiler is not controlled by DecompilationOptions.EscapeInvalidIdentifiers.

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

Successfully merging this pull request may close these issues.

3 participants