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

Debug Protocol: Support for getting information about a thrown exception #97

Closed
andrewcrawley opened this issue Jan 25, 2017 · 9 comments
Assignees

Comments

@andrewcrawley
Copy link
Contributor

When we receive a 'stopped' event with a reason of 'exception', we need additional information about the exception that has occurred.

Information we need in order to support VS scenarios:

  • Exception ID (e.g. "CLR/System.ArgumentException")
  • Exception Break Mode (the mode set for the exception that caused us to enter break mode, e.g. if an exception type's mode has been set to "always", the UI may differentiate between an exception that was caught vs uncaught by sending, respectively, "always" or "unhandled")
  • Description (explanatory text provided by the debug adapter, e.g. "An unhandled exception of type 'System.ArgumentNullException' occurred in ConsoleApp1.exe\nValue cannot be null.")
  • Error Code (Win32 exceptions, for instance, are identified by a code, e.g. 0xC0000005)
  • Optional details, consisting of:
    • Inner Exception (if this exception wraps another exception)
    • Object Expression (expression that can be evaluated to obtain the actual exception object, e.g. "$exception")
    • Formatted Description (Descriptive text shown in the UI that allows simple formatting, e.g. "**System.ArgumentNullException:** 'Value cannot be null\.'"
    • Type Name (Short name of the exception type, e.g. "ArgumentNullException")
    • Full Type Name (Full name of the exception type, e.g. "System.ArgumentNullException")
    • HResult (COM-style error code often associated with CLR exceptions)
    • Source (The name of the application that threw the exception)
    • Stack Trace (The stack trace at the point when the exception was thrown. May not be the current stack trace, e.g. if an exception type with an ExceptionBreakMode of 'uncaught' was caught and rethrown, and the rethrown exception was not caught)

This seems like functionality that would be broadly useful for anyone who wants to participate in the new exception experience enabled by #64.

Proposed protocol extension (implemented and verified in the VS debug adapter host):

      export interface InitializeRequestArguments {
            // ...
            /** Client supports the exceptionInfo request. */
            supportsExceptionInfoRequest?: boolean;
            // ...
      }

      /** Retrieves the details of the exception that caused the StoppedEvent to be raised. */
      export interface ExceptionInfoRequest extends Request {
            // command: 'exceptionInfo';
            arguments: ExceptionInfoArguments;
      }

      /** Arguments for 'exceptionInfo' request. */
      export interface ExceptionInfoArguments {
            /** Thread for which exception information should be retrieved. */
            threadId: number;
      }

      /** Response to 'exceptionInfo' request. */
      export interface ExceptionInfoResponse extends Response {
            body?: {
                  /** ID of the exception that was thrown. */
                  exceptionId: string;
                  /** Mode that caused the exception notification to be raised. */
                  breakMode: ExceptionBreakMode;
                  /** Descriptive text for the exception provided by the debug adapter. */
                  description?: string;
                  /** Numeric code for the exception provided by the debug adapter, if any. */
                  code?: number;
                  /** Detailed information about the exception. */
                  details?: ExceptionDetails;
            };
      }

      /** Detailed information about an exception that has occurred */
      export interface ExceptionDetails {
            /** Message contained in the exception. */
            message?: string;
            /** Property that can be evaluated in the current scope to obtain the exception object. */
            objectExpression?: string;
            /** Formatted description of the exception. */
            formattedDescription?: string;
            /** Type name of the exception object. */
            typeName?: string;
            /** Result code of the exception. */
            hresult?: number;
            /** Name of the object or application that raised the exception. */
            source?: string;
            /** Fully-qualified type name of the exception object. */
            fullTypeName?: string;
            /** Stack trace at the time the exception was thrown. */
            stackTrace?: string;
            /** Details of the exception contained by this exception, if any. */
            innerException?: ExceptionDetails;
      }

Namedrops: @jacdavis @gregg-miskelly @richardstanton @tzwlai @WardenGnaw

@DavidKarlas
Copy link
Member

I think innerException?: ExceptionDetails; should be array innerExceptions?: ExceptionDetails[]; which will be useful for System.AggregateException. I think overhead will be small in case of no or 1 inner exception.

@andrewcrawley
Copy link
Contributor Author

@DavidKarlas - Sounds reasonable. VS only supports a single inner exception per exception, but there's no reason another UI couldn't support multiple.

@gregg-miskelly
Copy link
Member

@andrewcrawley One thing I was thinking about -- will description be the same value as the text property on the stopped event? Or are they different? Other than that - LGTM.

@andrewcrawley
Copy link
Contributor Author

@gregg-miskelly - The description for the text field says Additional information. E.g. if reason is 'exception', text contains the exception name. This string is shown in the UI.. which sounds more like the proposed ExceptionDetails.typeName. There may be some overlap, but the fields in this extension all have defined purposes, as compared to the more free-form existing field.

@gregg-miskelly
Copy link
Member

@andrewcrawley I guess it depends on how the VS Code team would like to resolve microsoft/vscode#16860. If they would like to just display the text value, then I would think that text and description are going to hold the same data. If on the other hand VS Code will call this new API to populate its exception notification UI then that certainly works with me.

@weinand weinand self-assigned this Jan 30, 2017
@weinand
Copy link
Contributor

weinand commented Feb 2, 2017

  • What exactly is the numeric code of ExceptionInfoResponse? How is this surfaced in the UI? Is this useful to any other debug adapter? If not, could this become a private attribute?

  • What exactly is the 'result code of the exception' (hresult)? How is this surfaced in the UI? Is this useful to any other debug adapter? If not, could this become a private attribute?

  • What is the difference between the stackTrace attribute of ExceptionDetails and the stacktrace that is retrieved with the StackTraceRequest for a 'stopped' event?

@andrewcrawley
Copy link
Contributor Author

In general, the proposed members are mirroring VS structs. Members that you don't think would be broadly useful could certainly be included in a private extension, similar to how information on Modules is returned.

code - Some exceptions (e.g. CLR exceptions) are represented by a string (e.g. "System.ArgumentException"). Others (e.g. Win32 exceptions) are represented as a numeric code (e.g. 0xC0000026 means "An invalid exception disposition was returned by an exception handler"). When sending exception filters, these would be represented (respectively) as:

    path: [
      { names: [ "CLR" ] }
      { names: [ "System.ArgumentException" ] }
    ]

and

    path: [
      { names: [ "W32" ] }
      { names: [ "0xC0000026" ] }
    ]

When an exception occurs, VS expects you to notify it of this numeric code, if present. It might be possible for us to determine heuristically whether an exception string such as "W32/0xC0000026" represents a code or a name, but it seemed easier to just have the debug adapter specify it, since it knows which is which. This is used in the VS UI as the exception helper popup has a checkbox to allow you to easily ignore this exception in the future, which requires the numeric value (if any).

hresult - the standard CLR exceptions generally have an HResult associated with them, e.g. System.ArgumentException is 0x80070057, System.ArgumentNullException is 0x80004003, etc, which have special meanings in COM interop and such. This is used in the UI when the user clicks the "copy details" button in the exception helper popup, which puts exception info in a well-known text-based format (which includes the HResult) on the clipboard.

stackTrace - this is the stack trace at the point where the exception occured, as reported and formatted by the runtime (e.g. the CLR). This can differ from the current stack of the thread if the exception has been caught and re-thrown. It can also be useful where one exception wraps another, as it lets you see the stack for the outer (wrapping) exception as well as for any inner (wrapped) exceptions. This member is also used in the "copy details" case.

@weinand
Copy link
Contributor

weinand commented Feb 13, 2017

I've created PR #101 with the proposed changes.
Here are my deviations from the original request:

  • moved supportsExceptionInfoRequest from the InitializeRequestArguments to the Capabilities types (the client should only call the exceptionInfo request if it is supported).
  • innerException is an array of ExceptionDetails (as suggested by @DavidKarlas)
  • renamed objectExpression to evaluateName because that's the name used in the Variable type for the identical purpose.
  • I did not include the ExceptionDetails.formattedDescription, ExceptionDetails.hresult, ExceptionDetails.source, and ExceptionInfoResponse.code attributes since they appear to be not broadly useful. I suggest to introduce them as private protocol additions.

Namedrops: @jacdavis @gregg-miskelly @richardstanton @tzwlai @WardenGnaw

@andrewcrawley
Copy link
Contributor Author

@weinand - looks good to me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants