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
Make stack traces readable #274
Conversation
Changes the DeveloperExceptionPage to a more productive
from a less productive
|
Incorporated feedback from coreclr |
13147b2
to
5542fb7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the tests need to wait for a new build of CoreCLR to be consumed?
foreach (var attibute in method.CustomAttributes) | ||
{ | ||
// internal Attribute, match on name | ||
if (attibute.AttributeType.Name == "StackTraceHiddenAttribute") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we look up the full name? i.e. System.Diagnostics.StackTraceHiddenAttribute
. Also (string.Equals(StringComparison.Ordinal)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
operator ==(String a, String b)
is Ordinal?
operator ==(String a, String b)
=> Equals(String a, String b)
=> EqualsHelper(a, b);
Which is the same function String.Equals(String a, String b, StringComparison.Ordinal)
ends up calling: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/String.Comparison.cs#L904-L952
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we look up the full name? i.e. System.Diagnostics.StackTraceHiddenAttribute.
Can't add your own then :) Also much longer string to match on and if there was a second one its intent would likely be the same?
Will aim to get it exposed via api review; then can refer to the type directly; and everyone can use it for this purpose (and revisit the matching as can be more efficient then)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, always thought == did InvariantCulture. Nevermind then
|
||
foreach (var candidateMethod in methods) | ||
{ | ||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StateMachineAttribute
has AllowMultiple=false
. Would GetCustomAttribute
suffice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the coreclr PR they said there is nothing actually preventing multiple attributes being in the il dotnet/coreclr#14655 (comment); so is hardening it against failure.
Responding to my previous comment seeing
Yup. |
Statemachine output should change with this PR; the Task noise will need a newer CoreCLR |
e.g. with a new build of
instead of
on the diagnostics error page |
@pranavkm added fallbacks for earlier runtimes |
Would this warrant some tests now? |
return false; | ||
} | ||
else if (type == typeof(TaskAwaiter) || | ||
type == typeof(TaskAwaiter<>) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't try to align things like this in our code (harder to maintain). Could you let the editor naturally tab these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
case "HandleNonSuccessAndDebuggerNotification": | ||
case "ThrowForNonSuccess": | ||
case "ValidateEnd": | ||
case "GetResult": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a couple of places in Mvc where we manually unwrap tasks using GetAwaiter().GetResult()
. What does the stack trace look like in these cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added to test; Method(string value)
unwraps this way, output is
System.Collections.Generic.List<T>+Enumerator.MoveNextRare()
Microsoft.Extensions.Internal.StackTraceHelperTest.Iterator()+MoveNext()
string.Join(string separator, IEnumerable<string> values)
Microsoft.Extensions.Internal.StackTraceHelperTest+GenericClass<T>.GenericMethod<V>(ref V value)
Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync(int value)
Microsoft.Extensions.Internal.StackTraceHelperTest.MethodAsync<TValue>(TValue value)
Microsoft.Extensions.Internal.StackTraceHelperTest.Method(string value)
Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the .ToString() on the Exception is
at System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at Microsoft.Extensions.Internal.StackTraceHelperTest.<Iterator>d__5.MoveNext()
at System.String.Join(String separator, IEnumerable`1 values)
at Microsoft.Extensions.Internal.StackTraceHelperTest.GenericClass`1.GenericMethod[V](V& value)
at Microsoft.Extensions.Internal.StackTraceHelperTest.<MethodAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.Extensions.Internal.StackTraceHelperTest.<MethodAsync>d__3`1.MoveNext()
--- End of stack trace from previous location where exception was thrown
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.Extensions.Internal.StackTraceHelperTest.Method(String value)
at Microsoft.Extensions.Internal.StackTraceHelperTest.StackTraceHelper_ProducesReadableOutput()
a759659
to
a505819
Compare
cdf0fdb
to
1a76154
Compare
Add test and fixed the generic type resolution issues for types they highlighted e.g. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks really cool, a long time overdue!
return false; | ||
} | ||
} | ||
else if (type.FullName == "System.ThrowHelper") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type is a real type that shows up in real source code, no? Shouldn't it be left in the call stack?
I totally agree removing the "junk" that doesn't show up in anyone's source code (as in, not even in .NET's source code), but I'm less certain about legitimate method calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type is a real type that shows up in real source code, no? Shouldn't it be left in the call stack?
Its just a type to move the throw
out of the generic code to reduce instantiation code bloat. So it essentially turns the call stack back into looking like an inline throw
(as its just an implementation detail)
e.g. from
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
to
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
It will also be suppressed in future Exception.ToString()
dotnet/coreclr#14652 so its just matching ASP.NET's stack frame processing to match that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also they will also get hidden when running on latest 2.1.x as it checking for the StackTraceHiddenAttribute
so is just bringing Framework inline
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I know ThrowHelper
is cruft, but it's explicit cruft that an engineer chose to put in their code. I personally strongly dislike using such helpers because it does affect the stack trace and can potentially inhibit debugging tools from doing their jobs. That is, where did this ArgNullException really come from?
But, I don't see why the ThrowHelper needs to be special cased here. Won't https://github.com/dotnet/coreclr/pull/14652/files#diff-49215540f67fdd24553e4844693948b4R45 resolve it on the new runtime? I'm not sure I like the idea of ASP.NET Core checking for an odd implementation detail of .NET (and especially one which I 100% disagree with 😄).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the removal of ThrowHelper
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally strongly dislike using such helpers because it does affect the stack trace...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah inlining is a real problem in some cases. I just don't like working around "the system's" limitations. You want exceptions to be thrown from where the error happens, but you don't want to because then it won't inline, so you have to write helpers for it instead. So I say go fix .NET! 😄
@pranavkm assigning to you to track & merge when ready. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, thanks @benaadams !
@pranavkm can you take another look to review, then merge?
Looks like one of the test is broken on AppVeyor:
Also, could you rebase on dev? I've had very limited success using GitHub's UI to resolve merge conflicts and there's one in your PR. |
You're trolling me with these merge clashes; nothing for months then 3 come along at once... 😉 |
@benaadams I'm working on getting it sorted. By the way, I'm considering removing the use of |
Have almost rebased; want me to hold off? |
Yes, please 😄 |
Closing as superseded by #285 |
Also, add above root check to CreateFileChangeToken
Brings upstream the coreclr PRs which work on
Exception.ToString
to work on theStackFrames
for a far more productive trouble-shooting experience."Hide post exception stack frames" dotnet/coreclr#14652
"Resolve iterators and async methods" stacktrace dotnet/coreclr#14655
Microsoft.AspNetCore.Diagnostics
works onStackFrames
rather than the.ToString()
so it won't automatically fix with those PRs (except the "Show raw exception details" bit)"Hide post exception stack frames" drops
"Resolve iterators and async methods" converts
to the correct method signature
However it will need a recent build of coreclr to drop the Task/ExceptionDispatchInfo stuff as that's a new Attribute on the methods/Types.
/cc @DamianEdwards @davidfowl