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

Support parsing Go 1.17 stack traces #62

Closed

Conversation

nvanbenschoten
Copy link
Contributor

Fixes #61.

In Go 1.17, the format of function arguments printed in stack traces was improved. This is mentioned in the release notes: https://tip.golang.org/doc/go1.17#compiler

The format of stack traces from the runtime (printed when an uncaught
panic occurs, or when runtime.Stack is called) is improved. Previously,
the function arguments were printed as hexadecimal words based on the
memory layout. Now each argument in the source code is printed
separately, separated by commas. Aggregate-typed (struct, array, string,
slice, interface, and complex) arguments are delimited by curly braces.
A caveat is that the value of an argument that only lives in a register
and is not stored to memory may be inaccurate. Function return values
(which were usually inaccurate) are no longer printed.

This runtime change was primarily performed in https://go-review.googlesource.com/c/go/+/304470. The description on that CL presents the change in greater detail.

This commit adds support to panicparse for parsing these new aggregate-typed arguments. It does so while retaining the type structure from the stack traces, which allows for the structure to be faithfully recreated and for aggregate-type information to be accurately augmented, when available. The aggregate-type structure is stored in a tree of mutually recursive Args and Arg structs.

The commit also adds support for handling the new "offset too large" operator that was added in the same runtime CL. This operator comes in the form of an "_" scalar argument in cases where the argument's frame offset was too large (>= 0xf0), preventing the argument from being printed in the stack trace.

This change raises questions about backwards compatability in a few different areas.

  • this change does not break backwards compatability with pre-1.17 stack traces. However, due to the nature of how most of the unit tests are set up (i.e. shelling out to local go binary), we are losing some test coverage of pre-1.17 stack trace parsing. Is that ok?
  • similarly, this change breaks the library's tests for pre-1.17 installations of Go. As a result, it bumps the Go version run in CI. Is that ok?
  • this change will not cause users of the github.com/maruel/panicparse/v2/stack library to fail to compile. However, due to the additions to the Arg struct, users of the library will fail to observe all arguments when parsing post-1.17 stack traces (but not pre-1.17 stack traces) unless they are aware of the new IsAggregate field. Is that ok?

@nvanbenschoten nvanbenschoten force-pushed the nvanbenschoten/go1.17 branch 3 times, most recently from c9cb48c to 815bb15 Compare September 6, 2021 06:26
@codecov
Copy link

codecov bot commented Sep 7, 2021

Codecov Report

Merging #62 (21cb5f0) into main (98fd2ed) will decrease coverage by 0.1%.
The diff coverage is 88.0%.

Impacted file tree graph

@@           Coverage Diff           @@
##            main     #62     +/-   ##
=======================================
- Coverage   90.0%   89.9%   -0.1%     
=======================================
  Files         12      12             
  Lines       3471    3587    +116     
=======================================
+ Hits        3124    3226    +102     
- Misses       282     291      +9     
- Partials      65      70      +5     
Impacted Files Coverage Δ
stack/stack.go 83.9% <78.9%> (-0.5%) ⬇️
stack/source.go 85.2% <84.6%> (-2.3%) ⬇️
stack/context.go 86.2% <100.0%> (+1.1%) ⬆️
stack/webstack/webstack.go 96.2% <0.0%> (+5.7%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 98fd2ed...21cb5f0. Read the comment docs.

@maruel
Copy link
Owner

maruel commented Sep 7, 2021

Wow thanks so much for working on this.

Since it's a somewhat tricky change, would you mind creating one PR with the //go:build commit to reduce the number of files touched by this PR? Thanks!

Answering your questions:

  • For now pre-1.17 coverage is important, it's fine to skip tests based on compiler version.
  • The CI both test current and previous version, please keep them working. They indeed broke.
  • For your latest question, I'll get back to you once I took a deeper look.

Fixes maruel#61.

In Go 1.17, the format of function arguments printed in stack traces was
improved. This is mentioned in the release notes: https://tip.golang.org/doc/go1.17#compiler

> The format of stack traces from the runtime (printed when an uncaught
> panic occurs, or when runtime.Stack is called) is improved. Previously,
> the function arguments were printed as hexadecimal words based on the
> memory layout. Now each argument in the source code is printed
> separately, separated by commas. Aggregate-typed (struct, array, string,
> slice, interface, and complex) arguments are delimited by curly braces.
> A caveat is that the value of an argument that only lives in a register
> and is not stored to memory may be inaccurate. Function return values
> (which were usually inaccurate) are no longer printed.

This runtime change was primarily performed in https://go-review.googlesource.com/c/go/+/304470.
The description on that CL presents the change in greater detail.

This commit adds support to panicparse for parsing these new aggregate-typed
arguments. It does so while retaining the type structure from the stack traces,
which allows for the structure to be faithfully recreated and for aggregate-type
information to be accurately augmented, when available. The aggregate-type
structure is stored in a tree of mutually recursive `Args` and `Arg` structs.

The commit also adds support for handling the new "offset too large" operator
that was added in the same runtime CL. This operator comes in the form of an "_"
scalar argument in cases where the argument's frame offset was too large (>=
0xf0), preventing the argument from being printed in the stack trace.

