diff --git a/go.mod b/go.mod index 3043f50..3876ab1 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/gliderlabs/ssh v0.3.3 github.com/goodsign/monday v1.0.0 github.com/jeandeaual/go-locale v0.0.0-20210323163322-5cf4ff553a8d - github.com/maruel/panicparse v1.6.1 github.com/mattn/go-runewidth v0.0.13 github.com/miguelmota/go-coinmarketcap v0.1.8 github.com/mitchellh/go-wordwrap v1.0.1 diff --git a/go.sum b/go.sum index 0852068..fc211c8 100644 --- a/go.sum +++ b/go.sum @@ -155,7 +155,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -225,11 +224,7 @@ github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tW github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/maruel/panicparse v1.6.1 h1:803MjBzGcUgE1vYgg3UMNq3G1oyYeKkMu3t6hBS97x0= -github.com/maruel/panicparse v1.6.1/go.mod h1:uoxI4w9gJL6XahaYPMq/z9uadrdr1SyHuQwV2q80Mm0= -github.com/maruel/panicparse/v2 v2.1.1/go.mod h1:AeTWdCE4lcq8OKsLb6cHSj1RWHVSnV9HBCk7sKLF4Jg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -242,7 +237,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miguelmota/go-coinmarketcap v0.1.8 h1:rZhB7xs1j7qxxd1zftjADhAv6ECJQVhBom1dh3zURKY= github.com/miguelmota/go-coinmarketcap v0.1.8/go.mod h1:hBjej1IiB5+pfj+0cZhnxRkAc2bgky8qWLhCJTQ3zjw= @@ -476,7 +470,6 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/maruel/panicparse/LICENSE b/vendor/github.com/maruel/panicparse/LICENSE deleted file mode 100644 index b76840c..0000000 --- a/vendor/github.com/maruel/panicparse/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2015 Marc-Antoine Ruel - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/maruel/panicparse/stack/bucket.go b/vendor/github.com/maruel/panicparse/stack/bucket.go deleted file mode 100644 index 716f585..0000000 --- a/vendor/github.com/maruel/panicparse/stack/bucket.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2015 Marc-Antoine Ruel. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - -package stack - -import ( - "sort" -) - -// Similarity is the level at which two call lines arguments must match to be -// considered similar enough to coalesce them. -type Similarity int - -const ( - // ExactFlags requires same bits (e.g. Locked). - ExactFlags Similarity = iota - // ExactLines requests the exact same arguments on the call line. - ExactLines - // AnyPointer considers different pointers a similar call line. - AnyPointer - // AnyValue accepts any value as similar call line. - AnyValue -) - -// Aggregate merges similar goroutines into buckets. -// -// The buckets are ordered in library provided order of relevancy. You can -// reorder at your choosing. -func Aggregate(goroutines []*Goroutine, similar Similarity) []*Bucket { - type count struct { - ids []int - first bool - } - b := map[*Signature]*count{} - // O(n²). Fix eventually. - for _, routine := range goroutines { - found := false - for key, c := range b { - // When a match is found, this effectively drops the other goroutine ID. - if key.similar(&routine.Signature, similar) { - found = true - c.ids = append(c.ids, routine.ID) - c.first = c.first || routine.First - if !key.equal(&routine.Signature) { - // Almost but not quite equal. There's different pointers passed - // around but the same values. Zap out the different values. - newKey := key.merge(&routine.Signature) - b[newKey] = c - delete(b, key) - } - break - } - } - if !found { - // Create a copy of the Signature, since it will be mutated. - key := &Signature{} - *key = routine.Signature - b[key] = &count{ids: []int{routine.ID}, first: routine.First} - } - } - out := make(buckets, 0, len(b)) - for signature, c := range b { - sort.Ints(c.ids) - out = append(out, &Bucket{Signature: *signature, IDs: c.ids, First: c.first}) - } - sort.Sort(out) - return out -} - -// Bucket is a stack trace signature and the list of goroutines that fits this -// signature. -type Bucket struct { - // Signature is the generalized signature for this bucket. - Signature - // IDs is the ID of each Goroutine with this Signature. - IDs []int - // First is true if this Bucket contains the first goroutine, e.g. the one - // Signature that likely generated the panic() call, if any. - First bool -} - -// less does reverse sort. -func (b *Bucket) less(r *Bucket) bool { - if b.First || r.First { - return b.First - } - return b.Signature.less(&r.Signature) -} - -// - -// buckets is a list of Bucket sorted by repeation count. -type buckets []*Bucket - -func (b buckets) Len() int { - return len(b) -} - -func (b buckets) Less(i, j int) bool { - return b[i].less(b[j]) -} - -func (b buckets) Swap(i, j int) { - b[j], b[i] = b[i], b[j] -} diff --git a/vendor/github.com/maruel/panicparse/stack/context.go b/vendor/github.com/maruel/panicparse/stack/context.go deleted file mode 100644 index 692eb26..0000000 --- a/vendor/github.com/maruel/panicparse/stack/context.go +++ /dev/null @@ -1,935 +0,0 @@ -// Copyright 2018 Marc-Antoine Ruel. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - -//go:generate go get golang.org/x/tools/cmd/stringer -//go:generate stringer -type state - -package stack - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "os/user" - "path/filepath" - "regexp" - "runtime" - "sort" - "strconv" - "strings" -) - -// Context is a parsing context. -// -// It contains the deduced GOROOT and GOPATH, if guesspaths is true. -type Context struct { - // Goroutines is the Goroutines found. - // - // They are in the order that they were printed. - Goroutines []*Goroutine - - // GOROOT is the GOROOT as detected in the traceback, not the on the host. - // - // It can be empty if no root was determined, for example the traceback - // contains only non-stdlib source references. - // - // Empty is guesspaths was false. - GOROOT string - // GOPATHs is the GOPATH as detected in the traceback, with the value being - // the corresponding path mapped to the host. - // - // It can be empty if only stdlib code is in the traceback or if no local - // sources were matched up. In the general case there is only one entry in - // the map. - // - // Nil is guesspaths was false. - GOPATHs map[string]string - - // localGomoduleRoot is the root directory containing go.mod. It is - // considered to be the primary project containing the main executable. It is - // initialized by findRoots(). - // - // It only works with stack traces created in the local file system. - localGomoduleRoot string - // gomodImportPath is set to the relative import path that localGomoduleRoot - // represents. - gomodImportPath string - - // localgoroot is GOROOT with "/" as path separator. No trailing "/". - localgoroot string - // localgopaths is GOPATH with "/" as path separator. No trailing "/". - localgopaths []string -} - -// ParseDump processes the output from runtime.Stack(). -// -// Returns nil *Context if no stack trace was detected. -// -// It pipes anything not detected as a panic stack trace from r into out. It -// assumes there is junk before the actual stack trace. The junk is streamed to -// out. -// -// If guesspaths is false, no guessing of GOROOT and GOPATH is done, and Call -// entites do not have LocalSrcPath and IsStdlib filled in. If true, be warned -// that file presence is done, which means some level of disk I/O. -func ParseDump(r io.Reader, out io.Writer, guesspaths bool) (*Context, error) { - goroutines, err := parseDump(r, out) - if len(goroutines) == 0 { - return nil, err - } - c := &Context{ - Goroutines: goroutines, - localgoroot: strings.Replace(runtime.GOROOT(), "\\", "/", -1), - localgopaths: getGOPATHs(), - } - nameArguments(goroutines) - // Corresponding local values on the host for Context. - if guesspaths { - c.findRoots() - for _, r := range c.Goroutines { - // Note that this is important to call it even if - // c.GOROOT == c.localgoroot. - r.updateLocations(c.GOROOT, c.localgoroot, c.localGomoduleRoot, c.gomodImportPath, c.GOPATHs) - } - } - return c, err -} - -// Private stuff. - -func parseDump(r io.Reader, out io.Writer) ([]*Goroutine, error) { - scanner := bufio.NewScanner(r) - scanner.Split(scanLines) - // Do not enable race detection parsing yet, since it cannot be returned in - // Context at the moment. - s := scanningState{} - for scanner.Scan() { - line, err := s.scan(scanner.Text()) - if line != "" { - _, _ = io.WriteString(out, line) - } - if err != nil { - return s.goroutines, err - } - } - return s.goroutines, scanner.Err() -} - -// scanLines is similar to bufio.ScanLines except that it: -// - doesn't drop '\n' -// - doesn't strip '\r' -// - returns when the data is bufio.MaxScanTokenSize bytes -func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { - if atEOF && len(data) == 0 { - return 0, nil, nil - } - if i := bytes.IndexByte(data, '\n'); i >= 0 { - return i + 1, data[0 : i+1], nil - } - if atEOF { - return len(data), data, nil - } - if len(data) >= bufio.MaxScanTokenSize { - // Returns the line even if it is not at EOF nor has a '\n', otherwise the - // scanner will return bufio.ErrTooLong which is definitely not what we - // want. - return len(data), data, nil - } - return 0, nil, nil -} - -const ( - lockedToThread = "locked to thread" - elided = "...additional frames elided..." - // gotRaceHeader1, normal - raceHeaderFooter = "==================" - // gotRaceHeader2 - raceHeader = "WARNING: DATA RACE" -) - -// These are effectively constants. -var ( - // gotRoutineHeader - reRoutineHeader = regexp.MustCompile("^([ \t]*)goroutine (\\d+) \\[([^\\]]+)\\]\\:$") - reMinutes = regexp.MustCompile(`^(\d+) minutes$`) - - // gotUnavail - reUnavail = regexp.MustCompile("^(?:\t| +)goroutine running on other thread; stack unavailable") - - // gotFileFunc, gotRaceOperationFile, gotRaceGoroutineFile - // See gentraceback() in src/runtime/traceback.go for more information. - // - Sometimes the source file comes up as "". It is the - // compiler than generated these, not the runtime. - // - The tab may be replaced with spaces when a user copy-paste it, handle - // this transparently. - // - "runtime.gopanic" is explicitly replaced with "panic" by gentraceback(). - // - The +0x123 byte offset is printed when frame.pc > _func.entry. _func is - // generated by the linker. - // - The +0x123 byte offset is not included with generated code, e.g. unnamed - // functions "func·006()" which is generally go func() { ... }() - // statements. Since the _func is generated at runtime, it's probably why - // _func.entry is not set. - // - C calls may have fp=0x123 sp=0x123 appended. I think it normally happens - // when a signal is not correctly handled. It is printed with m.throwing>0. - // These are discarded. - // - For cgo, the source file may be "??". - reFile = regexp.MustCompile("^(?:\t| +)(\\?\\?|\\|.+\\.(?:c|go|s))\\:(\\d+)(?:| \\+0x[0-9a-f]+)(?:| fp=0x[0-9a-f]+ sp=0x[0-9a-f]+(?:| pc=0x[0-9a-f]+))$") - - // gotCreated - // Sadly, it doesn't note the goroutine number so we could cascade them per - // parenthood. - reCreated = regexp.MustCompile("^created by (.+)$") - - // gotFunc, gotRaceOperationFunc, gotRaceGoroutineFunc - reFunc = regexp.MustCompile(`^(.+)\((.*)\)$`) - - // Race: - // See https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/tsan_report.cpp - // for the code generating these messages. Please note only the block in - // #else // #if !SANITIZER_GO - // is used. - // TODO(maruel): " [failed to restore the stack]\n\n" - // TODO(maruel): "Global var %s of size %zu at %p declared at %s:%zu\n" - - // gotRaceOperationHeader - reRaceOperationHeader = regexp.MustCompile(`^(Read|Write) at (0x[0-9a-f]+) by goroutine (\d+):$`) - - // gotRaceOperationHeader - reRacePreviousOperationHeader = regexp.MustCompile(`^Previous (read|write) at (0x[0-9a-f]+) by goroutine (\d+):$`) - - // gotRaceGoroutineHeader - reRaceGoroutine = regexp.MustCompile(`^Goroutine (\d+) \((running|finished)\) created at:$`) - - // TODO(maruel): Use it. - //reRacePreviousOperationMainHeader = regexp.MustCompile("^Previous (read|write) at (0x[0-9a-f]+) by main goroutine:$") -) - -// state is the state of the scan to detect and process a stack trace. -type state int - -// Initial state is normal. Other states are when a stack trace is detected. -const ( - // Outside a stack trace. - // to: gotRoutineHeader, raceHeader1 - normal state = iota - - // Panic stack trace: - - // Signature: "" - // An empty line between goroutines. - // from: gotFileCreated, gotFileFunc - // to: gotRoutineHeader, normal - betweenRoutine - // Regexp: reRoutineHeader - // Signature: "goroutine 1 [running]:" - // Goroutine header was found. - // from: normal - // to: gotUnavail, gotFunc - gotRoutineHeader - // Regexp: reFunc - // Signature: "main.main()" - // Function call line was found. - // from: gotRoutineHeader - // to: gotFileFunc - gotFunc - // Regexp: reCreated - // Signature: "created by main.glob..func4" - // Goroutine creation line was found. - // from: gotFileFunc - // to: gotFileCreated - gotCreated - // Regexp: reFile - // Signature: "\t/foo/bar/baz.go:116 +0x35" - // File header was found. - // from: gotFunc - // to: gotFunc, gotCreated, betweenRoutine, normal - gotFileFunc - // Regexp: reFile - // Signature: "\t/foo/bar/baz.go:116 +0x35" - // File header was found. - // from: gotCreated - // to: betweenRoutine, normal - gotFileCreated - // Regexp: reUnavail - // Signature: "goroutine running on other thread; stack unavailable" - // State when the goroutine stack is instead is reUnavail. - // from: gotRoutineHeader - // to: betweenRoutine, gotCreated - gotUnavail - - // Race detector: - - // Constant: raceHeaderFooter - // Signature: "==================" - // from: normal - // to: normal, gotRaceHeader2 - gotRaceHeader1 - // Constant: raceHeader - // Signature: "WARNING: DATA RACE" - // from: gotRaceHeader1 - // to: normal, gotRaceOperationHeader - gotRaceHeader2 - // Regexp: reRaceOperationHeader, reRacePreviousOperationHeader - // Signature: "Read at 0x00c0000e4030 by goroutine 7:" - // A race operation was found. - // from: gotRaceHeader2 - // to: normal, gotRaceOperationFunc - gotRaceOperationHeader - // Regexp: reFunc - // Signature: " main.panicRace.func1()" - // Function that caused the race. - // from: gotRaceOperationHeader - // to: normal, gotRaceOperationFile - gotRaceOperationFunc - // Regexp: reFile - // Signature: "\t/foo/bar/baz.go:116 +0x35" - // File header that caused the race. - // from: gotRaceOperationFunc - // to: normal, betweenRaceOperations, gotRaceOperationFunc - gotRaceOperationFile - // Signature: "" - // Empty line between race operations or just after. - // from: gotRaceOperationFile - // to: normal, gotRaceOperationHeader, gotRaceGoroutineHeader - betweenRaceOperations - - // Regexp: reRaceGoroutine - // Signature: "Goroutine 7 (running) created at:" - // Goroutine header. - // from: betweenRaceOperations, betweenRaceGoroutines - // to: normal, gotRaceOperationHeader - gotRaceGoroutineHeader - // Regexp: reFunc - // Signature: " main.panicRace.func1()" - // Function that caused the race. - // from: gotRaceGoroutineHeader - // to: normal, gotRaceGoroutineFile - gotRaceGoroutineFunc - // Regexp: reFile - // Signature: "\t/foo/bar/baz.go:116 +0x35" - // File header that caused the race. - // from: gotRaceGoroutineFunc - // to: normal, betweenRaceGoroutines - gotRaceGoroutineFile - // Signature: "" - // Empty line between race stack traces. - // from: gotRaceGoroutineFile - // to: normal, gotRaceGoroutineHeader - betweenRaceGoroutines -) - -// raceOp is one of the detected data race operation as detected by the race -// detector. -type raceOp struct { - write bool - addr uint64 - id int - create Stack -} - -// scanningState is the state of the scan to detect and process a stack trace -// and stores the traces found. -type scanningState struct { - // Determines if race detection is enabled. Currently false since scan() - // would swallow the race detector output, but the data is not part of - // Context yet. - raceDetectionEnabled bool - - // goroutines contains all the goroutines found. - goroutines []*Goroutine - - state state - prefix string - races map[int]*raceOp - goroutineID int -} - -// scan scans one line, updates goroutines and move to the next state. -// -// TODO(maruel): Handle corrupted stack cases: -// - missed stack barrier -// - found next stack barrier at 0x123; expected -// - runtime: unexpected return pc for FUNC_NAME called from 0x123 -func (s *scanningState) scan(line string) (string, error) { - /* This is very useful to debug issues in the state machine. - defer func() { - log.Printf("scan(%q) -> %s", line, s.state) - }() - //*/ - var cur *Goroutine - if len(s.goroutines) != 0 { - cur = s.goroutines[len(s.goroutines)-1] - } - trimmed := line - if strings.HasSuffix(line, "\r\n") { - trimmed = line[:len(line)-2] - } else if strings.HasSuffix(line, "\n") { - trimmed = line[:len(line)-1] - } else { - // There's two cases: - // - It's the end of the stream and it's not terminating with EOL character. - // - The line is longer than bufio.MaxScanTokenSize - if s.state == normal { - return line, nil - } - // Let it flow. It's possible the last line was trimmed and we still want to parse it. - } - - if trimmed != "" && s.prefix != "" { - // This can only be the case if s.state != normal or the line is empty. - if !strings.HasPrefix(trimmed, s.prefix) { - prefix := s.prefix - s.state = normal - s.prefix = "" - return "", fmt.Errorf("inconsistent indentation: %q, expected %q", trimmed, prefix) - } - trimmed = trimmed[len(s.prefix):] - } - - switch s.state { - case normal: - // We could look for '^panic:' but this is more risky, there can be a lot - // of junk between this and the stack dump. - fallthrough - - case betweenRoutine: - // Look for a goroutine header. - if match := reRoutineHeader.FindStringSubmatch(trimmed); match != nil { - if id, err := strconv.Atoi(match[2]); err == nil { - // See runtime/traceback.go. - // ", \d+ minutes, locked to thread" - items := strings.Split(match[3], ", ") - sleep := 0 - locked := false - for i := 1; i < len(items); i++ { - if items[i] == lockedToThread { - locked = true - continue - } - // Look for duration, if any. - if match2 := reMinutes.FindStringSubmatch(items[i]); match2 != nil { - sleep, _ = strconv.Atoi(match2[1]) - } - } - g := &Goroutine{ - Signature: Signature{ - State: items[0], - SleepMin: sleep, - SleepMax: sleep, - Locked: locked, - }, - ID: id, - First: len(s.goroutines) == 0, - } - // Increase performance by always allocating 4 goroutines minimally. - if s.goroutines == nil { - s.goroutines = make([]*Goroutine, 0, 4) - } - s.goroutines = append(s.goroutines, g) - s.state = gotRoutineHeader - s.prefix = match[1] - return "", nil - } - } - // Switch to race detection mode. - if s.raceDetectionEnabled && trimmed == raceHeaderFooter { - // TODO(maruel): We should buffer it in case the next line is not a - // WARNING so we can output it back. - s.state = gotRaceHeader1 - return "", nil - } - // Fallthrough. - s.state = normal - s.prefix = "" - return line, nil - - case gotRoutineHeader: - if reUnavail.MatchString(trimmed) { - // Generate a fake stack entry. - cur.Stack.Calls = []Call{{SrcPath: ""}} - // Next line is expected to be an empty line. - s.state = gotUnavail - return "", nil - } - c := Call{} - if found, err := parseFunc(&c, trimmed); found { - // Increase performance by always allocating 4 calls minimally. - if cur.Stack.Calls == nil { - cur.Stack.Calls = make([]Call, 0, 4) - } - cur.Stack.Calls = append(cur.Stack.Calls, c) - s.state = gotFunc - return "", err - } - return "", fmt.Errorf("expected a function after a goroutine header, got: %q", strings.TrimSpace(trimmed)) - - case gotFunc: - // cur.Stack.Calls is guaranteed to have at least one item. - if found, err := parseFile(&cur.Stack.Calls[len(cur.Stack.Calls)-1], trimmed); err != nil { - return "", err - } else if !found { - return "", fmt.Errorf("expected a file after a function, got: %q", strings.TrimSpace(trimmed)) - } - s.state = gotFileFunc - return "", nil - - case gotCreated: - if found, err := parseFile(&cur.CreatedBy, trimmed); err != nil { - return "", err - } else if !found { - return "", fmt.Errorf("expected a file after a created line, got: %q", trimmed) - } - s.state = gotFileCreated - return "", nil - - case gotFileFunc: - if match := reCreated.FindStringSubmatch(trimmed); match != nil { - cur.CreatedBy.Func.Raw = match[1] - s.state = gotCreated - return "", nil - } - if elided == trimmed { - cur.Stack.Elided = true - // TODO(maruel): New state. - return "", nil - } - c := Call{} - if found, err := parseFunc(&c, trimmed); found { - // Increase performance by always allocating 4 calls minimally. - if cur.Stack.Calls == nil { - cur.Stack.Calls = make([]Call, 0, 4) - } - cur.Stack.Calls = append(cur.Stack.Calls, c) - s.state = gotFunc - return "", err - } - if trimmed == "" { - s.state = betweenRoutine - return "", nil - } - // Back to normal state. - s.state = normal - s.prefix = "" - return line, nil - - case gotFileCreated: - if trimmed == "" { - s.state = betweenRoutine - return "", nil - } - s.state = normal - s.prefix = "" - return line, nil - - case gotUnavail: - if trimmed == "" { - s.state = betweenRoutine - return "", nil - } - if match := reCreated.FindStringSubmatch(trimmed); match != nil { - cur.CreatedBy.Func.Raw = match[1] - s.state = gotCreated - return "", nil - } - return "", fmt.Errorf("expected empty line after unavailable stack, got: %q", strings.TrimSpace(trimmed)) - - // Race detector. - - case gotRaceHeader1: - if raceHeader == trimmed { - // TODO(maruel): We should buffer it in case the next line is not a - // WARNING so we can output it back. - s.state = gotRaceHeader2 - return "", nil - } - s.state = normal - return line, nil - - case gotRaceHeader2: - if match := reRaceOperationHeader.FindStringSubmatch(trimmed); match != nil { - w := match[1] == "Write" - addr, err := strconv.ParseUint(match[2], 0, 64) - if err != nil { - return "", fmt.Errorf("failed to parse address on line: %q", strings.TrimSpace(trimmed)) - } - id, err := strconv.Atoi(match[3]) - if err != nil { - return "", fmt.Errorf("failed to parse goroutine id on line: %q", strings.TrimSpace(trimmed)) - } - if s.races != nil { - panic("internal failure; expected s.races to be nil") - } - if s.goroutines != nil { - panic("internal failure; expected s.goroutines to be nil") - } - s.races = make(map[int]*raceOp, 4) - s.races[id] = &raceOp{write: w, addr: addr, id: id} - s.goroutines = append(make([]*Goroutine, 0, 4), &Goroutine{ID: id, First: true}) - s.goroutineID = id - s.state = gotRaceOperationHeader - return "", nil - } - s.state = normal - return line, nil - - case gotRaceOperationHeader: - c := Call{} - if found, err := parseFunc(&c, strings.TrimLeft(trimmed, "\t ")); found { - // Increase performance by always allocating 4 calls minimally. - if cur.Stack.Calls == nil { - cur.Stack.Calls = make([]Call, 0, 4) - } - cur.Stack.Calls = append(cur.Stack.Calls, c) - s.state = gotRaceOperationFunc - return "", err - } - return "", fmt.Errorf("expected a function after a race operation, got: %q", trimmed) - - case gotRaceOperationFunc: - if found, err := parseFile(&cur.Stack.Calls[len(cur.Stack.Calls)-1], trimmed); err != nil { - return "", err - } else if !found { - return "", fmt.Errorf("expected a file after a race function, got: %q", trimmed) - } - s.state = gotRaceOperationFile - return "", nil - - case gotRaceOperationFile: - if trimmed == "" { - s.state = betweenRaceOperations - return "", nil - } - c := Call{} - if found, err := parseFunc(&c, strings.TrimLeft(trimmed, "\t ")); found { - cur.Stack.Calls = append(cur.Stack.Calls, c) - s.state = gotRaceOperationFunc - return "", err - } - return "", fmt.Errorf("expected an empty line after a race file, got: %q", trimmed) - - case betweenRaceOperations: - // Look for other previous race data operations. - if match := reRacePreviousOperationHeader.FindStringSubmatch(trimmed); match != nil { - w := match[1] == "write" - addr, err := strconv.ParseUint(match[2], 0, 64) - if err != nil { - return "", fmt.Errorf("failed to parse address on line: %q", strings.TrimSpace(trimmed)) - } - id, err := strconv.Atoi(match[3]) - if err != nil { - return "", fmt.Errorf("failed to parse goroutine id on line: %q", strings.TrimSpace(trimmed)) - } - s.goroutineID = id - s.races[s.goroutineID] = &raceOp{write: w, addr: addr, id: id} - s.goroutines = append(s.goroutines, &Goroutine{ID: id}) - s.state = gotRaceOperationHeader - return "", nil - } - fallthrough - - case betweenRaceGoroutines: - if match := reRaceGoroutine.FindStringSubmatch(trimmed); match != nil { - id, err := strconv.Atoi(match[1]) - if err != nil { - return "", fmt.Errorf("failed to parse goroutine id on line: %q", strings.TrimSpace(trimmed)) - } - found := false - for _, g := range s.goroutines { - if g.ID == id { - g.State = match[2] - found = true - break - } - } - if !found { - return "", fmt.Errorf("unexpected goroutine ID on line: %q", strings.TrimSpace(trimmed)) - } - s.goroutineID = id - s.state = gotRaceGoroutineHeader - return "", nil - } - return "", fmt.Errorf("expected an operator or goroutine, got: %q", trimmed) - - // Race stack traces - - case gotRaceGoroutineFunc: - c := s.races[s.goroutineID].create.Calls - if found, err := parseFile(&c[len(c)-1], trimmed); err != nil { - return "", err - } else if !found { - return "", fmt.Errorf("expected a file after a race function, got: %q", trimmed) - } - // TODO(maruel): Set s.goroutines[].CreatedBy. - s.state = gotRaceGoroutineFile - return "", nil - - case gotRaceGoroutineFile: - if trimmed == "" { - s.state = betweenRaceGoroutines - return "", nil - } - if trimmed == raceHeaderFooter { - // Done. - s.state = normal - return "", nil - } - fallthrough - - case gotRaceGoroutineHeader: - c := Call{} - if found, err := parseFunc(&c, strings.TrimLeft(trimmed, "\t ")); found { - // TODO(maruel): Set s.goroutines[].CreatedBy. - s.races[s.goroutineID].create.Calls = append(s.races[s.goroutineID].create.Calls, c) - s.state = gotRaceGoroutineFunc - return "", err - } - return "", fmt.Errorf("expected a function after a race operation or a race file, got: %q", trimmed) - - default: - return "", errors.New("internal error") - } -} - -// parseFunc only return an error if also returning a Call. -// -// Uses reFunc. -func parseFunc(c *Call, line string) (bool, error) { - if match := reFunc.FindStringSubmatch(line); match != nil { - c.Func.Raw = match[1] - for _, a := range strings.Split(match[2], ", ") { - if a == "..." { - c.Args.Elided = true - continue - } - if a == "" { - // Remaining values were dropped. - break - } - v, err := strconv.ParseUint(a, 0, 64) - if err != nil { - return true, fmt.Errorf("failed to parse int on line: %q", strings.TrimSpace(line)) - } - // Increase performance by always allocating 4 values minimally. - if c.Args.Values == nil { - c.Args.Values = make([]Arg, 0, 4) - } - c.Args.Values = append(c.Args.Values, Arg{Value: v}) - } - return true, nil - } - return false, nil -} - -// parseFile only return an error if also processing a Call. -// -// Uses reFile. -func parseFile(c *Call, line string) (bool, error) { - if match := reFile.FindStringSubmatch(line); match != nil { - num, err := strconv.Atoi(match[2]) - if err != nil { - return true, fmt.Errorf("failed to parse int on line: %q", strings.TrimSpace(line)) - } - c.SrcPath = match[1] - c.Line = num - return true, nil - } - return false, nil -} - -// hasSrcPrefix returns true if any of s is the prefix of p. -func hasSrcPrefix(p string, s map[string]string) bool { - for prefix := range s { - if strings.HasPrefix(p, prefix+"/src/") || strings.HasPrefix(p, prefix+"/pkg/mod/") { - return true - } - } - return false -} - -// getFiles returns all the source files deduped and ordered. -func getFiles(goroutines []*Goroutine) []string { - files := map[string]struct{}{} - for _, g := range goroutines { - for _, c := range g.Stack.Calls { - files[c.SrcPath] = struct{}{} - } - } - if len(files) == 0 { - return nil - } - out := make([]string, 0, len(files)) - for f := range files { - out = append(out, f) - } - sort.Strings(out) - return out -} - -// splitPath splits a path using "/" as separator into its components. -// -// The first item has its initial path separator kept. -func splitPath(p string) []string { - if p == "" { - return nil - } - var out []string - s := "" - for _, c := range p { - if c != '/' || (len(out) == 0 && strings.Count(s, "/") == len(s)) { - s += string(c) - } else if s != "" { - out = append(out, s) - s = "" - } - } - if s != "" { - out = append(out, s) - } - return out -} - -// isFile returns true if the path is a valid file. -func isFile(p string) bool { - // TODO(maruel): Is it faster to open the file or to stat it? Worth a perf - // test on Windows. - i, err := os.Stat(p) - return err == nil && !i.IsDir() -} - -// rootedIn returns a root if the file split in parts is rooted in root. -// -// Uses "/" as path separator. -func rootedIn(root string, parts []string) string { - //log.Printf("rootIn(%s, %v)", root, parts) - for i := 1; i < len(parts); i++ { - suffix := pathJoin(parts[i:]...) - if isFile(pathJoin(root, suffix)) { - return pathJoin(parts[:i]...) - } - } - return "" -} - -// reModule find the module line in a go.mod file. It works even on CRLF file. -var reModule = regexp.MustCompile(`(?m)^module\s+([^\n\r]+)\r?$`) - -// isGoModule returns the string to the directory containing a go.mod/go.sum -// files pair, and the go import path it represents, if found. -func isGoModule(parts []string) (string, string) { - for i := len(parts); i > 0; i-- { - prefix := pathJoin(parts[:i]...) - if isFile(pathJoin(prefix, "go.sum")) { - b, err := ioutil.ReadFile(pathJoin(prefix, "go.mod")) - if err != nil { - continue - } - if match := reModule.FindSubmatch(b); match != nil { - return prefix, string(match[1]) - } - } - } - return "", "" -} - -// findRoots sets member GOROOT, GOPATHs and localGomoduleRoot. -// -// This causes disk I/O as it checks for file presence. -// -// Returns the number of missing files. -func (c *Context) findRoots() int { - c.GOPATHs = map[string]string{} - missing := 0 - for _, f := range getFiles(c.Goroutines) { - // TODO(maruel): Could a stack dump have mixed cases? I think it's - // possible, need to confirm and handle. - //log.Printf(" Analyzing %s", f) - - // First checks skip file I/O. - if c.GOROOT != "" && strings.HasPrefix(f, c.GOROOT+"/src/") { - // stdlib. - continue - } - if hasSrcPrefix(f, c.GOPATHs) { - // $GOPATH/src or go.mod dependency in $GOPATH/pkg/mod. - continue - } - - // At this point, disk will be looked up. - parts := splitPath(f) - if c.GOROOT == "" { - if r := rootedIn(c.localgoroot+"/src", parts); r != "" { - c.GOROOT = r[:len(r)-4] - //log.Printf("Found GOROOT=%s", c.GOROOT) - continue - } - } - found := false - for _, l := range c.localgopaths { - if r := rootedIn(l+"/src", parts); r != "" { - //log.Printf("Found GOPATH=%s", r[:len(r)-4]) - c.GOPATHs[r[:len(r)-4]] = l - found = true - break - } - if r := rootedIn(l+"/pkg/mod", parts); r != "" { - //log.Printf("Found GOPATH=%s", r[:len(r)-8]) - c.GOPATHs[r[:len(r)-8]] = l - found = true - break - } - } - // If the source is not found, it's probably a go module. - if !found { - if c.localGomoduleRoot == "" && len(parts) > 1 { - // Search upward looking for a go.mod/go.sum pair. - c.localGomoduleRoot, c.gomodImportPath = isGoModule(parts[:len(parts)-1]) - } - if c.localGomoduleRoot != "" && strings.HasPrefix(f, c.localGomoduleRoot+"/") { - continue - } - } - if !found { - // If the source is not found, just too bad. - //log.Printf("Failed to find locally: %s", f) - missing++ - } - } - return missing -} - -// getGOPATHs returns parsed GOPATH or its default, using "/" as path separator. -func getGOPATHs() []string { - var out []string - if gp := os.Getenv("GOPATH"); gp != "" { - for _, v := range filepath.SplitList(gp) { - // Disallow non-absolute paths? - if v != "" { - v = strings.Replace(v, "\\", "/", -1) - // Trim trailing "/". - if l := len(v); v[l-1] == '/' { - v = v[:l-1] - } - out = append(out, v) - } - } - } - if len(out) == 0 { - homeDir := "" - u, err := user.Current() - if err != nil { - homeDir = os.Getenv("HOME") - if homeDir == "" { - panic(fmt.Sprintf("Could not get current user or $HOME: %s\n", err.Error())) - } - } else { - homeDir = u.HomeDir - } - out = []string{strings.Replace(homeDir+"/go", "\\", "/", -1)} - } - return out -} diff --git a/vendor/github.com/maruel/panicparse/stack/source.go b/vendor/github.com/maruel/panicparse/stack/source.go deleted file mode 100644 index dc35505..0000000 --- a/vendor/github.com/maruel/panicparse/stack/source.go +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright 2015 Marc-Antoine Ruel. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - -// This file contains the code to process sources, to be able to deduct the -// original types. - -package stack - -import ( - "bytes" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io/ioutil" - "log" - "math" - "strings" -) - -// cache is a cache of sources on the file system. -type cache struct { - files map[string][]byte - parsed map[string]*parsedFile -} - -// Augment processes source files to improve calls to be more descriptive. -// -// It modifies goroutines in place. It requires calling ParseDump() with -// guesspaths set to true to work properly. -func Augment(goroutines []*Goroutine) { - c := &cache{} - for _, g := range goroutines { - c.augmentGoroutine(g) - } -} - -// augmentGoroutine processes source files to improve call to be more -// descriptive. -// -// It modifies the routine. -func (c *cache) augmentGoroutine(goroutine *Goroutine) { - if c.files == nil { - c.files = map[string][]byte{} - } - if c.parsed == nil { - c.parsed = map[string]*parsedFile{} - } - // For each call site, look at the next call and populate it. Then we can - // walk back and reformat things. - for i := range goroutine.Stack.Calls { - c.load(goroutine.Stack.Calls[i].LocalSrcPath) - } - - // Once all loaded, we can look at the next call when available. - for i := 0; i < len(goroutine.Stack.Calls)-1; i++ { - // Get the AST from the previous call and process the call line with it. - if f := c.getFuncAST(&goroutine.Stack.Calls[i]); f != nil { - processCall(&goroutine.Stack.Calls[i], f) - } - } -} - -// Private stuff. - -// load loads a source file and parses the AST tree. Failures are ignored. -func (c *cache) load(fileName string) { - if fileName == "" { - return - } - if _, ok := c.parsed[fileName]; ok { - return - } - c.parsed[fileName] = nil - if !strings.HasSuffix(fileName, ".go") { - // Ignore C and assembly. - c.files[fileName] = nil - return - } - //log.Printf("load(%s)", fileName) - if _, ok := c.files[fileName]; !ok { - var err error - if c.files[fileName], err = ioutil.ReadFile(fileName); err != nil { - log.Printf("Failed to read %s: %s", fileName, err) - c.files[fileName] = nil - return - } - } - fset := token.NewFileSet() - src := c.files[fileName] - parsed, err := parser.ParseFile(fset, fileName, src, 0) - if err != nil { - log.Printf("Failed to parse %s: %s", fileName, err) - return - } - // Convert the line number into raw file offset. - offsets := []int{0, 0} - start := 0 - for l := 1; start < len(src); l++ { - start += bytes.IndexByte(src[start:], '\n') + 1 - offsets = append(offsets, start) - } - c.parsed[fileName] = &parsedFile{offsets, parsed} -} - -func (c *cache) getFuncAST(call *Call) *ast.FuncDecl { - if p := c.parsed[call.LocalSrcPath]; p != nil { - return p.getFuncAST(call.Func.Name(), call.Line) - } - return nil -} - -type parsedFile struct { - lineToByteOffset []int - parsed *ast.File -} - -// getFuncAST gets the callee site function AST representation for the code -// inside the function f at line l. -func (p *parsedFile) getFuncAST(f string, l int) (d *ast.FuncDecl) { - if len(p.lineToByteOffset) <= l { - // The line number in the stack trace line does not exist in the file. That - // can only mean that the sources on disk do not match the sources used to - // build the binary. - // TODO(maruel): This should be surfaced, so that source parsing is - // completely ignored. - return - } - - // Walk the AST to find the lineToByteOffset that fits the line number. - var lastFunc *ast.FuncDecl - // Inspect() goes depth first. This means for example that a function like: - // func a() { - // b := func() {} - // c() - // } - // - // Were we are looking at the c() call can return confused values. It is - // important to look at the actual ast.Node hierarchy. - ast.Inspect(p.parsed, func(n ast.Node) bool { - if d != nil { - return false - } - if n == nil { - return true - } - if int(n.Pos()) >= p.lineToByteOffset[l] { - // We are expecting a ast.CallExpr node. It can be harder to figure out - // when there are multiple calls on a single line, as the stack trace - // doesn't have file byte offset information, only line based. - // gofmt will always format to one function call per line but there can - // be edge cases, like: - // a = A{Foo(), Bar()} - d = lastFunc - //p.processNode(call, n) - return false - } else if f, ok := n.(*ast.FuncDecl); ok { - lastFunc = f - } - return true - }) - return -} - -func name(n ast.Node) string { - switch t := n.(type) { - case *ast.InterfaceType: - return "interface{}" - case *ast.Ident: - return t.Name - case *ast.SelectorExpr: - return t.Sel.Name - case *ast.StarExpr: - return "*" + name(t.X) - default: - return "" - } -} - -// fieldToType returns the type name and whether if it's an ellipsis. -func fieldToType(f *ast.Field) (string, bool) { - switch arg := f.Type.(type) { - case *ast.ArrayType: - return "[]" + name(arg.Elt), false - case *ast.Ellipsis: - return name(arg.Elt), true - case *ast.FuncType: - // Do not print the function signature to not overload the trace. - return "func", false - case *ast.Ident: - return arg.Name, false - case *ast.InterfaceType: - return "interface{}", false - case *ast.SelectorExpr: - return arg.Sel.Name, false - case *ast.StarExpr: - return "*" + name(arg.X), false - case *ast.MapType: - return fmt.Sprintf("map[%s]%s", name(arg.Key), name(arg.Value)), false - case *ast.ChanType: - return fmt.Sprintf("chan %s", name(arg.Value)), false - default: - // TODO(maruel): Implement anything missing. - return "", false - } -} - -// extractArgumentsType returns the name of the type of each input argument. -func extractArgumentsType(f *ast.FuncDecl) ([]string, bool) { - var fields []*ast.Field - if f.Recv != nil { - if len(f.Recv.List) != 1 { - panic("Expect only one receiver; please fix panicparse's code") - } - // If it is an object receiver (vs a pointer receiver), its address is not - // printed in the stack trace so it needs to be ignored. - if _, ok := f.Recv.List[0].Type.(*ast.StarExpr); ok { - fields = append(fields, f.Recv.List[0]) - } - } - var types []string - extra := false - for _, arg := range append(fields, f.Type.Params.List...) { - // Assert that extra is only set on the last item of fields? - var t string - t, extra = fieldToType(arg) - mult := len(arg.Names) - if mult == 0 { - mult = 1 - } - for i := 0; i < mult; i++ { - types = append(types, t) - } - } - return types, extra -} - -// processCall walks the function and populate call accordingly. -func processCall(call *Call, f *ast.FuncDecl) { - values := make([]uint64, len(call.Args.Values)) - for i := range call.Args.Values { - values[i] = call.Args.Values[i].Value - } - index := 0 - pop := func() uint64 { - if len(values) != 0 { - x := values[0] - values = values[1:] - index++ - return x - } - return 0 - } - popName := func() string { - n := call.Args.Values[index].Name - v := pop() - if len(n) == 0 { - return fmt.Sprintf("0x%x", v) - } - return n - } - - types, extra := extractArgumentsType(f) - for i := 0; len(values) != 0; i++ { - var t string - if i >= len(types) { - if !extra { - // These are unexpected value! Print them as hex. - call.Args.Processed = append(call.Args.Processed, popName()) - continue - } - t = types[len(types)-1] - } else { - t = types[i] - } - switch t { - case "float32": - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%g", math.Float32frombits(uint32(pop())))) - case "float64": - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%g", math.Float64frombits(pop()))) - case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%d", pop())) - case "string": - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s, len=%d)", t, popName(), pop())) - default: - if strings.HasPrefix(t, "*") { - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s)", t, popName())) - } else if strings.HasPrefix(t, "[]") { - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s len=%d cap=%d)", t, popName(), pop(), pop())) - } else { - // Assumes it's an interface. For now, discard the object value, which - // is probably not a good idea. - call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s)", t, popName())) - pop() - } - } - if len(values) == 0 && call.Args.Elided { - return - } - } -} diff --git a/vendor/github.com/maruel/panicparse/stack/stack.go b/vendor/github.com/maruel/panicparse/stack/stack.go deleted file mode 100644 index 08bd118..0000000 --- a/vendor/github.com/maruel/panicparse/stack/stack.go +++ /dev/null @@ -1,733 +0,0 @@ -// Copyright 2015 Marc-Antoine Ruel. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - -// Package stack analyzes stack dump of Go processes and simplifies it. -// -// It is mostly useful on servers will large number of identical goroutines, -// making the crash dump harder to read than strictly necessary. -package stack - -import ( - "fmt" - "net/url" - "os" - "path/filepath" - "sort" - "strings" - "unicode" - "unicode/utf8" -) - -// Func is a function call as read in a goroutine stack trace. -// -// Go stack traces print a mangled function call, this wrapper unmangle the -// string before printing and adds other filtering methods. -// -// The main caveat is that for calls in package main, the package import URL is -// left out. -type Func struct { - Raw string -} - -// String return the fully qualified package import path dot function/method -// name. -// -// It returns the unmangled form of .Raw. -func (f *Func) String() string { - s, _ := url.QueryUnescape(f.Raw) - return s -} - -// Name returns the function name. -// -// Methods are fully qualified, including the struct type. -func (f *Func) Name() string { - // This works even on Windows as filepath.Base() splits also on "/". - // TODO(maruel): This code will fail on a source file with a dot in its name. - parts := strings.SplitN(filepath.Base(f.Raw), ".", 2) - if len(parts) == 1 { - return parts[0] - } - return parts[1] -} - -// importPath returns the fully qualified package import URL as a guess from -// the function signature. -// -// Not exported because Call.ImportPath() should be called instead, as this -// function can't return the import path for package main. -func (f *Func) importPath() string { - i := strings.LastIndexByte(f.Raw, '/') - if i == -1 { - return "" - } - j := strings.IndexByte(f.Raw[i:], '.') - if j == -1 { - return "" - } - s, _ := url.QueryUnescape(f.Raw[:i+j]) - return s -} - -// PkgName returns the guessed package name for this function reference. -// -// Since the package name can differ from the package import path, the result -// is incorrect when there's a mismatch between the directory name containing -// the package and the package name. -func (f *Func) PkgName() string { - parts := strings.SplitN(filepath.Base(f.Raw), ".", 2) - if len(parts) == 1 { - return "" - } - s, _ := url.QueryUnescape(parts[0]) - return s -} - -// PkgDotName returns "." format. -// -// Since the package name can differ from the package import path, the result -// is incorrect when there's a mismatch between the directory name containing -// the package and the package name. -func (f *Func) PkgDotName() string { - parts := strings.SplitN(filepath.Base(f.Raw), ".", 2) - s, _ := url.QueryUnescape(parts[0]) - if len(parts) == 1 { - return parts[0] - } - if s != "" || parts[1] != "" { - return s + "." + parts[1] - } - return "" -} - -// IsExported returns true if the function is exported. -func (f *Func) IsExported() bool { - name := f.Name() - // TODO(maruel): Something like serverHandler.ServeHTTP in package net/host - // should not be considered exported. We need something similar to the - // decoding done in symbol() in internal/htmlstack. - parts := strings.Split(name, ".") - r, _ := utf8.DecodeRuneInString(parts[len(parts)-1]) - if unicode.ToUpper(r) == r { - return true - } - return f.PkgName() == "main" && name == "main" -} - -// Arg is an argument on a Call. -type Arg struct { - Value uint64 // Value is the raw value as found in the stack trace - Name string // Name is a pseudo name given to the argument -} - -const ( - // Assumes all values are above 4MiB and positive are pointers; assuming that - // above half the memory is kernel memory. - // - // This is not always true but this should be good enough to help - // implementing AnyPointer. - pointerFloor = 4 * 1024 * 1024 - // Assume the stack was generated with the same bitness (32 vs 64) than the - // code processing it. - pointerCeiling = uint64((^uint(0)) >> 1) -) - -// IsPtr returns true if we guess it's a pointer. It's only a guess, it can be -// easily be confused by a bitmask. -func (a *Arg) IsPtr() bool { - return a.Value > pointerFloor && a.Value < pointerCeiling -} - -const zeroToNine = "0123456789" - -// String prints the argument as the name if present, otherwise as the value. -func (a *Arg) String() string { - if a.Name != "" { - return a.Name - } - if a.Value < uint64(len(zeroToNine)) { - return zeroToNine[a.Value : a.Value+1] - } - return fmt.Sprintf("0x%x", a.Value) -} - -// similar returns true if the two Arg are equal or almost but not quite equal. -func (a *Arg) similar(r *Arg, similar Similarity) bool { - switch similar { - case ExactFlags, ExactLines: - return *a == *r - case AnyValue: - return true - case AnyPointer: - if a.IsPtr() != r.IsPtr() { - return false - } - return a.IsPtr() || a.Value == r.Value - default: - return false - } -} - -// Args is a series of function call arguments. -type Args struct { - // Values is the arguments as shown on the stack trace. They are mangled via - // simplification. - Values []Arg - // Processed is the arguments generated from processing the source files. It - // can have a length lower than Values. - Processed []string - // Elided when set means there was a trailing ", ...". - Elided bool -} - -func (a *Args) String() string { - var v []string - if len(a.Processed) != 0 { - v = a.Processed - } else { - v = make([]string, 0, len(a.Values)) - for _, item := range a.Values { - v = append(v, item.String()) - } - } - if a.Elided { - v = append(v, "...") - } - return strings.Join(v, ", ") -} - -// equal returns true only if both arguments are exactly equal. -func (a *Args) equal(r *Args) bool { - if a.Elided != r.Elided || len(a.Values) != len(r.Values) { - return false - } - for i, l := range a.Values { - if l != r.Values[i] { - return false - } - } - return true -} - -// similar returns true if the two Args are equal or almost but not quite -// equal. -func (a *Args) similar(r *Args, similar Similarity) bool { - if a.Elided != r.Elided || len(a.Values) != len(r.Values) { - return false - } - for i := range a.Values { - if !a.Values[i].similar(&r.Values[i], similar) { - return false - } - } - return true -} - -// merge merges two similar Args, zapping out differences. -func (a *Args) merge(r *Args) Args { - out := Args{ - Values: make([]Arg, len(a.Values)), - Elided: a.Elided, - } - for i, l := range a.Values { - if l != r.Values[i] { - out.Values[i].Name = "*" - out.Values[i].Value = l.Value - } else { - out.Values[i] = l - } - } - return out -} - -// Call is an item in the stack trace. -type Call struct { - // SrcPath is the full path name of the source file as seen in the trace. - SrcPath string - // LocalSrcPath is the full path name of the source file as seen in the host, - // if found. - LocalSrcPath string - // Line is the line number. - Line int - // Func is the fully qualified function name (encoded). - Func Func - // Args is the call arguments. - Args Args - - // The following are only set if guesspaths is set to true in ParseDump(). - // IsStdlib is true if it is a Go standard library function. This includes - // the 'go test' generated main executable. - IsStdlib bool - // RelSrcPath is the relative path to GOROOT or GOPATH. Only set when - // Augment() is called. - RelSrcPath string -} - -// equal returns true only if both calls are exactly equal. -func (c *Call) equal(r *Call) bool { - return c.SrcPath == r.SrcPath && c.Line == r.Line && c.Func == r.Func && c.Args.equal(&r.Args) -} - -// similar returns true if the two Call are equal or almost but not quite -// equal. -func (c *Call) similar(r *Call, similar Similarity) bool { - return c.SrcPath == r.SrcPath && c.Line == r.Line && c.Func == r.Func && c.Args.similar(&r.Args, similar) -} - -// merge merges two similar Call, zapping out differences. -func (c *Call) merge(r *Call) Call { - return Call{ - SrcPath: c.SrcPath, - LocalSrcPath: c.LocalSrcPath, - Line: c.Line, - Func: c.Func, - Args: c.Args.merge(&r.Args), - IsStdlib: c.IsStdlib, - RelSrcPath: c.RelSrcPath, - } -} - -// SrcName returns the base file name of the source file. -func (c *Call) SrcName() string { - return filepath.Base(c.SrcPath) -} - -// SrcLine returns "source.go:line", including only the base file name. -// -// Deprecated: Format it yourself, will be removed in v2. -func (c *Call) SrcLine() string { - return fmt.Sprintf("%s:%d", c.SrcName(), c.Line) -} - -// FullSrcLine returns "/path/to/source.go:line". -// -// This file path is mutated to look like the local path. -// -// Deprecated: Format it yourself, will be removed in v2. -func (c *Call) FullSrcLine() string { - return fmt.Sprintf("%s:%d", c.SrcPath, c.Line) -} - -// PkgSrc returns one directory plus the file name of the source file. -// -// Since the package name can differ from the package import path, the result -// is incorrect when there's a mismatch between the directory name containing -// the package and the package name. -func (c *Call) PkgSrc() string { - return pathJoin(filepath.Base(filepath.Dir(c.SrcPath)), c.SrcName()) -} - -// IsPkgMain returns true if it is in the main package. -func (c *Call) IsPkgMain() bool { - return c.Func.PkgName() == "main" -} - -// ImportPath returns the fully qualified package import path. -// -// In the case of package "main", it returns the underlying path to the main -// package instead of "main" if guesspaths=true was specified to ParseDump(). -func (c *Call) ImportPath() string { - // In case guesspath=true was passed to ParseDump(). - if c.RelSrcPath != "" { - if i := strings.LastIndexByte(c.RelSrcPath, '/'); i != -1 { - return c.RelSrcPath[:i] - } - } - // Fallback to best effort. - if !c.IsPkgMain() { - return c.Func.importPath() - } - // In package main, it can only work well if guesspath=true was used. Return - // an empty string instead of garbagge. - return "" -} - -const testMainSrc = "_test" + string(os.PathSeparator) + "_testmain.go" - -// updateLocations initializes LocalSrcPath, RelSrcPath and IsStdlib. -// -// goroot, localgoroot, localgomod, gomodImportPath and gopaths are expected to -// be in "/" format even on Windows. They must not have a trailing "/". -func (c *Call) updateLocations(goroot, localgoroot, localgomod, gomodImportPath string, gopaths map[string]string) { - if c.SrcPath == "" { - return - } - // Check GOROOT first. - if goroot != "" { - if prefix := goroot + "/src/"; strings.HasPrefix(c.SrcPath, prefix) { - // Replace remote GOROOT with local GOROOT. - c.RelSrcPath = c.SrcPath[len(prefix):] - c.LocalSrcPath = pathJoin(localgoroot, "src", c.RelSrcPath) - c.IsStdlib = true - goto done - } - } - // Check GOPATH. - // TODO(maruel): Sort for deterministic behavior? - for prefix, dest := range gopaths { - if p := prefix + "/src/"; strings.HasPrefix(c.SrcPath, p) { - c.RelSrcPath = c.SrcPath[len(p):] - c.LocalSrcPath = pathJoin(dest, "src", c.RelSrcPath) - goto done - } - // For modules, the path has to be altered, as it contains the version. - if p := prefix + "/pkg/mod/"; strings.HasPrefix(c.SrcPath, p) { - c.RelSrcPath = c.SrcPath[len(p):] - c.LocalSrcPath = pathJoin(dest, "pkg/mod", c.RelSrcPath) - goto done - } - } - // Go module path detection only works with stack traces created on the local - // file system. - if localgomod != "" { - if prefix := localgomod + "/"; strings.HasPrefix(c.SrcPath, prefix) { - c.RelSrcPath = gomodImportPath + "/" + c.SrcPath[len(prefix):] - c.LocalSrcPath = c.SrcPath - goto done - } - } -done: - if !c.IsStdlib { - // Consider _test/_testmain.go as stdlib since it's injected by "go test". - c.IsStdlib = c.PkgSrc() == testMainSrc - } -} - -// Stack is a call stack. -type Stack struct { - // Calls is the call stack. First is original function, last is leaf - // function. - Calls []Call - // Elided is set when there's >100 items in Stack, currently hardcoded in - // package runtime. - Elided bool -} - -// equal returns true on if both call stacks are exactly equal. -func (s *Stack) equal(r *Stack) bool { - if len(s.Calls) != len(r.Calls) || s.Elided != r.Elided { - return false - } - for i := range s.Calls { - if !s.Calls[i].equal(&r.Calls[i]) { - return false - } - } - return true -} - -// similar returns true if the two Stack are equal or almost but not quite -// equal. -func (s *Stack) similar(r *Stack, similar Similarity) bool { - if len(s.Calls) != len(r.Calls) || s.Elided != r.Elided { - return false - } - for i := range s.Calls { - if !s.Calls[i].similar(&r.Calls[i], similar) { - return false - } - } - return true -} - -// merge merges two similar Stack, zapping out differences. -func (s *Stack) merge(r *Stack) *Stack { - // Assumes similar stacks have the same length. - out := &Stack{ - Calls: make([]Call, len(s.Calls)), - Elided: s.Elided, - } - for i := range s.Calls { - out.Calls[i] = s.Calls[i].merge(&r.Calls[i]) - } - return out -} - -// less compares two Stack, where the ones that are less are more -// important, so they come up front. -// -// A Stack with more private functions is 'less' so it is at the top. -// Inversely, a Stack with only public functions is 'more' so it is at the -// bottom. -func (s *Stack) less(r *Stack) bool { - lStdlib := 0 - lPrivate := 0 - for _, c := range s.Calls { - if c.IsStdlib { - lStdlib++ - } else { - lPrivate++ - } - } - rStdlib := 0 - rPrivate := 0 - for _, s := range r.Calls { - if s.IsStdlib { - rStdlib++ - } else { - rPrivate++ - } - } - if lPrivate > rPrivate { - return true - } - if lPrivate < rPrivate { - return false - } - if lStdlib > rStdlib { - return false - } - if lStdlib < rStdlib { - return true - } - - // Stack lengths are the same. - for x := range s.Calls { - if s.Calls[x].Func.Raw < r.Calls[x].Func.Raw { - return true - } - if s.Calls[x].Func.Raw > r.Calls[x].Func.Raw { - return true - } - if s.Calls[x].PkgSrc() < r.Calls[x].PkgSrc() { - return true - } - if s.Calls[x].PkgSrc() > r.Calls[x].PkgSrc() { - return true - } - if s.Calls[x].Line < r.Calls[x].Line { - return true - } - if s.Calls[x].Line > r.Calls[x].Line { - return true - } - } - return false -} - -func (s *Stack) updateLocations(goroot, localgoroot, localgomod, gomodImportPath string, gopaths map[string]string) { - for i := range s.Calls { - s.Calls[i].updateLocations(goroot, localgoroot, localgomod, gomodImportPath, gopaths) - } -} - -// Signature represents the signature of one or multiple goroutines. -// -// It is effectively the stack trace plus the goroutine internal bits, like -// it's state, if it is thread locked, which call site created this goroutine, -// etc. -type Signature struct { - // State is the goroutine state at the time of the snapshot. - // - // Use git grep 'gopark(|unlock)\(' to find them all plus everything listed - // in runtime/traceback.go. Valid values includes: - // - chan send, chan receive, select - // - finalizer wait, mark wait (idle), - // - Concurrent GC wait, GC sweep wait, force gc (idle) - // - IO wait, panicwait - // - semacquire, semarelease - // - sleep, timer goroutine (idle) - // - trace reader (blocked) - // Stuck cases: - // - chan send (nil chan), chan receive (nil chan), select (no cases) - // Runnable states: - // - idle, runnable, running, syscall, waiting, dead, enqueue, copystack, - // Scan states: - // - scan, scanrunnable, scanrunning, scansyscall, scanwaiting, scandead, - // scanenqueue - State string - // Createdby is the goroutine which created this one, if applicable. - CreatedBy Call - // SleepMin is the wait time in minutes, if applicable. - SleepMin int - // SleepMax is the wait time in minutes, if applicable. - SleepMax int - // Stack is the call stack. - Stack Stack - // Locked is set if the goroutine was locked to an OS thread. - Locked bool -} - -// equal returns true only if both signatures are exactly equal. -func (s *Signature) equal(r *Signature) bool { - if s.State != r.State || !s.CreatedBy.equal(&r.CreatedBy) || s.Locked != r.Locked || s.SleepMin != r.SleepMin || s.SleepMax != r.SleepMax { - return false - } - return s.Stack.equal(&r.Stack) -} - -// similar returns true if the two Signature are equal or almost but not quite -// equal. -func (s *Signature) similar(r *Signature, similar Similarity) bool { - if s.State != r.State || !s.CreatedBy.similar(&r.CreatedBy, similar) { - return false - } - if similar == ExactFlags && s.Locked != r.Locked { - return false - } - return s.Stack.similar(&r.Stack, similar) -} - -// merge merges two similar Signature, zapping out differences. -func (s *Signature) merge(r *Signature) *Signature { - min := s.SleepMin - if r.SleepMin < min { - min = r.SleepMin - } - max := s.SleepMax - if r.SleepMax > max { - max = r.SleepMax - } - return &Signature{ - State: s.State, // Drop right side. - CreatedBy: s.CreatedBy, // Drop right side. - SleepMin: min, - SleepMax: max, - Stack: *s.Stack.merge(&r.Stack), - Locked: s.Locked || r.Locked, // TODO(maruel): This is weirdo. - } -} - -// less compares two Signature, where the ones that are less are more -// important, so they come up front. A Signature with more private functions is -// 'less' so it is at the top. Inversely, a Signature with only public -// functions is 'more' so it is at the bottom. -func (s *Signature) less(r *Signature) bool { - if s.Stack.less(&r.Stack) { - return true - } - if r.Stack.less(&s.Stack) { - return false - } - if s.Locked && !r.Locked { - return true - } - if r.Locked && !s.Locked { - return false - } - if s.State < r.State { - return true - } - if s.State > r.State { - return false - } - return false -} - -// SleepString returns a string "N-M minutes" if the goroutine(s) slept for a -// long time. -// -// Returns an empty string otherwise. -func (s *Signature) SleepString() string { - if s.SleepMax == 0 { - return "" - } - if s.SleepMin != s.SleepMax { - return fmt.Sprintf("%d~%d minutes", s.SleepMin, s.SleepMax) - } - return fmt.Sprintf("%d minutes", s.SleepMax) -} - -// CreatedByString return a short context about the origin of this goroutine -// signature. -// -// Deprecated: Format it yourself, will be removed in v2. -func (s *Signature) CreatedByString(fullPath bool) string { - created := s.CreatedBy.Func.PkgDotName() - if created == "" { - return "" - } - created += " @ " - if fullPath { - created += s.CreatedBy.FullSrcLine() - } else { - created += s.CreatedBy.SrcLine() - } - return created -} - -func (s *Signature) updateLocations(goroot, localgoroot, localgomod, gomodImportPath string, gopaths map[string]string) { - s.CreatedBy.updateLocations(goroot, localgoroot, localgomod, gomodImportPath, gopaths) - s.Stack.updateLocations(goroot, localgoroot, localgomod, gomodImportPath, gopaths) -} - -// Goroutine represents the state of one goroutine, including the stack trace. -type Goroutine struct { - // Signature is the stack trace, internal bits, state, which call site - // created it, etc. - Signature - // ID is the goroutine id. - ID int - // First is the goroutine first printed, normally the one that crashed. - First bool -} - -// Private stuff. - -// nameArguments is a post-processing step where Args are 'named' with numbers. -func nameArguments(goroutines []*Goroutine) { - // Set a name for any pointer occurring more than once. - type object struct { - args []*Arg - inPrimary bool - } - objects := map[uint64]object{} - // Enumerate all the arguments. - for i := range goroutines { - for j := range goroutines[i].Stack.Calls { - for k := range goroutines[i].Stack.Calls[j].Args.Values { - arg := goroutines[i].Stack.Calls[j].Args.Values[k] - if arg.IsPtr() { - objects[arg.Value] = object{ - args: append(objects[arg.Value].args, &goroutines[i].Stack.Calls[j].Args.Values[k]), - inPrimary: objects[arg.Value].inPrimary || i == 0, - } - } - } - } - // CreatedBy.Args is never set. - } - order := make(uint64Slice, 0, len(objects)/2) - for k, obj := range objects { - if len(obj.args) > 1 && obj.inPrimary { - order = append(order, k) - } - } - sort.Sort(order) - nextID := 1 - for _, k := range order { - for _, arg := range objects[k].args { - arg.Name = fmt.Sprintf("#%d", nextID) - } - nextID++ - } - - // Now do the rest. This is done so the output is deterministic. - order = make(uint64Slice, 0, len(objects)) - for k := range objects { - order = append(order, k) - } - sort.Sort(order) - for _, k := range order { - // Process the remaining pointers, they were not referenced by primary - // thread so will have higher IDs. - if objects[k].inPrimary { - continue - } - for _, arg := range objects[k].args { - arg.Name = fmt.Sprintf("#%d", nextID) - } - nextID++ - } -} - -func pathJoin(s ...string) string { - return strings.Join(s, "/") -} - -type uint64Slice []uint64 - -func (a uint64Slice) Len() int { return len(a) } -func (a uint64Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a uint64Slice) Less(i, j int) bool { return a[i] < a[j] } diff --git a/vendor/github.com/maruel/panicparse/stack/state_string.go b/vendor/github.com/maruel/panicparse/stack/state_string.go deleted file mode 100644 index 298b669..0000000 --- a/vendor/github.com/maruel/panicparse/stack/state_string.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated by "stringer -type state"; DO NOT EDIT. - -package stack - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[normal-0] - _ = x[betweenRoutine-1] - _ = x[gotRoutineHeader-2] - _ = x[gotFunc-3] - _ = x[gotCreated-4] - _ = x[gotFileFunc-5] - _ = x[gotFileCreated-6] - _ = x[gotUnavail-7] - _ = x[gotRaceHeader1-8] - _ = x[gotRaceHeader2-9] - _ = x[gotRaceOperationHeader-10] - _ = x[gotRaceOperationFunc-11] - _ = x[gotRaceOperationFile-12] - _ = x[betweenRaceOperations-13] - _ = x[gotRaceGoroutineHeader-14] - _ = x[gotRaceGoroutineFunc-15] - _ = x[gotRaceGoroutineFile-16] - _ = x[betweenRaceGoroutines-17] -} - -const _state_name = "normalbetweenRoutinegotRoutineHeadergotFuncgotCreatedgotFileFuncgotFileCreatedgotUnavailgotRaceHeader1gotRaceHeader2gotRaceOperationHeadergotRaceOperationFuncgotRaceOperationFilebetweenRaceOperationsgotRaceGoroutineHeadergotRaceGoroutineFuncgotRaceGoroutineFilebetweenRaceGoroutines" - -var _state_index = [...]uint16{0, 6, 20, 36, 43, 53, 64, 78, 88, 102, 116, 138, 158, 178, 199, 221, 241, 261, 282} - -func (i state) String() string { - if i < 0 || i >= state(len(_state_index)-1) { - return "state(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _state_name[_state_index[i]:_state_index[i+1]] -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 7fdf871..98fd331 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -105,7 +105,6 @@ github.com/jeandeaual/go-locale github.com/lucasb-eyer/go-colorful # github.com/maruel/panicparse v1.6.1 ## explicit; go 1.11 -github.com/maruel/panicparse/stack # github.com/mattn/go-colorable v0.1.9 ## explicit; go 1.13 github.com/mattn/go-colorable