Skip to content

Improve debugging experience #3464

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

Merged
merged 1 commit into from
Mar 3, 2020
Merged

Improve debugging experience #3464

merged 1 commit into from
Mar 3, 2020

Conversation

sharwell
Copy link
Contributor

  • Do not optimize test framework (stepping in debugger will have full information about parameters, locals, and breakpoints)
  • Embed symbols in assemblies (eliminates the need to copy PDB files)
  • Embed sources in assemblies (eliminates the need to download sources separately)

@sharwell sharwell force-pushed the easy-debug branch 3 times, most recently from 808e0a1 to bb1abae Compare January 31, 2020 18:34
@jnm2 jnm2 requested a review from rprouse February 2, 2020 19:07
@jnm2
Copy link
Contributor

jnm2 commented Feb 2, 2020

  • Do not optimize test framework (stepping in debugger will have full information about parameters, locals, and breakpoints)

I'm concerned about this. Can you give us some idea of what performance we are giving up as well as some idea of how much time this would save you and how often this would happen? Stepping through release builds of libraries is a well-understood scenario. I'm not sure what makes NUnit a special case.

  • Embed symbols in assemblies (eliminates the need to copy PDB files)

I'm a fan. Isn't .snupkg the current recommendation and also working out of the box these days for anyone with VS2019+, with a very simple fallback (add symbol download address) for folks on older VS?

  • Embed sources in assemblies (eliminates the need to download sources separately)

I'm a fan. I do this absolutely everywhere I'm allowed.

@rprouse
Copy link
Member

rprouse commented Feb 28, 2020

@sharwell because of our history, I trust you and this looks good, but I would appreciate it if you could provide a bit more explanation or point us to the documentation for the build properties you introduced. I like the change, but I'd like to read up a bit more on it if possible.

