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

[API Proposal]: [StackTraceHidden(StackTraceVisibility.HideAllChildren)] #112130

Open
TonyValenti opened this issue Feb 4, 2025 · 4 comments
Open
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Diagnostics untriaged New issue has not been triaged by the area owner

Comments

@TonyValenti
Copy link

Background and motivation

.NET stack traces are an important element that helps developers diagnose bugs and exceptions. However, diagnosing the actual location of the bug often involves developers skipping over unnecessary entries that should be hidden from the stack trace.

To alleviate this issue, the attribute [StackTraceHidden()] can be used to hide a single current method from the stack trace. This is often used for throw helpers but is an incomplete solution due to there being no ability to prune an entire branch of code.

Take the following program as an example:

DoTest();

static void DoTest() {
    //This will always throw
    using var FS = System.IO.File.OpenRead($@"\\0.0.0.0\C$\Testasdf.txt");
}

When this crashes, the stack trace will show:

[0] Unhandled exception. System.IO.FileNotFoundException: Could not find file '\\CODE2024\C$\Testasdf.txt'.
[1] File name: '\\CODE2024\C$\Testasdf.txt'
[2]    at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
[3]    at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
[4]    at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
[5]    at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
[6]    at System.IO.File.OpenRead(String path)
[7]    at Program.<<Main>$>g__DoTest|0_0() in C:\Users\Administrator\source\repos\ConsoleApp16\ConsoleApp16\Program.cs:line 7
[8]    at Program.<Main>$(String[] args) in C:\Users\Administrator\source\repos\ConsoleApp16\ConsoleApp16\Program.cs:line 3

The important part of the stack trace really begins at line 6 as lines 2-5 are essentially internal calls.

.NET should expose a way of easily hiding internal stack trace trees.

API Proposal

namespace System.Diagnostics;

public enum StackTraceVisibility {
   Default,
   Hidden,
   HideAllChildren,
}

public partial class StackTraceHidden {
  public StackTraceHidden(StackTraceVisibility Visibility) { }
}

API Usage

[StackTraceHidden(StackTraceVisibility.HideAllChildren)]
public void MyMethod() { }

Alternative Designs

Developers could catch and re-throw exceptions to try to clean up stack traces.

Risks

It is possible that someone might actually want all the internal details of exactly where an internal call failed. A method should exist that can retrieve them.

@TonyValenti TonyValenti added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Feb 4, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Feb 4, 2025
Copy link
Contributor

Tagging subscribers to this area: @tommcdon
See info in area-owners.md if you want to be subscribed.

@teo-tsirpanis
Copy link
Contributor

The important part of the stack trace really begins at line 6 as lines 2-5 are essentially internal calls.

Internal calls are in the overwhelming majority of cases very valuable in diagnosing the cause of an exception. Trivial methods like throw helpers can already be skipped from a stack trace, but a way to hide all subsequent stack frames is a step too far and I cannot find a valid use case for it.

@KalleOlaviNiemitalo
Copy link

In your example program, would you decorate the static void DoTest() method with [StackTraceHidden(StackTraceVisibility.HideAllChildren)]? Or would you want the attribute to be added to the File.OpenRead method?

The desired effects of the attribute don't seem trivial to define. The stack trace output by the following program currently shows only C.M3() and C.M2(), not C.Main(). If C.Main() were decorated with [StackTraceHidden(StackTraceVisibility.HideAllChildren)], I think that should not cause the stack trace to become entirely empty.

using System;

class C
{
    //[StackTraceHidden(StackTraceVisibility.HideAllChildren)]
    static void Main()
    {
        M2();
    }

    static void M2()
    {
        try
        {
            M3();
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }

    static void M3()
    {
        throw new Exception("what?");
    }
}

@f2bo
Copy link

f2bo commented Feb 4, 2025

You could always define an extension method to hide these entries. For example, this extensions method will only print lines associated with a source file.

try
{
    DoTest();
}
catch (Exception ex)
{
    Console.WriteLine(ex.UserStackTrace());
}

static class ExceptionExtensions
{
    public static string UserStackTrace(this Exception exception)
    {
        int offset = 0;
        ReadOnlySpan<char> stackTrace = exception.StackTrace;
        foreach (var line in stackTrace.EnumerateLines())
        {
            if (line.IndexOf(" in ") > 0)
            {
                break;
            }

            offset += line.Length + Environment.NewLine.Length;
        }

        return stackTrace[offset..].ToString();
    }
}

Note: I've only thought about this for about 5 minutes so the test for relevant lines might need to be a bit more sophisticated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Diagnostics untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

4 participants