This change raises questions about backwards compatability in a few different
areas.
- this change does not break backwards compatability with pre-1.17 stack traces.
  However, due to the nature of how most of the unit tests are set up (i.e.
  shelling out to local go binary), we are losing some test coverage of pre-1.17
  stack trace parsing. Is that ok?
- similarly, this change breaks the library's tests for pre-1.17 installations
  of Go. As a result, it bumps the Go version run in CI. Is that ok?
- this change will not cause users of the `github.com/maruel/panicparse/v2/stack`
  library to fail to compile. However, due to the additions to the `Arg` struct,
  users of the library will fail to observe all arguments when parsing post-1.17
  stack traces (but not pre-1.17 stack traces) unless they are aware of the new
  `IsAggregate` field. Is that ok?

The change has the following effect on microbenchmarks:
```
name                      old time/op    new time/op    delta
ScanSnapshot_Passthru-16    2.45ns ± 3%    2.42ns ± 2%     ~     (p=0.094 n=9+9)
Aggregated_ToHTML-16        5.81ms ± 1%    5.87ms ± 3%     ~     (p=0.079 n=10+9)
ScanSnapshot_NoGuess-16     2.36ms ± 3%    2.52ms ± 1%   +7.10%  (p=0.000 n=10+9)
ScanSnapshot_Guess-16       2.35ms ± 4%    2.53ms ± 1%   +7.52%  (p=0.000 n=9+10)
Aggregate-16                 139µs ± 2%     195µs ± 3%  +40.14%  (p=0.000 n=9+10)

name                      old alloc/op   new alloc/op   delta
Aggregated_ToHTML-16        1.17MB ± 0%    1.17MB ± 0%   -0.18%  (p=0.010 n=9+10)
ScanSnapshot_Passthru-16     0.00B          0.00B          ~     (all equal)
ScanSnapshot_NoGuess-16      730kB ± 0%    1076kB ± 0%  +47.39%  (p=0.000 n=10+10)
ScanSnapshot_Guess-16        730kB ± 0%    1076kB ± 0%  +47.39%  (p=0.000 n=10+10)
Aggregate-16                 143kB ± 0%     254kB ± 0%  +77.81%  (p=0.000 n=10+10)

name                      old allocs/op  new allocs/op  delta
Aggregate-16                   565 ± 0%       565 ± 0%     ~     (all equal)
ScanSnapshot_Passthru-16      0.00           0.00          ~     (all equal)
Aggregated_ToHTML-16         41.0k ± 0%     41.1k ± 0%   +0.16%  (p=0.000 n=10+8)
ScanSnapshot_Guess-16        8.73k ± 0%     9.39k ± 0%   +7.46%  (p=0.000 n=10+9)
ScanSnapshot_NoGuess-16      8.73k ± 0%     9.39k ± 0%   +7.46%  (p=0.000 n=9+10)
```
This change uses a small bit of unsafe code to avoid a `string->[]byte` cast on
each use of `strconv.ParseUint`. This saves about 20% of the heap allocations in
the `ScanSnapshot` benchmarks.