Comment on lines -328 to -338
Task("GitLink")
.IsDependentOn("CreateImage")
.Description("Source-indexes PDBs in the images directory to the current commit")
.Does(() =>
{
var settings = new GitLink3Settings
{
BaseDir = PROJECT_DIR
};
GitLink3(GetFiles($"{CurrentImageDir}**/*.pdb"), settings);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 GitLink (and SourceLink) are used to help the debugger locate the original sources used for building an assembly, allowing for a seamless debugging experience. This pull request removes this functionality because the original sources are now embedded inside the DLL itself. It provides the best possible debugging experience (zero latency, zero configuration required) at the expense of a larger assembly (the DLL now contains the PDB and all the source files). Note that the embedded PDB uses the portable PDB format so even with the sources it's likely smaller than the "full" PDB format used in the past.

Copy link
Contributor

@jnm2 jnm2 Mar 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I did the numbers, the portable PDB+embedded source was 3.75× the size and Windows PDB+GitLink was 2.51× the size. I don't think size should be the determining factor though.

Embedded source requires VS 2017 Update 5+ for the debugger to locate the source. Do you know what version of VS is required to locate the source when the PDB is also embedded in the assembly?

I'm thinking that's far enough behind us now that it isn't a concern. Guessing that latest 2017 and 2019 both work.

Comment on lines +30 to +33
<Optimize>false</Optimize>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
<EmbedAllSources>true</EmbedAllSources>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Two of these properties (Optimize=false and DebugSymbols=true) are the default settings you always see when building with /p:Configuration=Debug.
  • DebugType=portable is the default for .NET Standard and .NET Core (and perhaps all SDK projects). DebugType=embedded uses the same PDB format as portable, but instead of creating a standalone PDB it embeds the PDB itself inside the DLL.
  • EmbedAllSources=true embeds all source files inside the PDB, which allows the debugger to locate source files without needing to look for them elsewhere. The source files end up embedded inside the DLL as a side effect of the PDB being embedded inside the DLL.

At this point, the debugging experience for end users will be very close to the experience NUnit developers have when debugging with a Debug build configuration. There are two differences:

  • I doubt Edit and Continue will work.
  • Release builds still do not define the DEBUG preprocessor symbol, so things like Debug.Assert will still not be included in the compiled code.

@sharwell
Copy link
Contributor Author

I'm concerned about this. Can you give us some idea of what performance we are giving up as well as some idea of how much time this would save you and how often this would happen?

Since NUnit doesn't have heavy computation loops, I'm not expecting a significant observable change. The most likely observations will be increases in allocations associated with methods marked async in the NUnit code base, though these should not be significant compared to the cost of the tests themselves.

The best test case would likely be a project with a very large number of very small tests.

@sharwell sharwell marked this pull request as ready for review February 28, 2020 02:04
@sharwell
Copy link
Contributor Author

I'm going to rebase this change to address the merge conflict.

@jnm2
Copy link
Contributor

jnm2 commented Feb 28, 2020

@sharwell I'm still curious what makes NUnit special, or is this coming from a blanket recommendation that all .NET libraries should consider switching to shipping debug builds? (And what is the source of the recommendation?)

@sharwell
Copy link
Contributor Author

More the idea that unit testing frameworks are part of the developer's inner loop more than part of production code. The case where I found myself wishing for better debugging was trying to step through ThrowsAsync to see how it interacted with synchronization contexts.

@jnm2
Copy link
Contributor

jnm2 commented Feb 28, 2020

That helps, thanks! This type of change would thus not be applicable to the NUnit console, engine, or VSTest adapters then, I'm thinking?

Copy link
Member

@rprouse rprouse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with this but it is redoing the work you did @jnm2. Can you give it a second review?

@rprouse rprouse requested a review from jnm2 February 29, 2020 20:14
@jnm2
Copy link
Contributor

jnm2 commented Mar 2, 2020

https://github.com/nunit/nunit/blob/master/README.md#debugger-source-stepping also needs to be updated or deleted.

@rprouse rprouse merged commit 22ea163 into nunit:master Mar 3, 2020
@rprouse rprouse added this to the 3.13 milestone Mar 3, 2020
@sharwell sharwell deleted the easy-debug branch March 3, 2020 01:21
@clairernovotny
Copy link

Just came across this and it looks like GitLink was generating invalid Source Link data:

image

It's embedding its data as lower-case instead of matching what's in the PDB so we can't correctly substitute. Moot point now but something we should be aware of.

@mikkelbu
Copy link
Member

@sharwell, @jnm2 After this change is seems that the debugger will always enter NUnit code when people are debugging their tests (no matter if "Enable Just My Code" is checked or not). It has reported in https://groups.google.com/g/nunit-discuss/c/f2yV4G_kdi8/m/ypWZfLSEDgAJ and I can also reproduce this. Do you know if it is possible to configure VS such that it does not enter NUnit code.

@jnm2
Copy link
Contributor

jnm2 commented Jan 22, 2021

I see, even with Just My Code turned off. I'm in favor of moving to the snupkg format then, since that uses the well-known nuget.org symbol server that has been built into VS for a while.

@sharwell
Copy link
Contributor Author

sharwell commented Jan 26, 2021

@mikkelbu I would suggest filing an issue through Developer Community. I can't wrap my head around the idea that someone could have Just My Code enabled and not think it's a bug, so the response that makes the most sense to me is "obviously this behavior is correct, so I'm not sure what the question is 😕 ". But based on what I know of Just My Code, the behavior would be considered a bug in that scenario.

@jnm2
Copy link
Contributor

jnm2 commented Jan 26, 2021

@sharwell Just My Code is a lifesaver when you are working with libraries that handle exceptions or when you don't want extra waits every time you debug where VS loads symbols (even when using a whitelist).

@mikkelbu
Copy link
Member

I've created the following issue in Developer Community - https://developercommunity2.visualstudio.com/t/The-debugger-enters-code-in-PackageRefer/1321266

@gfoidl
Copy link

gfoidl commented Jan 28, 2021

From .NET Just My Code:

In .NET projects, Just My Code uses symbol (.pdb) files and program optimizations to classify user and non-user code. The .NET debugger considers optimized binaries and non-loaded .pdb files to be non-user code.

So one can argue that the .NET Debugger is correct by thinking NUnit* is user-code, as pdb is there and program optimizations are off.
grafik

But I agree that this is counterintuitive and "Just my code" should be tightened to only consider "my" code.

* Moq, etc. which also embeds the pdb have the same "issue".

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.

6 participants