```
name                      old time/op    new time/op    delta
ScanSnapshot_NoGuess-16     2.60ms ± 4%    2.48ms ± 1%   -4.37%  (p=0.008 n=5+5)
Aggregate-16                 195µs ± 2%     196µs ± 5%     ~     (p=0.841 n=5+5)
ScanSnapshot_Guess-16       2.58ms ± 5%    2.50ms ± 2%     ~     (p=0.056 n=5+5)
ScanSnapshot_Passthru-16    2.43ns ± 5%    2.43ns ± 3%     ~     (p=0.548 n=5+5)
Aggregated_ToHTML-16        5.81ms ± 1%    5.84ms ± 1%     ~     (p=0.421 n=5+5)

name                      old alloc/op   new alloc/op   delta
ScanSnapshot_NoGuess-16     1.08MB ± 0%    1.06MB ± 0%   -1.67%  (p=0.008 n=5+5)
ScanSnapshot_Guess-16       1.08MB ± 0%    1.06MB ± 0%   -1.65%  (p=0.008 n=5+5)
Aggregate-16                 254kB ± 0%     254kB ± 0%     ~     (p=0.722 n=5+5)
ScanSnapshot_Passthru-16     0.00B          0.00B          ~     (all equal)
Aggregated_ToHTML-16        1.17MB ± 0%    1.17MB ± 0%     ~     (p=0.841 n=5+5)

name                      old allocs/op  new allocs/op  delta
ScanSnapshot_Guess-16        9.39k ± 0%     7.49k ± 0%  -20.26%  (p=0.008 n=5+5)
ScanSnapshot_NoGuess-16      9.39k ± 0%     7.49k ± 0%  -20.26%  (p=0.000 n=5+4)
Aggregate-16                   565 ± 0%       565 ± 0%     ~     (all equal)
ScanSnapshot_Passthru-16      0.00           0.00          ~     (all equal)
Aggregated_ToHTML-16         41.0k ± 0%     41.0k ± 0%     ~     (p=0.492 n=5+5)
```
@nvanbenschoten
Copy link
Contributor Author

Thanks @maruel, I'm glad this is going in the right direction and that you're open to an external contribution. Also, thank you for this library - we're big fans over at CockroachDB!

Since it's a somewhat tricky change, would you mind creating one PR with the //go:build commit to reduce the number of files touched by this PR?

Done in #63 and #64.

For now pre-1.17 coverage is important, it's fine to skip tests based on compiler version.

I updated the commit to expand the version-dependent testing to support pre and post-1.17 stack traces.

The CI both test current and previous version, please keep them working. They indeed broke.

CI should be green now across the board.

For your latest question, I'll get back to you once I took a deeper look.

Sounds good to me. I'm happy to answer questions if I can be of any help.

@maruel
Copy link
Owner

maruel commented Sep 11, 2021

Update: I fixed issues that a debian maintainer discovered so you'll want to rebase.

I'd like to have the unsafeString() change to be in a separate PR. It's unrelated to the fix.

Is the handling about ast.BasicLit and ast.Ellipsis in source.go also unrelated?

@derekperkins
Copy link
Contributor

Excited for this to land!

@maruel
Copy link
Owner

maruel commented Oct 3, 2021

Sorry for the late response. This PR is quite hard to review because it changes multiple things at once. It would really help if "1 feature change = 1 PR".

Here I can see that you further disable inlining, improve source parsing and improve go1.17 processing support. These should be separate.

@maruel
Copy link
Owner

maruel commented Oct 17, 2021

Thanks for the change!

I cherry-picked the two commits with attribution but minor change as d7adfee and 5825e8f. Tests now pass everywhere.

@maruel maruel closed this Oct 17, 2021
@nvanbenschoten
Copy link
Contributor Author

Thanks @maruel for the help with this change, and thanks again for the fantastic library!

@derekperkins
Copy link
Contributor

Thanks to both of you! When should we expect a release with this included?

@nvanbenschoten
Copy link
Contributor Author

@derekperkins I think @maruel already has you covered with v2.2.0: https://github.com/maruel/panicparse/releases/tag/v2.2.0.

@derekperkins
Copy link
Contributor

Thanks. I saw that and since it only referenced GitHub Actions, I thought it was a maintenance release and didn't dig deeper.

@maruel
Copy link
Owner

maruel commented Oct 19, 2021

Oh thanks for the note, converted to a real release. https://github.com/maruel/panicparse/releases/tag/v2.2.0 now talks about go1.17.

@derekperkins
Copy link
Contributor

Thanks!

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.

panicparse fails on processing the new stack trace format in go1.17
3 participants