diff options
| author | Taras Madan <tarasmadan@google.com> | 2025-01-22 16:07:17 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2025-01-23 10:42:36 +0000 |
| commit | 7b4377ad9d8a7205416df8d6217ef2b010f89481 (patch) | |
| tree | e6fec4fd12ff807a16d847923f501075bf71d16c /vendor/github.com/google/pprof | |
| parent | 475a4c203afb8b7d3af51c4fd32bb170ff32a45e (diff) | |
vendor: delete
Diffstat (limited to 'vendor/github.com/google/pprof')
| -rw-r--r-- | vendor/github.com/google/pprof/AUTHORS | 7 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/CONTRIBUTORS | 16 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/LICENSE | 202 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/encode.go | 596 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/filter.go | 274 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/index.go | 64 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/legacy_java_profile.go | 315 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/legacy_profile.go | 1228 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/merge.go | 674 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/profile.go | 869 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/proto.go | 367 | ||||
| -rw-r--r-- | vendor/github.com/google/pprof/profile/prune.go | 194 |
12 files changed, 0 insertions, 4806 deletions
diff --git a/vendor/github.com/google/pprof/AUTHORS b/vendor/github.com/google/pprof/AUTHORS deleted file mode 100644 index fd736cb1c..000000000 --- a/vendor/github.com/google/pprof/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -# This is the official list of pprof authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. -# Names should be added to this file as: -# Name or Organization <email address> -# The email address is not required for organizations. -Google Inc.
\ No newline at end of file diff --git a/vendor/github.com/google/pprof/CONTRIBUTORS b/vendor/github.com/google/pprof/CONTRIBUTORS deleted file mode 100644 index 8c8c37d2c..000000000 --- a/vendor/github.com/google/pprof/CONTRIBUTORS +++ /dev/null @@ -1,16 +0,0 @@ -# People who have agreed to one of the CLAs and can contribute patches. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# https://developers.google.com/open-source/cla/individual -# https://developers.google.com/open-source/cla/corporate -# -# Names should be added to this file as: -# Name <email address> -Raul Silvera <rsilvera@google.com> -Tipp Moseley <tipp@google.com> -Hyoun Kyu Cho <netforce@google.com> -Martin Spier <spiermar@gmail.com> -Taco de Wolff <tacodewolff@gmail.com> -Andrew Hunter <andrewhhunter@gmail.com> diff --git a/vendor/github.com/google/pprof/LICENSE b/vendor/github.com/google/pprof/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/vendor/github.com/google/pprof/LICENSE +++ /dev/null @@ -1,202 +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 [yyyy] [name of copyright owner] - - 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/google/pprof/profile/encode.go b/vendor/github.com/google/pprof/profile/encode.go deleted file mode 100644 index 8ce9d3cf3..000000000 --- a/vendor/github.com/google/pprof/profile/encode.go +++ /dev/null @@ -1,596 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -package profile - -import ( - "errors" - "sort" - "strings" -) - -func (p *Profile) decoder() []decoder { - return profileDecoder -} - -// preEncode populates the unexported fields to be used by encode -// (with suffix X) from the corresponding exported fields. The -// exported fields are cleared up to facilitate testing. -func (p *Profile) preEncode() { - strings := make(map[string]int) - addString(strings, "") - - for _, st := range p.SampleType { - st.typeX = addString(strings, st.Type) - st.unitX = addString(strings, st.Unit) - } - - for _, s := range p.Sample { - s.labelX = nil - var keys []string - for k := range s.Label { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - vs := s.Label[k] - for _, v := range vs { - s.labelX = append(s.labelX, - label{ - keyX: addString(strings, k), - strX: addString(strings, v), - }, - ) - } - } - var numKeys []string - for k := range s.NumLabel { - numKeys = append(numKeys, k) - } - sort.Strings(numKeys) - for _, k := range numKeys { - keyX := addString(strings, k) - vs := s.NumLabel[k] - units := s.NumUnit[k] - for i, v := range vs { - var unitX int64 - if len(units) != 0 { - unitX = addString(strings, units[i]) - } - s.labelX = append(s.labelX, - label{ - keyX: keyX, - numX: v, - unitX: unitX, - }, - ) - } - } - s.locationIDX = make([]uint64, len(s.Location)) - for i, loc := range s.Location { - s.locationIDX[i] = loc.ID - } - } - - for _, m := range p.Mapping { - m.fileX = addString(strings, m.File) - m.buildIDX = addString(strings, m.BuildID) - } - - for _, l := range p.Location { - for i, ln := range l.Line { - if ln.Function != nil { - l.Line[i].functionIDX = ln.Function.ID - } else { - l.Line[i].functionIDX = 0 - } - } - if l.Mapping != nil { - l.mappingIDX = l.Mapping.ID - } else { - l.mappingIDX = 0 - } - } - for _, f := range p.Function { - f.nameX = addString(strings, f.Name) - f.systemNameX = addString(strings, f.SystemName) - f.filenameX = addString(strings, f.Filename) - } - - p.dropFramesX = addString(strings, p.DropFrames) - p.keepFramesX = addString(strings, p.KeepFrames) - - if pt := p.PeriodType; pt != nil { - pt.typeX = addString(strings, pt.Type) - pt.unitX = addString(strings, pt.Unit) - } - - p.commentX = nil - for _, c := range p.Comments { - p.commentX = append(p.commentX, addString(strings, c)) - } - - p.defaultSampleTypeX = addString(strings, p.DefaultSampleType) - p.docURLX = addString(strings, p.DocURL) - - p.stringTable = make([]string, len(strings)) - for s, i := range strings { - p.stringTable[i] = s - } -} - -func (p *Profile) encode(b *buffer) { - for _, x := range p.SampleType { - encodeMessage(b, 1, x) - } - for _, x := range p.Sample { - encodeMessage(b, 2, x) - } - for _, x := range p.Mapping { - encodeMessage(b, 3, x) - } - for _, x := range p.Location { - encodeMessage(b, 4, x) - } - for _, x := range p.Function { - encodeMessage(b, 5, x) - } - encodeStrings(b, 6, p.stringTable) - encodeInt64Opt(b, 7, p.dropFramesX) - encodeInt64Opt(b, 8, p.keepFramesX) - encodeInt64Opt(b, 9, p.TimeNanos) - encodeInt64Opt(b, 10, p.DurationNanos) - if pt := p.PeriodType; pt != nil && (pt.typeX != 0 || pt.unitX != 0) { - encodeMessage(b, 11, p.PeriodType) - } - encodeInt64Opt(b, 12, p.Period) - encodeInt64s(b, 13, p.commentX) - encodeInt64(b, 14, p.defaultSampleTypeX) - encodeInt64Opt(b, 15, p.docURLX) -} - -var profileDecoder = []decoder{ - nil, // 0 - // repeated ValueType sample_type = 1 - func(b *buffer, m message) error { - x := new(ValueType) - pp := m.(*Profile) - pp.SampleType = append(pp.SampleType, x) - return decodeMessage(b, x) - }, - // repeated Sample sample = 2 - func(b *buffer, m message) error { - x := new(Sample) - pp := m.(*Profile) - pp.Sample = append(pp.Sample, x) - return decodeMessage(b, x) - }, - // repeated Mapping mapping = 3 - func(b *buffer, m message) error { - x := new(Mapping) - pp := m.(*Profile) - pp.Mapping = append(pp.Mapping, x) - return decodeMessage(b, x) - }, - // repeated Location location = 4 - func(b *buffer, m message) error { - x := new(Location) - x.Line = b.tmpLines[:0] // Use shared space temporarily - pp := m.(*Profile) - pp.Location = append(pp.Location, x) - err := decodeMessage(b, x) - b.tmpLines = x.Line[:0] - // Copy to shrink size and detach from shared space. - x.Line = append([]Line(nil), x.Line...) - return err - }, - // repeated Function function = 5 - func(b *buffer, m message) error { - x := new(Function) - pp := m.(*Profile) - pp.Function = append(pp.Function, x) - return decodeMessage(b, x) - }, - // repeated string string_table = 6 - func(b *buffer, m message) error { - err := decodeStrings(b, &m.(*Profile).stringTable) - if err != nil { - return err - } - if m.(*Profile).stringTable[0] != "" { - return errors.New("string_table[0] must be ''") - } - return nil - }, - // int64 drop_frames = 7 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).dropFramesX) }, - // int64 keep_frames = 8 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).keepFramesX) }, - // int64 time_nanos = 9 - func(b *buffer, m message) error { - if m.(*Profile).TimeNanos != 0 { - return errConcatProfile - } - return decodeInt64(b, &m.(*Profile).TimeNanos) - }, - // int64 duration_nanos = 10 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).DurationNanos) }, - // ValueType period_type = 11 - func(b *buffer, m message) error { - x := new(ValueType) - pp := m.(*Profile) - pp.PeriodType = x - return decodeMessage(b, x) - }, - // int64 period = 12 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) }, - // repeated int64 comment = 13 - func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) }, - // int64 defaultSampleType = 14 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) }, - // string doc_link = 15; - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).docURLX) }, -} - -// postDecode takes the unexported fields populated by decode (with -// suffix X) and populates the corresponding exported fields. -// The unexported fields are cleared up to facilitate testing. -func (p *Profile) postDecode() error { - var err error - mappings := make(map[uint64]*Mapping, len(p.Mapping)) - mappingIds := make([]*Mapping, len(p.Mapping)+1) - for _, m := range p.Mapping { - m.File, err = getString(p.stringTable, &m.fileX, err) - m.BuildID, err = getString(p.stringTable, &m.buildIDX, err) - if m.ID < uint64(len(mappingIds)) { - mappingIds[m.ID] = m - } else { - mappings[m.ID] = m - } - - // If this a main linux kernel mapping with a relocation symbol suffix - // ("[kernel.kallsyms]_text"), extract said suffix. - // It is fairly hacky to handle at this level, but the alternatives appear even worse. - const prefix = "[kernel.kallsyms]" - if strings.HasPrefix(m.File, prefix) { - m.KernelRelocationSymbol = m.File[len(prefix):] - } - } - - functions := make(map[uint64]*Function, len(p.Function)) - functionIds := make([]*Function, len(p.Function)+1) - for _, f := range p.Function { - f.Name, err = getString(p.stringTable, &f.nameX, err) - f.SystemName, err = getString(p.stringTable, &f.systemNameX, err) - f.Filename, err = getString(p.stringTable, &f.filenameX, err) - if f.ID < uint64(len(functionIds)) { - functionIds[f.ID] = f - } else { - functions[f.ID] = f - } - } - - locations := make(map[uint64]*Location, len(p.Location)) - locationIds := make([]*Location, len(p.Location)+1) - for _, l := range p.Location { - if id := l.mappingIDX; id < uint64(len(mappingIds)) { - l.Mapping = mappingIds[id] - } else { - l.Mapping = mappings[id] - } - l.mappingIDX = 0 - for i, ln := range l.Line { - if id := ln.functionIDX; id != 0 { - l.Line[i].functionIDX = 0 - if id < uint64(len(functionIds)) { - l.Line[i].Function = functionIds[id] - } else { - l.Line[i].Function = functions[id] - } - } - } - if l.ID < uint64(len(locationIds)) { - locationIds[l.ID] = l - } else { - locations[l.ID] = l - } - } - - for _, st := range p.SampleType { - st.Type, err = getString(p.stringTable, &st.typeX, err) - st.Unit, err = getString(p.stringTable, &st.unitX, err) - } - - // Pre-allocate space for all locations. - numLocations := 0 - for _, s := range p.Sample { - numLocations += len(s.locationIDX) - } - locBuffer := make([]*Location, numLocations) - - for _, s := range p.Sample { - if len(s.labelX) > 0 { - labels := make(map[string][]string, len(s.labelX)) - numLabels := make(map[string][]int64, len(s.labelX)) - numUnits := make(map[string][]string, len(s.labelX)) - for _, l := range s.labelX { - var key, value string - key, err = getString(p.stringTable, &l.keyX, err) - if l.strX != 0 { - value, err = getString(p.stringTable, &l.strX, err) - labels[key] = append(labels[key], value) - } else if l.numX != 0 || l.unitX != 0 { - numValues := numLabels[key] - units := numUnits[key] - if l.unitX != 0 { - var unit string - unit, err = getString(p.stringTable, &l.unitX, err) - units = padStringArray(units, len(numValues)) - numUnits[key] = append(units, unit) - } - numLabels[key] = append(numLabels[key], l.numX) - } - } - if len(labels) > 0 { - s.Label = labels - } - if len(numLabels) > 0 { - s.NumLabel = numLabels - for key, units := range numUnits { - if len(units) > 0 { - numUnits[key] = padStringArray(units, len(numLabels[key])) - } - } - s.NumUnit = numUnits - } - } - - s.Location = locBuffer[:len(s.locationIDX)] - locBuffer = locBuffer[len(s.locationIDX):] - for i, lid := range s.locationIDX { - if lid < uint64(len(locationIds)) { - s.Location[i] = locationIds[lid] - } else { - s.Location[i] = locations[lid] - } - } - s.locationIDX = nil - } - - p.DropFrames, err = getString(p.stringTable, &p.dropFramesX, err) - p.KeepFrames, err = getString(p.stringTable, &p.keepFramesX, err) - - if pt := p.PeriodType; pt == nil { - p.PeriodType = &ValueType{} - } - - if pt := p.PeriodType; pt != nil { - pt.Type, err = getString(p.stringTable, &pt.typeX, err) - pt.Unit, err = getString(p.stringTable, &pt.unitX, err) - } - - for _, i := range p.commentX { - var c string - c, err = getString(p.stringTable, &i, err) - p.Comments = append(p.Comments, c) - } - - p.commentX = nil - p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err) - p.DocURL, err = getString(p.stringTable, &p.docURLX, err) - p.stringTable = nil - return err -} - -// padStringArray pads arr with enough empty strings to make arr -// length l when arr's length is less than l. -func padStringArray(arr []string, l int) []string { - if l <= len(arr) { - return arr - } - return append(arr, make([]string, l-len(arr))...) -} - -func (p *ValueType) decoder() []decoder { - return valueTypeDecoder -} - -func (p *ValueType) encode(b *buffer) { - encodeInt64Opt(b, 1, p.typeX) - encodeInt64Opt(b, 2, p.unitX) -} - -var valueTypeDecoder = []decoder{ - nil, // 0 - // optional int64 type = 1 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).typeX) }, - // optional int64 unit = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*ValueType).unitX) }, -} - -func (p *Sample) decoder() []decoder { - return sampleDecoder -} - -func (p *Sample) encode(b *buffer) { - encodeUint64s(b, 1, p.locationIDX) - encodeInt64s(b, 2, p.Value) - for _, x := range p.labelX { - encodeMessage(b, 3, x) - } -} - -var sampleDecoder = []decoder{ - nil, // 0 - // repeated uint64 location = 1 - func(b *buffer, m message) error { return decodeUint64s(b, &m.(*Sample).locationIDX) }, - // repeated int64 value = 2 - func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Sample).Value) }, - // repeated Label label = 3 - func(b *buffer, m message) error { - s := m.(*Sample) - n := len(s.labelX) - s.labelX = append(s.labelX, label{}) - return decodeMessage(b, &s.labelX[n]) - }, -} - -func (p label) decoder() []decoder { - return labelDecoder -} - -func (p label) encode(b *buffer) { - encodeInt64Opt(b, 1, p.keyX) - encodeInt64Opt(b, 2, p.strX) - encodeInt64Opt(b, 3, p.numX) - encodeInt64Opt(b, 4, p.unitX) -} - -var labelDecoder = []decoder{ - nil, // 0 - // optional int64 key = 1 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).keyX) }, - // optional int64 str = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).strX) }, - // optional int64 num = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).numX) }, - // optional int64 num = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*label).unitX) }, -} - -func (p *Mapping) decoder() []decoder { - return mappingDecoder -} - -func (p *Mapping) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeUint64Opt(b, 2, p.Start) - encodeUint64Opt(b, 3, p.Limit) - encodeUint64Opt(b, 4, p.Offset) - encodeInt64Opt(b, 5, p.fileX) - encodeInt64Opt(b, 6, p.buildIDX) - encodeBoolOpt(b, 7, p.HasFunctions) - encodeBoolOpt(b, 8, p.HasFilenames) - encodeBoolOpt(b, 9, p.HasLineNumbers) - encodeBoolOpt(b, 10, p.HasInlineFrames) -} - -var mappingDecoder = []decoder{ - nil, // 0 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).ID) }, // optional uint64 id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Start) }, // optional uint64 memory_offset = 2 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Limit) }, // optional uint64 memory_limit = 3 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Mapping).Offset) }, // optional uint64 file_offset = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).fileX) }, // optional int64 filename = 5 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Mapping).buildIDX) }, // optional int64 build_id = 6 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFunctions) }, // optional bool has_functions = 7 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasFilenames) }, // optional bool has_filenames = 8 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasLineNumbers) }, // optional bool has_line_numbers = 9 - func(b *buffer, m message) error { return decodeBool(b, &m.(*Mapping).HasInlineFrames) }, // optional bool has_inline_frames = 10 -} - -func (p *Location) decoder() []decoder { - return locationDecoder -} - -func (p *Location) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeUint64Opt(b, 2, p.mappingIDX) - encodeUint64Opt(b, 3, p.Address) - for i := range p.Line { - encodeMessage(b, 4, &p.Line[i]) - } - encodeBoolOpt(b, 5, p.IsFolded) -} - -var locationDecoder = []decoder{ - nil, // 0 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).ID) }, // optional uint64 id = 1; - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).mappingIDX) }, // optional uint64 mapping_id = 2; - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Location).Address) }, // optional uint64 address = 3; - func(b *buffer, m message) error { // repeated Line line = 4 - pp := m.(*Location) - n := len(pp.Line) - pp.Line = append(pp.Line, Line{}) - return decodeMessage(b, &pp.Line[n]) - }, - func(b *buffer, m message) error { return decodeBool(b, &m.(*Location).IsFolded) }, // optional bool is_folded = 5; -} - -func (p *Line) decoder() []decoder { - return lineDecoder -} - -func (p *Line) encode(b *buffer) { - encodeUint64Opt(b, 1, p.functionIDX) - encodeInt64Opt(b, 2, p.Line) - encodeInt64Opt(b, 3, p.Column) -} - -var lineDecoder = []decoder{ - nil, // 0 - // optional uint64 function_id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Line).functionIDX) }, - // optional int64 line = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Line) }, - // optional int64 column = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Line).Column) }, -} - -func (p *Function) decoder() []decoder { - return functionDecoder -} - -func (p *Function) encode(b *buffer) { - encodeUint64Opt(b, 1, p.ID) - encodeInt64Opt(b, 2, p.nameX) - encodeInt64Opt(b, 3, p.systemNameX) - encodeInt64Opt(b, 4, p.filenameX) - encodeInt64Opt(b, 5, p.StartLine) -} - -var functionDecoder = []decoder{ - nil, // 0 - // optional uint64 id = 1 - func(b *buffer, m message) error { return decodeUint64(b, &m.(*Function).ID) }, - // optional int64 function_name = 2 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).nameX) }, - // optional int64 function_system_name = 3 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).systemNameX) }, - // repeated int64 filename = 4 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).filenameX) }, - // optional int64 start_line = 5 - func(b *buffer, m message) error { return decodeInt64(b, &m.(*Function).StartLine) }, -} - -func addString(strings map[string]int, s string) int64 { - i, ok := strings[s] - if !ok { - i = len(strings) - strings[s] = i - } - return int64(i) -} - -func getString(strings []string, strng *int64, err error) (string, error) { - if err != nil { - return "", err - } - s := int(*strng) - if s < 0 || s >= len(strings) { - return "", errMalformed - } - *strng = 0 - return strings[s], nil -} diff --git a/vendor/github.com/google/pprof/profile/filter.go b/vendor/github.com/google/pprof/profile/filter.go deleted file mode 100644 index c794b9390..000000000 --- a/vendor/github.com/google/pprof/profile/filter.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -package profile - -// Implements methods to filter samples from profiles. - -import "regexp" - -// FilterSamplesByName filters the samples in a profile and only keeps -// samples where at least one frame matches focus but none match ignore. -// Returns true is the corresponding regexp matched at least one sample. -func (p *Profile) FilterSamplesByName(focus, ignore, hide, show *regexp.Regexp) (fm, im, hm, hnm bool) { - if focus == nil && ignore == nil && hide == nil && show == nil { - fm = true // Missing focus implies a match - return - } - focusOrIgnore := make(map[uint64]bool) - hidden := make(map[uint64]bool) - for _, l := range p.Location { - if ignore != nil && l.matchesName(ignore) { - im = true - focusOrIgnore[l.ID] = false - } else if focus == nil || l.matchesName(focus) { - fm = true - focusOrIgnore[l.ID] = true - } - - if hide != nil && l.matchesName(hide) { - hm = true - l.Line = l.unmatchedLines(hide) - if len(l.Line) == 0 { - hidden[l.ID] = true - } - } - if show != nil { - l.Line = l.matchedLines(show) - if len(l.Line) == 0 { - hidden[l.ID] = true - } else { - hnm = true - } - } - } - - s := make([]*Sample, 0, len(p.Sample)) - for _, sample := range p.Sample { - if focusedAndNotIgnored(sample.Location, focusOrIgnore) { - if len(hidden) > 0 { - var locs []*Location - for _, loc := range sample.Location { - if !hidden[loc.ID] { - locs = append(locs, loc) - } - } - if len(locs) == 0 { - // Remove sample with no locations (by not adding it to s). - continue - } - sample.Location = locs - } - s = append(s, sample) - } - } - p.Sample = s - - return -} - -// ShowFrom drops all stack frames above the highest matching frame and returns -// whether a match was found. If showFrom is nil it returns false and does not -// modify the profile. -// -// Example: consider a sample with frames [A, B, C, B], where A is the root. -// ShowFrom(nil) returns false and has frames [A, B, C, B]. -// ShowFrom(A) returns true and has frames [A, B, C, B]. -// ShowFrom(B) returns true and has frames [B, C, B]. -// ShowFrom(C) returns true and has frames [C, B]. -// ShowFrom(D) returns false and drops the sample because no frames remain. -func (p *Profile) ShowFrom(showFrom *regexp.Regexp) (matched bool) { - if showFrom == nil { - return false - } - // showFromLocs stores location IDs that matched ShowFrom. - showFromLocs := make(map[uint64]bool) - // Apply to locations. - for _, loc := range p.Location { - if filterShowFromLocation(loc, showFrom) { - showFromLocs[loc.ID] = true - matched = true - } - } - // For all samples, strip locations after the highest matching one. - s := make([]*Sample, 0, len(p.Sample)) - for _, sample := range p.Sample { - for i := len(sample.Location) - 1; i >= 0; i-- { - if showFromLocs[sample.Location[i].ID] { - sample.Location = sample.Location[:i+1] - s = append(s, sample) - break - } - } - } - p.Sample = s - return matched -} - -// filterShowFromLocation tests a showFrom regex against a location, removes -// lines after the last match and returns whether a match was found. If the -// mapping is matched, then all lines are kept. -func filterShowFromLocation(loc *Location, showFrom *regexp.Regexp) bool { - if m := loc.Mapping; m != nil && showFrom.MatchString(m.File) { - return true - } - if i := loc.lastMatchedLineIndex(showFrom); i >= 0 { - loc.Line = loc.Line[:i+1] - return true - } - return false -} - -// lastMatchedLineIndex returns the index of the last line that matches a regex, -// or -1 if no match is found. -func (loc *Location) lastMatchedLineIndex(re *regexp.Regexp) int { - for i := len(loc.Line) - 1; i >= 0; i-- { - if fn := loc.Line[i].Function; fn != nil { - if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { - return i - } - } - } - return -1 -} - -// FilterTagsByName filters the tags in a profile and only keeps -// tags that match show and not hide. -func (p *Profile) FilterTagsByName(show, hide *regexp.Regexp) (sm, hm bool) { - matchRemove := func(name string) bool { - matchShow := show == nil || show.MatchString(name) - matchHide := hide != nil && hide.MatchString(name) - - if matchShow { - sm = true - } - if matchHide { - hm = true - } - return !matchShow || matchHide - } - for _, s := range p.Sample { - for lab := range s.Label { - if matchRemove(lab) { - delete(s.Label, lab) - } - } - for lab := range s.NumLabel { - if matchRemove(lab) { - delete(s.NumLabel, lab) - } - } - } - return -} - -// matchesName returns whether the location matches the regular -// expression. It checks any available function names, file names, and -// mapping object filename. -func (loc *Location) matchesName(re *regexp.Regexp) bool { - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { - return true - } - } - } - if m := loc.Mapping; m != nil && re.MatchString(m.File) { - return true - } - return false -} - -// unmatchedLines returns the lines in the location that do not match -// the regular expression. -func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { - if m := loc.Mapping; m != nil && re.MatchString(m.File) { - return nil - } - var lines []Line - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if re.MatchString(fn.Name) || re.MatchString(fn.Filename) { - continue - } - } - lines = append(lines, ln) - } - return lines -} - -// matchedLines returns the lines in the location that match -// the regular expression. -func (loc *Location) matchedLines(re *regexp.Regexp) []Line { - if m := loc.Mapping; m != nil && re.MatchString(m.File) { - return loc.Line - } - var lines []Line - for _, ln := range loc.Line { - if fn := ln.Function; fn != nil { - if !re.MatchString(fn.Name) && !re.MatchString(fn.Filename) { - continue - } - } - lines = append(lines, ln) - } - return lines -} - -// focusedAndNotIgnored looks up a slice of ids against a map of -// focused/ignored locations. The map only contains locations that are -// explicitly focused or ignored. Returns whether there is at least -// one focused location but no ignored locations. -func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { - var f bool - for _, loc := range locs { - if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { - if focus { - // Found focused location. Must keep searching in case there - // is an ignored one as well. - f = true - } else { - // Found ignored location. Can return false right away. - return false - } - } - } - return f -} - -// TagMatch selects tags for filtering -type TagMatch func(s *Sample) bool - -// FilterSamplesByTag removes all samples from the profile, except -// those that match focus and do not match the ignore regular -// expression. -func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { - samples := make([]*Sample, 0, len(p.Sample)) - for _, s := range p.Sample { - focused, ignored := true, false - if focus != nil { - focused = focus(s) - } - if ignore != nil { - ignored = ignore(s) - } - fm = fm || focused - im = im || ignored - if focused && !ignored { - samples = append(samples, s) - } - } - p.Sample = samples - return -} diff --git a/vendor/github.com/google/pprof/profile/index.go b/vendor/github.com/google/pprof/profile/index.go deleted file mode 100644 index bef1d6046..000000000 --- a/vendor/github.com/google/pprof/profile/index.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// 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. - -package profile - -import ( - "fmt" - "strconv" - "strings" -) - -// SampleIndexByName returns the appropriate index for a value of sample index. -// If numeric, it returns the number, otherwise it looks up the text in the -// profile sample types. -func (p *Profile) SampleIndexByName(sampleIndex string) (int, error) { - if sampleIndex == "" { - if dst := p.DefaultSampleType; dst != "" { - for i, t := range sampleTypes(p) { - if t == dst { - return i, nil - } - } - } - // By default select the last sample value - return len(p.SampleType) - 1, nil - } - if i, err := strconv.Atoi(sampleIndex); err == nil { - if i < 0 || i >= len(p.SampleType) { - return 0, fmt.Errorf("sample_index %s is outside the range [0..%d]", sampleIndex, len(p.SampleType)-1) - } - return i, nil - } - - // Remove the inuse_ prefix to support legacy pprof options - // "inuse_space" and "inuse_objects" for profiles containing types - // "space" and "objects". - noInuse := strings.TrimPrefix(sampleIndex, "inuse_") - for i, t := range p.SampleType { - if t.Type == sampleIndex || t.Type == noInuse { - return i, nil - } - } - - return 0, fmt.Errorf("sample_index %q must be one of: %v", sampleIndex, sampleTypes(p)) -} - -func sampleTypes(p *Profile) []string { - types := make([]string, len(p.SampleType)) - for i, t := range p.SampleType { - types[i] = t.Type - } - return types -} diff --git a/vendor/github.com/google/pprof/profile/legacy_java_profile.go b/vendor/github.com/google/pprof/profile/legacy_java_profile.go deleted file mode 100644 index 4580bab18..000000000 --- a/vendor/github.com/google/pprof/profile/legacy_java_profile.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -// This file implements parsers to convert java legacy profiles into -// the profile.proto format. - -package profile - -import ( - "bytes" - "fmt" - "io" - "path/filepath" - "regexp" - "strconv" - "strings" -) - -var ( - attributeRx = regexp.MustCompile(`([\w ]+)=([\w ]+)`) - javaSampleRx = regexp.MustCompile(` *(\d+) +(\d+) +@ +([ x0-9a-f]*)`) - javaLocationRx = regexp.MustCompile(`^\s*0x([[:xdigit:]]+)\s+(.*)\s*$`) - javaLocationFileLineRx = regexp.MustCompile(`^(.*)\s+\((.+):(-?[[:digit:]]+)\)$`) - javaLocationPathRx = regexp.MustCompile(`^(.*)\s+\((.*)\)$`) -) - -// javaCPUProfile returns a new Profile from profilez data. -// b is the profile bytes after the header, period is the profiling -// period, and parse is a function to parse 8-byte chunks from the -// profile in its native endianness. -func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { - p := &Profile{ - Period: period * 1000, - PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, - SampleType: []*ValueType{{Type: "samples", Unit: "count"}, {Type: "cpu", Unit: "nanoseconds"}}, - } - var err error - var locs map[uint64]*Location - if b, locs, err = parseCPUSamples(b, parse, false, p); err != nil { - return nil, err - } - - if err = parseJavaLocations(b, locs, p); err != nil { - return nil, err - } - - // Strip out addresses for better merge. - if err = p.Aggregate(true, true, true, true, false, false); err != nil { - return nil, err - } - - return p, nil -} - -// parseJavaProfile returns a new profile from heapz or contentionz -// data. b is the profile bytes after the header. -func parseJavaProfile(b []byte) (*Profile, error) { - h := bytes.SplitAfterN(b, []byte("\n"), 2) - if len(h) < 2 { - return nil, errUnrecognized - } - - p := &Profile{ - PeriodType: &ValueType{}, - } - header := string(bytes.TrimSpace(h[0])) - - var err error - var pType string - switch header { - case "--- heapz 1 ---": - pType = "heap" - case "--- contentionz 1 ---": - pType = "contention" - default: - return nil, errUnrecognized - } - - if b, err = parseJavaHeader(pType, h[1], p); err != nil { - return nil, err - } - var locs map[uint64]*Location - if b, locs, err = parseJavaSamples(pType, b, p); err != nil { - return nil, err - } - if err = parseJavaLocations(b, locs, p); err != nil { - return nil, err - } - - // Strip out addresses for better merge. - if err = p.Aggregate(true, true, true, true, false, false); err != nil { - return nil, err - } - - return p, nil -} - -// parseJavaHeader parses the attribute section on a java profile and -// populates a profile. Returns the remainder of the buffer after all -// attributes. -func parseJavaHeader(pType string, b []byte, p *Profile) ([]byte, error) { - nextNewLine := bytes.IndexByte(b, byte('\n')) - for nextNewLine != -1 { - line := string(bytes.TrimSpace(b[0:nextNewLine])) - if line != "" { - h := attributeRx.FindStringSubmatch(line) - if h == nil { - // Not a valid attribute, exit. - return b, nil - } - - attribute, value := strings.TrimSpace(h[1]), strings.TrimSpace(h[2]) - var err error - switch pType + "/" + attribute { - case "heap/format", "cpu/format", "contention/format": - if value != "java" { - return nil, errUnrecognized - } - case "heap/resolution": - p.SampleType = []*ValueType{ - {Type: "inuse_objects", Unit: "count"}, - {Type: "inuse_space", Unit: value}, - } - case "contention/resolution": - p.SampleType = []*ValueType{ - {Type: "contentions", Unit: "count"}, - {Type: "delay", Unit: value}, - } - case "contention/sampling period": - p.PeriodType = &ValueType{ - Type: "contentions", Unit: "count", - } - if p.Period, err = strconv.ParseInt(value, 0, 64); err != nil { - return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) - } - case "contention/ms since reset": - millis, err := strconv.ParseInt(value, 0, 64) - if err != nil { - return nil, fmt.Errorf("failed to parse attribute %s: %v", line, err) - } - p.DurationNanos = millis * 1000 * 1000 - default: - return nil, errUnrecognized - } - } - // Grab next line. - b = b[nextNewLine+1:] - nextNewLine = bytes.IndexByte(b, byte('\n')) - } - return b, nil -} - -// parseJavaSamples parses the samples from a java profile and -// populates the Samples in a profile. Returns the remainder of the -// buffer after the samples. -func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[uint64]*Location, error) { - nextNewLine := bytes.IndexByte(b, byte('\n')) - locs := make(map[uint64]*Location) - for nextNewLine != -1 { - line := string(bytes.TrimSpace(b[0:nextNewLine])) - if line != "" { - sample := javaSampleRx.FindStringSubmatch(line) - if sample == nil { - // Not a valid sample, exit. - return b, locs, nil - } - - // Java profiles have data/fields inverted compared to other - // profile types. - var err error - value1, value2, value3 := sample[2], sample[1], sample[3] - addrs, err := parseHexAddresses(value3) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - var sloc []*Location - for _, addr := range addrs { - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - s := &Sample{ - Value: make([]int64, 2), - Location: sloc, - } - - if s.Value[0], err = strconv.ParseInt(value1, 0, 64); err != nil { - return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) - } - if s.Value[1], err = strconv.ParseInt(value2, 0, 64); err != nil { - return nil, nil, fmt.Errorf("parsing sample %s: %v", line, err) - } - - switch pType { - case "heap": - const javaHeapzSamplingRate = 524288 // 512K - if s.Value[0] == 0 { - return nil, nil, fmt.Errorf("parsing sample %s: second value must be non-zero", line) - } - s.NumLabel = map[string][]int64{"bytes": {s.Value[1] / s.Value[0]}} - s.Value[0], s.Value[1] = scaleHeapSample(s.Value[0], s.Value[1], javaHeapzSamplingRate) - case "contention": - if period := p.Period; period != 0 { - s.Value[0] = s.Value[0] * p.Period - s.Value[1] = s.Value[1] * p.Period - } - } - p.Sample = append(p.Sample, s) - } - // Grab next line. - b = b[nextNewLine+1:] - nextNewLine = bytes.IndexByte(b, byte('\n')) - } - return b, locs, nil -} - -// parseJavaLocations parses the location information in a java -// profile and populates the Locations in a profile. It uses the -// location addresses from the profile as both the ID of each -// location. -func parseJavaLocations(b []byte, locs map[uint64]*Location, p *Profile) error { - r := bytes.NewBuffer(b) - fns := make(map[string]*Function) - for { - line, err := r.ReadString('\n') - if err != nil { - if err != io.EOF { - return err - } - if line == "" { - break - } - } - - if line = strings.TrimSpace(line); line == "" { - continue - } - - jloc := javaLocationRx.FindStringSubmatch(line) - if len(jloc) != 3 { - continue - } - addr, err := strconv.ParseUint(jloc[1], 16, 64) - if err != nil { - return fmt.Errorf("parsing sample %s: %v", line, err) - } - loc := locs[addr] - if loc == nil { - // Unused/unseen - continue - } - var lineFunc, lineFile string - var lineNo int64 - - if fileLine := javaLocationFileLineRx.FindStringSubmatch(jloc[2]); len(fileLine) == 4 { - // Found a line of the form: "function (file:line)" - lineFunc, lineFile = fileLine[1], fileLine[2] - if n, err := strconv.ParseInt(fileLine[3], 10, 64); err == nil && n > 0 { - lineNo = n - } - } else if filePath := javaLocationPathRx.FindStringSubmatch(jloc[2]); len(filePath) == 3 { - // If there's not a file:line, it's a shared library path. - // The path isn't interesting, so just give the .so. - lineFunc, lineFile = filePath[1], filepath.Base(filePath[2]) - } else if strings.Contains(jloc[2], "generated stub/JIT") { - lineFunc = "STUB" - } else { - // Treat whole line as the function name. This is used by the - // java agent for internal states such as "GC" or "VM". - lineFunc = jloc[2] - } - fn := fns[lineFunc] - - if fn == nil { - fn = &Function{ - Name: lineFunc, - SystemName: lineFunc, - Filename: lineFile, - } - fns[lineFunc] = fn - p.Function = append(p.Function, fn) - } - loc.Line = []Line{ - { - Function: fn, - Line: lineNo, - }, - } - loc.Address = 0 - } - - p.remapLocationIDs() - p.remapFunctionIDs() - p.remapMappingIDs() - - return nil -} diff --git a/vendor/github.com/google/pprof/profile/legacy_profile.go b/vendor/github.com/google/pprof/profile/legacy_profile.go deleted file mode 100644 index 8d07fd6c2..000000000 --- a/vendor/github.com/google/pprof/profile/legacy_profile.go +++ /dev/null @@ -1,1228 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -// This file implements parsers to convert legacy profiles into the -// profile.proto format. - -package profile - -import ( - "bufio" - "bytes" - "fmt" - "io" - "math" - "regexp" - "strconv" - "strings" -) - -var ( - countStartRE = regexp.MustCompile(`\A(\S+) profile: total \d+\z`) - countRE = regexp.MustCompile(`\A(\d+) @(( 0x[0-9a-f]+)+)\z`) - - heapHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] *@ *(heap[_a-z0-9]*)/?(\d*)`) - heapSampleRE = regexp.MustCompile(`(-?\d+): *(-?\d+) *\[ *(\d+): *(\d+) *] @([ x0-9a-f]*)`) - - contentionSampleRE = regexp.MustCompile(`(\d+) *(\d+) @([ x0-9a-f]*)`) - - hexNumberRE = regexp.MustCompile(`0x[0-9a-f]+`) - - growthHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ growthz?`) - - fragmentationHeaderRE = regexp.MustCompile(`heap profile: *(\d+): *(\d+) *\[ *(\d+): *(\d+) *\] @ fragmentationz?`) - - threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`) - threadStartRE = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`) - - // Regular expressions to parse process mappings. Support the format used by Linux /proc/.../maps and other tools. - // Recommended format: - // Start End object file name offset(optional) linker build id - // 0x40000-0x80000 /path/to/binary (@FF00) abc123456 - spaceDigits = `\s+[[:digit:]]+` - hexPair = `\s+[[:xdigit:]]+:[[:xdigit:]]+` - oSpace = `\s*` - // Capturing expressions. - cHex = `(?:0x)?([[:xdigit:]]+)` - cHexRange = `\s*` + cHex + `[\s-]?` + oSpace + cHex + `:?` - cSpaceString = `(?:\s+(\S+))?` - cSpaceHex = `(?:\s+([[:xdigit:]]+))?` - cSpaceAtOffset = `(?:\s+\(@([[:xdigit:]]+)\))?` - cPerm = `(?:\s+([-rwxp]+))?` - - procMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceHex + hexPair + spaceDigits + cSpaceString) - briefMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceString + cSpaceAtOffset + cSpaceHex) - - // Regular expression to parse log data, of the form: - // ... file:line] msg... - logInfoRE = regexp.MustCompile(`^[^\[\]]+:[0-9]+]\s`) -) - -func isSpaceOrComment(line string) bool { - trimmed := strings.TrimSpace(line) - return len(trimmed) == 0 || trimmed[0] == '#' -} - -// parseGoCount parses a Go count profile (e.g., threadcreate or -// goroutine) and returns a new Profile. -func parseGoCount(b []byte) (*Profile, error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - // Skip comments at the beginning of the file. - for s.Scan() && isSpaceOrComment(s.Text()) { - } - if err := s.Err(); err != nil { - return nil, err - } - m := countStartRE.FindStringSubmatch(s.Text()) - if m == nil { - return nil, errUnrecognized - } - profileType := m[1] - p := &Profile{ - PeriodType: &ValueType{Type: profileType, Unit: "count"}, - Period: 1, - SampleType: []*ValueType{{Type: profileType, Unit: "count"}}, - } - locations := make(map[uint64]*Location) - for s.Scan() { - line := s.Text() - if isSpaceOrComment(line) { - continue - } - if strings.HasPrefix(line, "---") { - break - } - m := countRE.FindStringSubmatch(line) - if m == nil { - return nil, errMalformed - } - n, err := strconv.ParseInt(m[1], 0, 64) - if err != nil { - return nil, errMalformed - } - fields := strings.Fields(m[2]) - locs := make([]*Location, 0, len(fields)) - for _, stk := range fields { - addr, err := strconv.ParseUint(stk, 0, 64) - if err != nil { - return nil, errMalformed - } - // Adjust all frames by -1 to land on top of the call instruction. - addr-- - loc := locations[addr] - if loc == nil { - loc = &Location{ - Address: addr, - } - locations[addr] = loc - p.Location = append(p.Location, loc) - } - locs = append(locs, loc) - } - p.Sample = append(p.Sample, &Sample{ - Location: locs, - Value: []int64{n}, - }) - } - if err := s.Err(); err != nil { - return nil, err - } - - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - return p, nil -} - -// remapLocationIDs ensures there is a location for each address -// referenced by a sample, and remaps the samples to point to the new -// location ids. -func (p *Profile) remapLocationIDs() { - seen := make(map[*Location]bool, len(p.Location)) - var locs []*Location - - for _, s := range p.Sample { - for _, l := range s.Location { - if seen[l] { - continue - } - l.ID = uint64(len(locs) + 1) - locs = append(locs, l) - seen[l] = true - } - } - p.Location = locs -} - -func (p *Profile) remapFunctionIDs() { - seen := make(map[*Function]bool, len(p.Function)) - var fns []*Function - - for _, l := range p.Location { - for _, ln := range l.Line { - fn := ln.Function - if fn == nil || seen[fn] { - continue - } - fn.ID = uint64(len(fns) + 1) - fns = append(fns, fn) - seen[fn] = true - } - } - p.Function = fns -} - -// remapMappingIDs matches location addresses with existing mappings -// and updates them appropriately. This is O(N*M), if this ever shows -// up as a bottleneck, evaluate sorting the mappings and doing a -// binary search, which would make it O(N*log(M)). -func (p *Profile) remapMappingIDs() { - // Some profile handlers will incorrectly set regions for the main - // executable if its section is remapped. Fix them through heuristics. - - if len(p.Mapping) > 0 { - // Remove the initial mapping if named '/anon_hugepage' and has a - // consecutive adjacent mapping. - if m := p.Mapping[0]; strings.HasPrefix(m.File, "/anon_hugepage") { - if len(p.Mapping) > 1 && m.Limit == p.Mapping[1].Start { - p.Mapping = p.Mapping[1:] - } - } - } - - // Subtract the offset from the start of the main mapping if it - // ends up at a recognizable start address. - if len(p.Mapping) > 0 { - const expectedStart = 0x400000 - if m := p.Mapping[0]; m.Start-m.Offset == expectedStart { - m.Start = expectedStart - m.Offset = 0 - } - } - - // Associate each location with an address to the corresponding - // mapping. Create fake mapping if a suitable one isn't found. - var fake *Mapping -nextLocation: - for _, l := range p.Location { - a := l.Address - if l.Mapping != nil || a == 0 { - continue - } - for _, m := range p.Mapping { - if m.Start <= a && a < m.Limit { - l.Mapping = m - continue nextLocation - } - } - // Work around legacy handlers failing to encode the first - // part of mappings split into adjacent ranges. - for _, m := range p.Mapping { - if m.Offset != 0 && m.Start-m.Offset <= a && a < m.Start { - m.Start -= m.Offset - m.Offset = 0 - l.Mapping = m - continue nextLocation - } - } - // If there is still no mapping, create a fake one. - // This is important for the Go legacy handler, which produced - // no mappings. - if fake == nil { - fake = &Mapping{ - ID: 1, - Limit: ^uint64(0), - } - p.Mapping = append(p.Mapping, fake) - } - l.Mapping = fake - } - - // Reset all mapping IDs. - for i, m := range p.Mapping { - m.ID = uint64(i + 1) - } -} - -var cpuInts = []func([]byte) (uint64, []byte){ - get32l, - get32b, - get64l, - get64b, -} - -func get32l(b []byte) (uint64, []byte) { - if len(b) < 4 { - return 0, nil - } - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24, b[4:] -} - -func get32b(b []byte) (uint64, []byte) { - if len(b) < 4 { - return 0, nil - } - return uint64(b[3]) | uint64(b[2])<<8 | uint64(b[1])<<16 | uint64(b[0])<<24, b[4:] -} - -func get64l(b []byte) (uint64, []byte) { - if len(b) < 8 { - return 0, nil - } - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56, b[8:] -} - -func get64b(b []byte) (uint64, []byte) { - if len(b) < 8 { - return 0, nil - } - return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56, b[8:] -} - -// parseCPU parses a profilez legacy profile and returns a newly -// populated Profile. -// -// The general format for profilez samples is a sequence of words in -// binary format. The first words are a header with the following data: -// -// 1st word -- 0 -// 2nd word -- 3 -// 3rd word -- 0 if a c++ application, 1 if a java application. -// 4th word -- Sampling period (in microseconds). -// 5th word -- Padding. -func parseCPU(b []byte) (*Profile, error) { - var parse func([]byte) (uint64, []byte) - var n1, n2, n3, n4, n5 uint64 - for _, parse = range cpuInts { - var tmp []byte - n1, tmp = parse(b) - n2, tmp = parse(tmp) - n3, tmp = parse(tmp) - n4, tmp = parse(tmp) - n5, tmp = parse(tmp) - - if tmp != nil && n1 == 0 && n2 == 3 && n3 == 0 && n4 > 0 && n5 == 0 { - b = tmp - return cpuProfile(b, int64(n4), parse) - } - if tmp != nil && n1 == 0 && n2 == 3 && n3 == 1 && n4 > 0 && n5 == 0 { - b = tmp - return javaCPUProfile(b, int64(n4), parse) - } - } - return nil, errUnrecognized -} - -// cpuProfile returns a new Profile from C++ profilez data. -// b is the profile bytes after the header, period is the profiling -// period, and parse is a function to parse 8-byte chunks from the -// profile in its native endianness. -func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (*Profile, error) { - p := &Profile{ - Period: period * 1000, - PeriodType: &ValueType{Type: "cpu", Unit: "nanoseconds"}, - SampleType: []*ValueType{ - {Type: "samples", Unit: "count"}, - {Type: "cpu", Unit: "nanoseconds"}, - }, - } - var err error - if b, _, err = parseCPUSamples(b, parse, true, p); err != nil { - return nil, err - } - - // If *most* samples have the same second-to-the-bottom frame, it - // strongly suggests that it is an uninteresting artifact of - // measurement -- a stack frame pushed by the signal handler. The - // bottom frame is always correct as it is picked up from the signal - // structure, not the stack. Check if this is the case and if so, - // remove. - - // Remove up to two frames. - maxiter := 2 - // Allow one different sample for this many samples with the same - // second-to-last frame. - similarSamples := 32 - margin := len(p.Sample) / similarSamples - - for iter := 0; iter < maxiter; iter++ { - addr1 := make(map[uint64]int) - for _, s := range p.Sample { - if len(s.Location) > 1 { - a := s.Location[1].Address - addr1[a] = addr1[a] + 1 - } - } - - for id1, count := range addr1 { - if count >= len(p.Sample)-margin { - // Found uninteresting frame, strip it out from all samples - for _, s := range p.Sample { - if len(s.Location) > 1 && s.Location[1].Address == id1 { - s.Location = append(s.Location[:1], s.Location[2:]...) - } - } - break - } - } - } - - if err := p.ParseMemoryMap(bytes.NewBuffer(b)); err != nil { - return nil, err - } - - cleanupDuplicateLocations(p) - return p, nil -} - -func cleanupDuplicateLocations(p *Profile) { - // The profile handler may duplicate the leaf frame, because it gets - // its address both from stack unwinding and from the signal - // context. Detect this and delete the duplicate, which has been - // adjusted by -1. The leaf address should not be adjusted as it is - // not a call. - for _, s := range p.Sample { - if len(s.Location) > 1 && s.Location[0].Address == s.Location[1].Address+1 { - s.Location = append(s.Location[:1], s.Location[2:]...) - } - } -} - -// parseCPUSamples parses a collection of profilez samples from a -// profile. -// -// profilez samples are a repeated sequence of stack frames of the -// form: -// -// 1st word -- The number of times this stack was encountered. -// 2nd word -- The size of the stack (StackSize). -// 3rd word -- The first address on the stack. -// ... -// StackSize + 2 -- The last address on the stack -// -// The last stack trace is of the form: -// -// 1st word -- 0 -// 2nd word -- 1 -// 3rd word -- 0 -// -// Addresses from stack traces may point to the next instruction after -// each call. Optionally adjust by -1 to land somewhere on the actual -// call (except for the leaf, which is not a call). -func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), adjust bool, p *Profile) ([]byte, map[uint64]*Location, error) { - locs := make(map[uint64]*Location) - for len(b) > 0 { - var count, nstk uint64 - count, b = parse(b) - nstk, b = parse(b) - if b == nil || nstk > uint64(len(b)/4) { - return nil, nil, errUnrecognized - } - var sloc []*Location - addrs := make([]uint64, nstk) - for i := 0; i < int(nstk); i++ { - addrs[i], b = parse(b) - } - - if count == 0 && nstk == 1 && addrs[0] == 0 { - // End of data marker - break - } - for i, addr := range addrs { - if adjust && i > 0 { - addr-- - } - loc := locs[addr] - if loc == nil { - loc = &Location{ - Address: addr, - } - locs[addr] = loc - p.Location = append(p.Location, loc) - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, - &Sample{ - Value: []int64{int64(count), int64(count) * p.Period}, - Location: sloc, - }) - } - // Reached the end without finding the EOD marker. - return b, locs, nil -} - -// parseHeap parses a heapz legacy or a growthz profile and -// returns a newly populated Profile. -func parseHeap(b []byte) (p *Profile, err error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - if !s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } - return nil, errUnrecognized - } - p = &Profile{} - - sampling := "" - hasAlloc := false - - line := s.Text() - p.PeriodType = &ValueType{Type: "space", Unit: "bytes"} - if header := heapHeaderRE.FindStringSubmatch(line); header != nil { - sampling, p.Period, hasAlloc, err = parseHeapHeader(line) - if err != nil { - return nil, err - } - } else if header = growthHeaderRE.FindStringSubmatch(line); header != nil { - p.Period = 1 - } else if header = fragmentationHeaderRE.FindStringSubmatch(line); header != nil { - p.Period = 1 - } else { - return nil, errUnrecognized - } - - if hasAlloc { - // Put alloc before inuse so that default pprof selection - // will prefer inuse_space. - p.SampleType = []*ValueType{ - {Type: "alloc_objects", Unit: "count"}, - {Type: "alloc_space", Unit: "bytes"}, - {Type: "inuse_objects", Unit: "count"}, - {Type: "inuse_space", Unit: "bytes"}, - } - } else { - p.SampleType = []*ValueType{ - {Type: "objects", Unit: "count"}, - {Type: "space", Unit: "bytes"}, - } - } - - locs := make(map[uint64]*Location) - for s.Scan() { - line := strings.TrimSpace(s.Text()) - - if isSpaceOrComment(line) { - continue - } - - if isMemoryMapSentinel(line) { - break - } - - value, blocksize, addrs, err := parseHeapSample(line, p.Period, sampling, hasAlloc) - if err != nil { - return nil, err - } - - var sloc []*Location - for _, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call. - addr-- - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - p.Sample = append(p.Sample, &Sample{ - Value: value, - Location: sloc, - NumLabel: map[string][]int64{"bytes": {blocksize}}, - }) - } - if err := s.Err(); err != nil { - return nil, err - } - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - return p, nil -} - -func parseHeapHeader(line string) (sampling string, period int64, hasAlloc bool, err error) { - header := heapHeaderRE.FindStringSubmatch(line) - if header == nil { - return "", 0, false, errUnrecognized - } - - if len(header[6]) > 0 { - if period, err = strconv.ParseInt(header[6], 10, 64); err != nil { - return "", 0, false, errUnrecognized - } - } - - if (header[3] != header[1] && header[3] != "0") || (header[4] != header[2] && header[4] != "0") { - hasAlloc = true - } - - switch header[5] { - case "heapz_v2", "heap_v2": - return "v2", period, hasAlloc, nil - case "heapprofile": - return "", 1, hasAlloc, nil - case "heap": - return "v2", period / 2, hasAlloc, nil - default: - return "", 0, false, errUnrecognized - } -} - -// parseHeapSample parses a single row from a heap profile into a new Sample. -func parseHeapSample(line string, rate int64, sampling string, includeAlloc bool) (value []int64, blocksize int64, addrs []uint64, err error) { - sampleData := heapSampleRE.FindStringSubmatch(line) - if len(sampleData) != 6 { - return nil, 0, nil, fmt.Errorf("unexpected number of sample values: got %d, want 6", len(sampleData)) - } - - // This is a local-scoped helper function to avoid needing to pass - // around rate, sampling and many return parameters. - addValues := func(countString, sizeString string, label string) error { - count, err := strconv.ParseInt(countString, 10, 64) - if err != nil { - return fmt.Errorf("malformed sample: %s: %v", line, err) - } - size, err := strconv.ParseInt(sizeString, 10, 64) - if err != nil { - return fmt.Errorf("malformed sample: %s: %v", line, err) - } - if count == 0 && size != 0 { - return fmt.Errorf("%s count was 0 but %s bytes was %d", label, label, size) - } - if count != 0 { - blocksize = size / count - if sampling == "v2" { - count, size = scaleHeapSample(count, size, rate) - } - } - value = append(value, count, size) - return nil - } - - if includeAlloc { - if err := addValues(sampleData[3], sampleData[4], "allocation"); err != nil { - return nil, 0, nil, err - } - } - - if err := addValues(sampleData[1], sampleData[2], "inuse"); err != nil { - return nil, 0, nil, err - } - - addrs, err = parseHexAddresses(sampleData[5]) - if err != nil { - return nil, 0, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - return value, blocksize, addrs, nil -} - -// parseHexAddresses extracts hex numbers from a string, attempts to convert -// each to an unsigned 64-bit number and returns the resulting numbers as a -// slice, or an error if the string contains hex numbers which are too large to -// handle (which means a malformed profile). -func parseHexAddresses(s string) ([]uint64, error) { - hexStrings := hexNumberRE.FindAllString(s, -1) - var addrs []uint64 - for _, s := range hexStrings { - if addr, err := strconv.ParseUint(s, 0, 64); err == nil { - addrs = append(addrs, addr) - } else { - return nil, fmt.Errorf("failed to parse as hex 64-bit number: %s", s) - } - } - return addrs, nil -} - -// scaleHeapSample adjusts the data from a heapz Sample to -// account for its probability of appearing in the collected -// data. heapz profiles are a sampling of the memory allocations -// requests in a program. We estimate the unsampled value by dividing -// each collected sample by its probability of appearing in the -// profile. heapz v2 profiles rely on a poisson process to determine -// which samples to collect, based on the desired average collection -// rate R. The probability of a sample of size S to appear in that -// profile is 1-exp(-S/R). -func scaleHeapSample(count, size, rate int64) (int64, int64) { - if count == 0 || size == 0 { - return 0, 0 - } - - if rate <= 1 { - // if rate==1 all samples were collected so no adjustment is needed. - // if rate<1 treat as unknown and skip scaling. - return count, size - } - - avgSize := float64(size) / float64(count) - scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) - - return int64(float64(count) * scale), int64(float64(size) * scale) -} - -// parseContention parses a mutex or contention profile. There are 2 cases: -// "--- contentionz " for legacy C++ profiles (and backwards compatibility) -// "--- mutex:" or "--- contention:" for profiles generated by the Go runtime. -func parseContention(b []byte) (*Profile, error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - if !s.Scan() { - if err := s.Err(); err != nil { - return nil, err - } - return nil, errUnrecognized - } - - switch l := s.Text(); { - case strings.HasPrefix(l, "--- contentionz "): - case strings.HasPrefix(l, "--- mutex:"): - case strings.HasPrefix(l, "--- contention:"): - default: - return nil, errUnrecognized - } - - p := &Profile{ - PeriodType: &ValueType{Type: "contentions", Unit: "count"}, - Period: 1, - SampleType: []*ValueType{ - {Type: "contentions", Unit: "count"}, - {Type: "delay", Unit: "nanoseconds"}, - }, - } - - var cpuHz int64 - // Parse text of the form "attribute = value" before the samples. - const delimiter = "=" - for s.Scan() { - line := s.Text() - if line = strings.TrimSpace(line); isSpaceOrComment(line) { - continue - } - if strings.HasPrefix(line, "---") { - break - } - attr := strings.SplitN(line, delimiter, 2) - if len(attr) != 2 { - break - } - key, val := strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1]) - var err error - switch key { - case "cycles/second": - if cpuHz, err = strconv.ParseInt(val, 0, 64); err != nil { - return nil, errUnrecognized - } - case "sampling period": - if p.Period, err = strconv.ParseInt(val, 0, 64); err != nil { - return nil, errUnrecognized - } - case "ms since reset": - ms, err := strconv.ParseInt(val, 0, 64) - if err != nil { - return nil, errUnrecognized - } - p.DurationNanos = ms * 1000 * 1000 - case "format": - // CPP contentionz profiles don't have format. - return nil, errUnrecognized - case "resolution": - // CPP contentionz profiles don't have resolution. - return nil, errUnrecognized - case "discarded samples": - default: - return nil, errUnrecognized - } - } - if err := s.Err(); err != nil { - return nil, err - } - - locs := make(map[uint64]*Location) - for { - line := strings.TrimSpace(s.Text()) - if strings.HasPrefix(line, "---") { - break - } - if !isSpaceOrComment(line) { - value, addrs, err := parseContentionSample(line, p.Period, cpuHz) - if err != nil { - return nil, err - } - var sloc []*Location - for _, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call. - addr-- - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - p.Sample = append(p.Sample, &Sample{ - Value: value, - Location: sloc, - }) - } - if !s.Scan() { - break - } - } - if err := s.Err(); err != nil { - return nil, err - } - - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - - return p, nil -} - -// parseContentionSample parses a single row from a contention profile -// into a new Sample. -func parseContentionSample(line string, period, cpuHz int64) (value []int64, addrs []uint64, err error) { - sampleData := contentionSampleRE.FindStringSubmatch(line) - if sampleData == nil { - return nil, nil, errUnrecognized - } - - v1, err := strconv.ParseInt(sampleData[1], 10, 64) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - v2, err := strconv.ParseInt(sampleData[2], 10, 64) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - // Unsample values if period and cpuHz are available. - // - Delays are scaled to cycles and then to nanoseconds. - // - Contentions are scaled to cycles. - if period > 0 { - if cpuHz > 0 { - cpuGHz := float64(cpuHz) / 1e9 - v1 = int64(float64(v1) * float64(period) / cpuGHz) - } - v2 = v2 * period - } - - value = []int64{v2, v1} - addrs, err = parseHexAddresses(sampleData[3]) - if err != nil { - return nil, nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - - return value, addrs, nil -} - -// parseThread parses a Threadz profile and returns a new Profile. -func parseThread(b []byte) (*Profile, error) { - s := bufio.NewScanner(bytes.NewBuffer(b)) - // Skip past comments and empty lines seeking a real header. - for s.Scan() && isSpaceOrComment(s.Text()) { - } - - line := s.Text() - if m := threadzStartRE.FindStringSubmatch(line); m != nil { - // Advance over initial comments until first stack trace. - for s.Scan() { - if line = s.Text(); isMemoryMapSentinel(line) || strings.HasPrefix(line, "-") { - break - } - } - } else if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { - return nil, errUnrecognized - } - - p := &Profile{ - SampleType: []*ValueType{{Type: "thread", Unit: "count"}}, - PeriodType: &ValueType{Type: "thread", Unit: "count"}, - Period: 1, - } - - locs := make(map[uint64]*Location) - // Recognize each thread and populate profile samples. - for !isMemoryMapSentinel(line) { - if strings.HasPrefix(line, "---- no stack trace for") { - break - } - if t := threadStartRE.FindStringSubmatch(line); len(t) != 4 { - return nil, errUnrecognized - } - - var addrs []uint64 - var err error - line, addrs, err = parseThreadSample(s) - if err != nil { - return nil, err - } - if len(addrs) == 0 { - // We got a --same as previous threads--. Bump counters. - if len(p.Sample) > 0 { - s := p.Sample[len(p.Sample)-1] - s.Value[0]++ - } - continue - } - - var sloc []*Location - for i, addr := range addrs { - // Addresses from stack traces point to the next instruction after - // each call. Adjust by -1 to land somewhere on the actual call - // (except for the leaf, which is not a call). - if i > 0 { - addr-- - } - loc := locs[addr] - if locs[addr] == nil { - loc = &Location{ - Address: addr, - } - p.Location = append(p.Location, loc) - locs[addr] = loc - } - sloc = append(sloc, loc) - } - - p.Sample = append(p.Sample, &Sample{ - Value: []int64{1}, - Location: sloc, - }) - } - - if err := parseAdditionalSections(s, p); err != nil { - return nil, err - } - - cleanupDuplicateLocations(p) - return p, nil -} - -// parseThreadSample parses a symbolized or unsymbolized stack trace. -// Returns the first line after the traceback, the sample (or nil if -// it hits a 'same-as-previous' marker) and an error. -func parseThreadSample(s *bufio.Scanner) (nextl string, addrs []uint64, err error) { - var line string - sameAsPrevious := false - for s.Scan() { - line = strings.TrimSpace(s.Text()) - if line == "" { - continue - } - - if strings.HasPrefix(line, "---") { - break - } - if strings.Contains(line, "same as previous thread") { - sameAsPrevious = true - continue - } - - curAddrs, err := parseHexAddresses(line) - if err != nil { - return "", nil, fmt.Errorf("malformed sample: %s: %v", line, err) - } - addrs = append(addrs, curAddrs...) - } - if err := s.Err(); err != nil { - return "", nil, err - } - if sameAsPrevious { - return line, nil, nil - } - return line, addrs, nil -} - -// parseAdditionalSections parses any additional sections in the -// profile, ignoring any unrecognized sections. -func parseAdditionalSections(s *bufio.Scanner, p *Profile) error { - for !isMemoryMapSentinel(s.Text()) && s.Scan() { - } - if err := s.Err(); err != nil { - return err - } - return p.ParseMemoryMapFromScanner(s) -} - -// ParseProcMaps parses a memory map in the format of /proc/self/maps. -// ParseMemoryMap should be called after setting on a profile to -// associate locations to the corresponding mapping based on their -// address. -func ParseProcMaps(rd io.Reader) ([]*Mapping, error) { - s := bufio.NewScanner(rd) - return parseProcMapsFromScanner(s) -} - -func parseProcMapsFromScanner(s *bufio.Scanner) ([]*Mapping, error) { - var mapping []*Mapping - - var attrs []string - const delimiter = "=" - r := strings.NewReplacer() - for s.Scan() { - line := r.Replace(removeLoggingInfo(s.Text())) - m, err := parseMappingEntry(line) - if err != nil { - if err == errUnrecognized { - // Recognize assignments of the form: attr=value, and replace - // $attr with value on subsequent mappings. - if attr := strings.SplitN(line, delimiter, 2); len(attr) == 2 { - attrs = append(attrs, "$"+strings.TrimSpace(attr[0]), strings.TrimSpace(attr[1])) - r = strings.NewReplacer(attrs...) - } - // Ignore any unrecognized entries - continue - } - return nil, err - } - if m == nil { - continue - } - mapping = append(mapping, m) - } - if err := s.Err(); err != nil { - return nil, err - } - return mapping, nil -} - -// removeLoggingInfo detects and removes log prefix entries generated -// by the glog package. If no logging prefix is detected, the string -// is returned unmodified. -func removeLoggingInfo(line string) string { - if match := logInfoRE.FindStringIndex(line); match != nil { - return line[match[1]:] - } - return line -} - -// ParseMemoryMap parses a memory map in the format of -// /proc/self/maps, and overrides the mappings in the current profile. -// It renumbers the samples and locations in the profile correspondingly. -func (p *Profile) ParseMemoryMap(rd io.Reader) error { - return p.ParseMemoryMapFromScanner(bufio.NewScanner(rd)) -} - -// ParseMemoryMapFromScanner parses a memory map in the format of -// /proc/self/maps or a variety of legacy format, and overrides the -// mappings in the current profile. It renumbers the samples and -// locations in the profile correspondingly. -func (p *Profile) ParseMemoryMapFromScanner(s *bufio.Scanner) error { - mapping, err := parseProcMapsFromScanner(s) - if err != nil { - return err - } - p.Mapping = append(p.Mapping, mapping...) - p.massageMappings() - p.remapLocationIDs() - p.remapFunctionIDs() - p.remapMappingIDs() - return nil -} - -func parseMappingEntry(l string) (*Mapping, error) { - var start, end, perm, file, offset, buildID string - if me := procMapsRE.FindStringSubmatch(l); len(me) == 6 { - start, end, perm, offset, file = me[1], me[2], me[3], me[4], me[5] - } else if me := briefMapsRE.FindStringSubmatch(l); len(me) == 7 { - start, end, perm, file, offset, buildID = me[1], me[2], me[3], me[4], me[5], me[6] - } else { - return nil, errUnrecognized - } - - var err error - mapping := &Mapping{ - File: file, - BuildID: buildID, - } - if perm != "" && !strings.Contains(perm, "x") { - // Skip non-executable entries. - return nil, nil - } - if mapping.Start, err = strconv.ParseUint(start, 16, 64); err != nil { - return nil, errUnrecognized - } - if mapping.Limit, err = strconv.ParseUint(end, 16, 64); err != nil { - return nil, errUnrecognized - } - if offset != "" { - if mapping.Offset, err = strconv.ParseUint(offset, 16, 64); err != nil { - return nil, errUnrecognized - } - } - return mapping, nil -} - -var memoryMapSentinels = []string{ - "--- Memory map: ---", - "MAPPED_LIBRARIES:", -} - -// isMemoryMapSentinel returns true if the string contains one of the -// known sentinels for memory map information. -func isMemoryMapSentinel(line string) bool { - for _, s := range memoryMapSentinels { - if strings.Contains(line, s) { - return true - } - } - return false -} - -func (p *Profile) addLegacyFrameInfo() { - switch { - case isProfileType(p, heapzSampleTypes): - p.DropFrames, p.KeepFrames = allocRxStr, allocSkipRxStr - case isProfileType(p, contentionzSampleTypes): - p.DropFrames, p.KeepFrames = lockRxStr, "" - default: - p.DropFrames, p.KeepFrames = cpuProfilerRxStr, "" - } -} - -var heapzSampleTypes = [][]string{ - {"allocations", "size"}, // early Go pprof profiles - {"objects", "space"}, - {"inuse_objects", "inuse_space"}, - {"alloc_objects", "alloc_space"}, - {"alloc_objects", "alloc_space", "inuse_objects", "inuse_space"}, // Go pprof legacy profiles -} -var contentionzSampleTypes = [][]string{ - {"contentions", "delay"}, -} - -func isProfileType(p *Profile, types [][]string) bool { - st := p.SampleType -nextType: - for _, t := range types { - if len(st) != len(t) { - continue - } - - for i := range st { - if st[i].Type != t[i] { - continue nextType - } - } - return true - } - return false -} - -var allocRxStr = strings.Join([]string{ - // POSIX entry points. - `calloc`, - `cfree`, - `malloc`, - `free`, - `memalign`, - `do_memalign`, - `(__)?posix_memalign`, - `pvalloc`, - `valloc`, - `realloc`, - - // TC malloc. - `tcmalloc::.*`, - `tc_calloc`, - `tc_cfree`, - `tc_malloc`, - `tc_free`, - `tc_memalign`, - `tc_posix_memalign`, - `tc_pvalloc`, - `tc_valloc`, - `tc_realloc`, - `tc_new`, - `tc_delete`, - `tc_newarray`, - `tc_deletearray`, - `tc_new_nothrow`, - `tc_newarray_nothrow`, - - // Memory-allocation routines on OS X. - `malloc_zone_malloc`, - `malloc_zone_calloc`, - `malloc_zone_valloc`, - `malloc_zone_realloc`, - `malloc_zone_memalign`, - `malloc_zone_free`, - - // Go runtime - `runtime\..*`, - - // Other misc. memory allocation routines - `BaseArena::.*`, - `(::)?do_malloc_no_errno`, - `(::)?do_malloc_pages`, - `(::)?do_malloc`, - `DoSampledAllocation`, - `MallocedMemBlock::MallocedMemBlock`, - `_M_allocate`, - `__builtin_(vec_)?delete`, - `__builtin_(vec_)?new`, - `__gnu_cxx::new_allocator::allocate`, - `__libc_malloc`, - `__malloc_alloc_template::allocate`, - `allocate`, - `cpp_alloc`, - `operator new(\[\])?`, - `simple_alloc::allocate`, -}, `|`) - -var allocSkipRxStr = strings.Join([]string{ - // Preserve Go runtime frames that appear in the middle/bottom of - // the stack. - `runtime\.panic`, - `runtime\.reflectcall`, - `runtime\.call[0-9]*`, -}, `|`) - -var cpuProfilerRxStr = strings.Join([]string{ - `ProfileData::Add`, - `ProfileData::prof_handler`, - `CpuProfiler::prof_handler`, - `__pthread_sighandler`, - `__restore`, -}, `|`) - -var lockRxStr = strings.Join([]string{ - `RecordLockProfileData`, - `(base::)?RecordLockProfileData.*`, - `(base::)?SubmitMutexProfileData.*`, - `(base::)?SubmitSpinLockProfileData.*`, - `(base::Mutex::)?AwaitCommon.*`, - `(base::Mutex::)?Unlock.*`, - `(base::Mutex::)?UnlockSlow.*`, - `(base::Mutex::)?ReaderUnlock.*`, - `(base::MutexLock::)?~MutexLock.*`, - `(Mutex::)?AwaitCommon.*`, - `(Mutex::)?Unlock.*`, - `(Mutex::)?UnlockSlow.*`, - `(Mutex::)?ReaderUnlock.*`, - `(MutexLock::)?~MutexLock.*`, - `(SpinLock::)?Unlock.*`, - `(SpinLock::)?SlowUnlock.*`, - `(SpinLockHolder::)?~SpinLockHolder.*`, -}, `|`) diff --git a/vendor/github.com/google/pprof/profile/merge.go b/vendor/github.com/google/pprof/profile/merge.go deleted file mode 100644 index ba4d74640..000000000 --- a/vendor/github.com/google/pprof/profile/merge.go +++ /dev/null @@ -1,674 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -package profile - -import ( - "encoding/binary" - "fmt" - "sort" - "strconv" - "strings" -) - -// Compact performs garbage collection on a profile to remove any -// unreferenced fields. This is useful to reduce the size of a profile -// after samples or locations have been removed. -func (p *Profile) Compact() *Profile { - p, _ = Merge([]*Profile{p}) - return p -} - -// Merge merges all the profiles in profs into a single Profile. -// Returns a new profile independent of the input profiles. The merged -// profile is compacted to eliminate unused samples, locations, -// functions and mappings. Profiles must have identical profile sample -// and period types or the merge will fail. profile.Period of the -// resulting profile will be the maximum of all profiles, and -// profile.TimeNanos will be the earliest nonzero one. Merges are -// associative with the caveat of the first profile having some -// specialization in how headers are combined. There may be other -// subtleties now or in the future regarding associativity. -func Merge(srcs []*Profile) (*Profile, error) { - if len(srcs) == 0 { - return nil, fmt.Errorf("no profiles to merge") - } - p, err := combineHeaders(srcs) - if err != nil { - return nil, err - } - - pm := &profileMerger{ - p: p, - samples: make(map[sampleKey]*Sample, len(srcs[0].Sample)), - locations: make(map[locationKey]*Location, len(srcs[0].Location)), - functions: make(map[functionKey]*Function, len(srcs[0].Function)), - mappings: make(map[mappingKey]*Mapping, len(srcs[0].Mapping)), - } - - for _, src := range srcs { - // Clear the profile-specific hash tables - pm.locationsByID = makeLocationIDMap(len(src.Location)) - pm.functionsByID = make(map[uint64]*Function, len(src.Function)) - pm.mappingsByID = make(map[uint64]mapInfo, len(src.Mapping)) - - if len(pm.mappings) == 0 && len(src.Mapping) > 0 { - // The Mapping list has the property that the first mapping - // represents the main binary. Take the first Mapping we see, - // otherwise the operations below will add mappings in an - // arbitrary order. - pm.mapMapping(src.Mapping[0]) - } - - for _, s := range src.Sample { - if !isZeroSample(s) { - pm.mapSample(s) - } - } - } - - for _, s := range p.Sample { - if isZeroSample(s) { - // If there are any zero samples, re-merge the profile to GC - // them. - return Merge([]*Profile{p}) - } - } - - return p, nil -} - -// Normalize normalizes the source profile by multiplying each value in profile by the -// ratio of the sum of the base profile's values of that sample type to the sum of the -// source profile's value of that sample type. -func (p *Profile) Normalize(pb *Profile) error { - - if err := p.compatible(pb); err != nil { - return err - } - - baseVals := make([]int64, len(p.SampleType)) - for _, s := range pb.Sample { - for i, v := range s.Value { - baseVals[i] += v - } - } - - srcVals := make([]int64, len(p.SampleType)) - for _, s := range p.Sample { - for i, v := range s.Value { - srcVals[i] += v - } - } - - normScale := make([]float64, len(baseVals)) - for i := range baseVals { - if srcVals[i] == 0 { - normScale[i] = 0.0 - } else { - normScale[i] = float64(baseVals[i]) / float64(srcVals[i]) - } - } - p.ScaleN(normScale) - return nil -} - -func isZeroSample(s *Sample) bool { - for _, v := range s.Value { - if v != 0 { - return false - } - } - return true -} - -type profileMerger struct { - p *Profile - - // Memoization tables within a profile. - locationsByID locationIDMap - functionsByID map[uint64]*Function - mappingsByID map[uint64]mapInfo - - // Memoization tables for profile entities. - samples map[sampleKey]*Sample - locations map[locationKey]*Location - functions map[functionKey]*Function - mappings map[mappingKey]*Mapping -} - -type mapInfo struct { - m *Mapping - offset int64 -} - -func (pm *profileMerger) mapSample(src *Sample) *Sample { - // Check memoization table - k := pm.sampleKey(src) - if ss, ok := pm.samples[k]; ok { - for i, v := range src.Value { - ss.Value[i] += v - } - return ss - } - - // Make new sample. - s := &Sample{ - Location: make([]*Location, len(src.Location)), - Value: make([]int64, len(src.Value)), - Label: make(map[string][]string, len(src.Label)), - NumLabel: make(map[string][]int64, len(src.NumLabel)), - NumUnit: make(map[string][]string, len(src.NumLabel)), - } - for i, l := range src.Location { - s.Location[i] = pm.mapLocation(l) - } - for k, v := range src.Label { - vv := make([]string, len(v)) - copy(vv, v) - s.Label[k] = vv - } - for k, v := range src.NumLabel { - u := src.NumUnit[k] - vv := make([]int64, len(v)) - uu := make([]string, len(u)) - copy(vv, v) - copy(uu, u) - s.NumLabel[k] = vv - s.NumUnit[k] = uu - } - copy(s.Value, src.Value) - pm.samples[k] = s - pm.p.Sample = append(pm.p.Sample, s) - return s -} - -func (pm *profileMerger) sampleKey(sample *Sample) sampleKey { - // Accumulate contents into a string. - var buf strings.Builder - buf.Grow(64) // Heuristic to avoid extra allocs - - // encode a number - putNumber := func(v uint64) { - var num [binary.MaxVarintLen64]byte - n := binary.PutUvarint(num[:], v) - buf.Write(num[:n]) - } - - // encode a string prefixed with its length. - putDelimitedString := func(s string) { - putNumber(uint64(len(s))) - buf.WriteString(s) - } - - for _, l := range sample.Location { - // Get the location in the merged profile, which may have a different ID. - if loc := pm.mapLocation(l); loc != nil { - putNumber(loc.ID) - } - } - putNumber(0) // Delimiter - - for _, l := range sortedKeys1(sample.Label) { - putDelimitedString(l) - values := sample.Label[l] - putNumber(uint64(len(values))) - for _, v := range values { - putDelimitedString(v) - } - } - - for _, l := range sortedKeys2(sample.NumLabel) { - putDelimitedString(l) - values := sample.NumLabel[l] - putNumber(uint64(len(values))) - for _, v := range values { - putNumber(uint64(v)) - } - units := sample.NumUnit[l] - putNumber(uint64(len(units))) - for _, v := range units { - putDelimitedString(v) - } - } - - return sampleKey(buf.String()) -} - -type sampleKey string - -// sortedKeys1 returns the sorted keys found in a string->[]string map. -// -// Note: this is currently non-generic since github pprof runs golint, -// which does not support generics. When that issue is fixed, it can -// be merged with sortedKeys2 and made into a generic function. -func sortedKeys1(m map[string][]string) []string { - if len(m) == 0 { - return nil - } - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} - -// sortedKeys2 returns the sorted keys found in a string->[]int64 map. -// -// Note: this is currently non-generic since github pprof runs golint, -// which does not support generics. When that issue is fixed, it can -// be merged with sortedKeys1 and made into a generic function. -func sortedKeys2(m map[string][]int64) []string { - if len(m) == 0 { - return nil - } - keys := make([]string, 0, len(m)) - for k := range m { - keys = append(keys, k) - } - sort.Strings(keys) - return keys -} - -func (pm *profileMerger) mapLocation(src *Location) *Location { - if src == nil { - return nil - } - - if l := pm.locationsByID.get(src.ID); l != nil { - return l - } - - mi := pm.mapMapping(src.Mapping) - l := &Location{ - ID: uint64(len(pm.p.Location) + 1), - Mapping: mi.m, - Address: uint64(int64(src.Address) + mi.offset), - Line: make([]Line, len(src.Line)), - IsFolded: src.IsFolded, - } - for i, ln := range src.Line { - l.Line[i] = pm.mapLine(ln) - } - // Check memoization table. Must be done on the remapped location to - // account for the remapped mapping ID. - k := l.key() - if ll, ok := pm.locations[k]; ok { - pm.locationsByID.set(src.ID, ll) - return ll - } - pm.locationsByID.set(src.ID, l) - pm.locations[k] = l - pm.p.Location = append(pm.p.Location, l) - return l -} - -// key generates locationKey to be used as a key for maps. -func (l *Location) key() locationKey { - key := locationKey{ - addr: l.Address, - isFolded: l.IsFolded, - } - if l.Mapping != nil { - // Normalizes address to handle address space randomization. - key.addr -= l.Mapping.Start - key.mappingID = l.Mapping.ID - } - lines := make([]string, len(l.Line)*3) - for i, line := range l.Line { - if line.Function != nil { - lines[i*2] = strconv.FormatUint(line.Function.ID, 16) - } - lines[i*2+1] = strconv.FormatInt(line.Line, 16) - lines[i*2+2] = strconv.FormatInt(line.Column, 16) - } - key.lines = strings.Join(lines, "|") - return key -} - -type locationKey struct { - addr, mappingID uint64 - lines string - isFolded bool -} - -func (pm *profileMerger) mapMapping(src *Mapping) mapInfo { - if src == nil { - return mapInfo{} - } - - if mi, ok := pm.mappingsByID[src.ID]; ok { - return mi - } - - // Check memoization tables. - mk := src.key() - if m, ok := pm.mappings[mk]; ok { - mi := mapInfo{m, int64(m.Start) - int64(src.Start)} - pm.mappingsByID[src.ID] = mi - return mi - } - m := &Mapping{ - ID: uint64(len(pm.p.Mapping) + 1), - Start: src.Start, - Limit: src.Limit, - Offset: src.Offset, - File: src.File, - KernelRelocationSymbol: src.KernelRelocationSymbol, - BuildID: src.BuildID, - HasFunctions: src.HasFunctions, - HasFilenames: src.HasFilenames, - HasLineNumbers: src.HasLineNumbers, - HasInlineFrames: src.HasInlineFrames, - } - pm.p.Mapping = append(pm.p.Mapping, m) - - // Update memoization tables. - pm.mappings[mk] = m - mi := mapInfo{m, 0} - pm.mappingsByID[src.ID] = mi - return mi -} - -// key generates encoded strings of Mapping to be used as a key for -// maps. -func (m *Mapping) key() mappingKey { - // Normalize addresses to handle address space randomization. - // Round up to next 4K boundary to avoid minor discrepancies. - const mapsizeRounding = 0x1000 - - size := m.Limit - m.Start - size = size + mapsizeRounding - 1 - size = size - (size % mapsizeRounding) - key := mappingKey{ - size: size, - offset: m.Offset, - } - - switch { - case m.BuildID != "": - key.buildIDOrFile = m.BuildID - case m.File != "": - key.buildIDOrFile = m.File - default: - // A mapping containing neither build ID nor file name is a fake mapping. A - // key with empty buildIDOrFile is used for fake mappings so that they are - // treated as the same mapping during merging. - } - return key -} - -type mappingKey struct { - size, offset uint64 - buildIDOrFile string -} - -func (pm *profileMerger) mapLine(src Line) Line { - ln := Line{ - Function: pm.mapFunction(src.Function), - Line: src.Line, - Column: src.Column, - } - return ln -} - -func (pm *profileMerger) mapFunction(src *Function) *Function { - if src == nil { - return nil - } - if f, ok := pm.functionsByID[src.ID]; ok { - return f - } - k := src.key() - if f, ok := pm.functions[k]; ok { - pm.functionsByID[src.ID] = f - return f - } - f := &Function{ - ID: uint64(len(pm.p.Function) + 1), - Name: src.Name, - SystemName: src.SystemName, - Filename: src.Filename, - StartLine: src.StartLine, - } - pm.functions[k] = f - pm.functionsByID[src.ID] = f - pm.p.Function = append(pm.p.Function, f) - return f -} - -// key generates a struct to be used as a key for maps. -func (f *Function) key() functionKey { - return functionKey{ - f.StartLine, - f.Name, - f.SystemName, - f.Filename, - } -} - -type functionKey struct { - startLine int64 - name, systemName, fileName string -} - -// combineHeaders checks that all profiles can be merged and returns -// their combined profile. -func combineHeaders(srcs []*Profile) (*Profile, error) { - for _, s := range srcs[1:] { - if err := srcs[0].compatible(s); err != nil { - return nil, err - } - } - - var timeNanos, durationNanos, period int64 - var comments []string - seenComments := map[string]bool{} - var docURL string - var defaultSampleType string - for _, s := range srcs { - if timeNanos == 0 || s.TimeNanos < timeNanos { - timeNanos = s.TimeNanos - } - durationNanos += s.DurationNanos - if period == 0 || period < s.Period { - period = s.Period - } - for _, c := range s.Comments { - if seen := seenComments[c]; !seen { - comments = append(comments, c) - seenComments[c] = true - } - } - if defaultSampleType == "" { - defaultSampleType = s.DefaultSampleType - } - if docURL == "" { - docURL = s.DocURL - } - } - - p := &Profile{ - SampleType: make([]*ValueType, len(srcs[0].SampleType)), - - DropFrames: srcs[0].DropFrames, - KeepFrames: srcs[0].KeepFrames, - - TimeNanos: timeNanos, - DurationNanos: durationNanos, - PeriodType: srcs[0].PeriodType, - Period: period, - - Comments: comments, - DefaultSampleType: defaultSampleType, - DocURL: docURL, - } - copy(p.SampleType, srcs[0].SampleType) - return p, nil -} - -// compatible determines if two profiles can be compared/merged. -// returns nil if the profiles are compatible; otherwise an error with -// details on the incompatibility. -func (p *Profile) compatible(pb *Profile) error { - if !equalValueType(p.PeriodType, pb.PeriodType) { - return fmt.Errorf("incompatible period types %v and %v", p.PeriodType, pb.PeriodType) - } - - if len(p.SampleType) != len(pb.SampleType) { - return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) - } - - for i := range p.SampleType { - if !equalValueType(p.SampleType[i], pb.SampleType[i]) { - return fmt.Errorf("incompatible sample types %v and %v", p.SampleType, pb.SampleType) - } - } - return nil -} - -// equalValueType returns true if the two value types are semantically -// equal. It ignores the internal fields used during encode/decode. -func equalValueType(st1, st2 *ValueType) bool { - return st1.Type == st2.Type && st1.Unit == st2.Unit -} - -// locationIDMap is like a map[uint64]*Location, but provides efficiency for -// ids that are densely numbered, which is often the case. -type locationIDMap struct { - dense []*Location // indexed by id for id < len(dense) - sparse map[uint64]*Location // indexed by id for id >= len(dense) -} - -func makeLocationIDMap(n int) locationIDMap { - return locationIDMap{ - dense: make([]*Location, n), - sparse: map[uint64]*Location{}, - } -} - -func (lm locationIDMap) get(id uint64) *Location { - if id < uint64(len(lm.dense)) { - return lm.dense[int(id)] - } - return lm.sparse[id] -} - -func (lm locationIDMap) set(id uint64, loc *Location) { - if id < uint64(len(lm.dense)) { - lm.dense[id] = loc - return - } - lm.sparse[id] = loc -} - -// CompatibilizeSampleTypes makes profiles compatible to be compared/merged. It -// keeps sample types that appear in all profiles only and drops/reorders the -// sample types as necessary. -// -// In the case of sample types order is not the same for given profiles the -// order is derived from the first profile. -// -// Profiles are modified in-place. -// -// It returns an error if the sample type's intersection is empty. -func CompatibilizeSampleTypes(ps []*Profile) error { - sTypes := commonSampleTypes(ps) - if len(sTypes) == 0 { - return fmt.Errorf("profiles have empty common sample type list") - } - for _, p := range ps { - if err := compatibilizeSampleTypes(p, sTypes); err != nil { - return err - } - } - return nil -} - -// commonSampleTypes returns sample types that appear in all profiles in the -// order how they ordered in the first profile. -func commonSampleTypes(ps []*Profile) []string { - if len(ps) == 0 { - return nil - } - sTypes := map[string]int{} - for _, p := range ps { - for _, st := range p.SampleType { - sTypes[st.Type]++ - } - } - var res []string - for _, st := range ps[0].SampleType { - if sTypes[st.Type] == len(ps) { - res = append(res, st.Type) - } - } - return res -} - -// compatibilizeSampleTypes drops sample types that are not present in sTypes -// list and reorder them if needed. -// -// It sets DefaultSampleType to sType[0] if it is not in sType list. -// -// It assumes that all sample types from the sTypes list are present in the -// given profile otherwise it returns an error. -func compatibilizeSampleTypes(p *Profile, sTypes []string) error { - if len(sTypes) == 0 { - return fmt.Errorf("sample type list is empty") - } - defaultSampleType := sTypes[0] - reMap, needToModify := make([]int, len(sTypes)), false - for i, st := range sTypes { - if st == p.DefaultSampleType { - defaultSampleType = p.DefaultSampleType - } - idx := searchValueType(p.SampleType, st) - if idx < 0 { - return fmt.Errorf("%q sample type is not found in profile", st) - } - reMap[i] = idx - if idx != i { - needToModify = true - } - } - if !needToModify && len(sTypes) == len(p.SampleType) { - return nil - } - p.DefaultSampleType = defaultSampleType - oldSampleTypes := p.SampleType - p.SampleType = make([]*ValueType, len(sTypes)) - for i, idx := range reMap { - p.SampleType[i] = oldSampleTypes[idx] - } - values := make([]int64, len(sTypes)) - for _, s := range p.Sample { - for i, idx := range reMap { - values[i] = s.Value[idx] - } - s.Value = s.Value[:len(values)] - copy(s.Value, values) - } - return nil -} - -func searchValueType(vts []*ValueType, s string) int { - for i, vt := range vts { - if vt.Type == s { - return i - } - } - return -1 -} diff --git a/vendor/github.com/google/pprof/profile/profile.go b/vendor/github.com/google/pprof/profile/profile.go deleted file mode 100644 index f47a24390..000000000 --- a/vendor/github.com/google/pprof/profile/profile.go +++ /dev/null @@ -1,869 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -// Package profile provides a representation of profile.proto and -// methods to encode/decode profiles in this format. -package profile - -import ( - "bytes" - "compress/gzip" - "fmt" - "io" - "math" - "path/filepath" - "regexp" - "sort" - "strings" - "sync" - "time" -) - -// Profile is an in-memory representation of profile.proto. -type Profile struct { - SampleType []*ValueType - DefaultSampleType string - Sample []*Sample - Mapping []*Mapping - Location []*Location - Function []*Function - Comments []string - DocURL string - - DropFrames string - KeepFrames string - - TimeNanos int64 - DurationNanos int64 - PeriodType *ValueType - Period int64 - - // The following fields are modified during encoding and copying, - // so are protected by a Mutex. - encodeMu sync.Mutex - - commentX []int64 - docURLX int64 - dropFramesX int64 - keepFramesX int64 - stringTable []string - defaultSampleTypeX int64 -} - -// ValueType corresponds to Profile.ValueType -type ValueType struct { - Type string // cpu, wall, inuse_space, etc - Unit string // seconds, nanoseconds, bytes, etc - - typeX int64 - unitX int64 -} - -// Sample corresponds to Profile.Sample -type Sample struct { - Location []*Location - Value []int64 - // Label is a per-label-key map to values for string labels. - // - // In general, having multiple values for the given label key is strongly - // discouraged - see docs for the sample label field in profile.proto. The - // main reason this unlikely state is tracked here is to make the - // decoding->encoding roundtrip not lossy. But we expect that the value - // slices present in this map are always of length 1. - Label map[string][]string - // NumLabel is a per-label-key map to values for numeric labels. See a note - // above on handling multiple values for a label. - NumLabel map[string][]int64 - // NumUnit is a per-label-key map to the unit names of corresponding numeric - // label values. The unit info may be missing even if the label is in - // NumLabel, see the docs in profile.proto for details. When the value is - // slice is present and not nil, its length must be equal to the length of - // the corresponding value slice in NumLabel. - NumUnit map[string][]string - - locationIDX []uint64 - labelX []label -} - -// label corresponds to Profile.Label -type label struct { - keyX int64 - // Exactly one of the two following values must be set - strX int64 - numX int64 // Integer value for this label - // can be set if numX has value - unitX int64 -} - -// Mapping corresponds to Profile.Mapping -type Mapping struct { - ID uint64 - Start uint64 - Limit uint64 - Offset uint64 - File string - BuildID string - HasFunctions bool - HasFilenames bool - HasLineNumbers bool - HasInlineFrames bool - - fileX int64 - buildIDX int64 - - // Name of the kernel relocation symbol ("_text" or "_stext"), extracted from File. - // For linux kernel mappings generated by some tools, correct symbolization depends - // on knowing which of the two possible relocation symbols was used for `Start`. - // This is given to us as a suffix in `File` (e.g. "[kernel.kallsyms]_stext"). - // - // Note, this public field is not persisted in the proto. For the purposes of - // copying / merging / hashing profiles, it is considered subsumed by `File`. - KernelRelocationSymbol string -} - -// Location corresponds to Profile.Location -type Location struct { - ID uint64 - Mapping *Mapping - Address uint64 - Line []Line - IsFolded bool - - mappingIDX uint64 -} - -// Line corresponds to Profile.Line -type Line struct { - Function *Function - Line int64 - Column int64 - - functionIDX uint64 -} - -// Function corresponds to Profile.Function -type Function struct { - ID uint64 - Name string - SystemName string - Filename string - StartLine int64 - - nameX int64 - systemNameX int64 - filenameX int64 -} - -// Parse parses a profile and checks for its validity. The input -// may be a gzip-compressed encoded protobuf or one of many legacy -// profile formats which may be unsupported in the future. -func Parse(r io.Reader) (*Profile, error) { - data, err := io.ReadAll(r) - if err != nil { - return nil, err - } - return ParseData(data) -} - -// ParseData parses a profile from a buffer and checks for its -// validity. -func ParseData(data []byte) (*Profile, error) { - var p *Profile - var err error - if len(data) >= 2 && data[0] == 0x1f && data[1] == 0x8b { - gz, err := gzip.NewReader(bytes.NewBuffer(data)) - if err == nil { - data, err = io.ReadAll(gz) - } - if err != nil { - return nil, fmt.Errorf("decompressing profile: %v", err) - } - } - if p, err = ParseUncompressed(data); err != nil && err != errNoData && err != errConcatProfile { - p, err = parseLegacy(data) - } - - if err != nil { - return nil, fmt.Errorf("parsing profile: %v", err) - } - - if err := p.CheckValid(); err != nil { - return nil, fmt.Errorf("malformed profile: %v", err) - } - return p, nil -} - -var errUnrecognized = fmt.Errorf("unrecognized profile format") -var errMalformed = fmt.Errorf("malformed profile format") -var errNoData = fmt.Errorf("empty input file") -var errConcatProfile = fmt.Errorf("concatenated profiles detected") - -func parseLegacy(data []byte) (*Profile, error) { - parsers := []func([]byte) (*Profile, error){ - parseCPU, - parseHeap, - parseGoCount, // goroutine, threadcreate - parseThread, - parseContention, - parseJavaProfile, - } - - for _, parser := range parsers { - p, err := parser(data) - if err == nil { - p.addLegacyFrameInfo() - return p, nil - } - if err != errUnrecognized { - return nil, err - } - } - return nil, errUnrecognized -} - -// ParseUncompressed parses an uncompressed protobuf into a profile. -func ParseUncompressed(data []byte) (*Profile, error) { - if len(data) == 0 { - return nil, errNoData - } - p := &Profile{} - if err := unmarshal(data, p); err != nil { - return nil, err - } - - if err := p.postDecode(); err != nil { - return nil, err - } - - return p, nil -} - -var libRx = regexp.MustCompile(`([.]so$|[.]so[._][0-9]+)`) - -// massageMappings applies heuristic-based changes to the profile -// mappings to account for quirks of some environments. -func (p *Profile) massageMappings() { - // Merge adjacent regions with matching names, checking that the offsets match - if len(p.Mapping) > 1 { - mappings := []*Mapping{p.Mapping[0]} - for _, m := range p.Mapping[1:] { - lm := mappings[len(mappings)-1] - if adjacent(lm, m) { - lm.Limit = m.Limit - if m.File != "" { - lm.File = m.File - } - if m.BuildID != "" { - lm.BuildID = m.BuildID - } - p.updateLocationMapping(m, lm) - continue - } - mappings = append(mappings, m) - } - p.Mapping = mappings - } - - // Use heuristics to identify main binary and move it to the top of the list of mappings - for i, m := range p.Mapping { - file := strings.TrimSpace(strings.Replace(m.File, "(deleted)", "", -1)) - if len(file) == 0 { - continue - } - if len(libRx.FindStringSubmatch(file)) > 0 { - continue - } - if file[0] == '[' { - continue - } - // Swap what we guess is main to position 0. - p.Mapping[0], p.Mapping[i] = p.Mapping[i], p.Mapping[0] - break - } - - // Keep the mapping IDs neatly sorted - for i, m := range p.Mapping { - m.ID = uint64(i + 1) - } -} - -// adjacent returns whether two mapping entries represent the same -// mapping that has been split into two. Check that their addresses are adjacent, -// and if the offsets match, if they are available. -func adjacent(m1, m2 *Mapping) bool { - if m1.File != "" && m2.File != "" { - if m1.File != m2.File { - return false - } - } - if m1.BuildID != "" && m2.BuildID != "" { - if m1.BuildID != m2.BuildID { - return false - } - } - if m1.Limit != m2.Start { - return false - } - if m1.Offset != 0 && m2.Offset != 0 { - offset := m1.Offset + (m1.Limit - m1.Start) - if offset != m2.Offset { - return false - } - } - return true -} - -func (p *Profile) updateLocationMapping(from, to *Mapping) { - for _, l := range p.Location { - if l.Mapping == from { - l.Mapping = to - } - } -} - -func serialize(p *Profile) []byte { - p.encodeMu.Lock() - p.preEncode() - b := marshal(p) - p.encodeMu.Unlock() - return b -} - -// Write writes the profile as a gzip-compressed marshaled protobuf. -func (p *Profile) Write(w io.Writer) error { - zw := gzip.NewWriter(w) - defer zw.Close() - _, err := zw.Write(serialize(p)) - return err -} - -// WriteUncompressed writes the profile as a marshaled protobuf. -func (p *Profile) WriteUncompressed(w io.Writer) error { - _, err := w.Write(serialize(p)) - return err -} - -// CheckValid tests whether the profile is valid. Checks include, but are -// not limited to: -// - len(Profile.Sample[n].value) == len(Profile.value_unit) -// - Sample.id has a corresponding Profile.Location -func (p *Profile) CheckValid() error { - // Check that sample values are consistent - sampleLen := len(p.SampleType) - if sampleLen == 0 && len(p.Sample) != 0 { - return fmt.Errorf("missing sample type information") - } - for _, s := range p.Sample { - if s == nil { - return fmt.Errorf("profile has nil sample") - } - if len(s.Value) != sampleLen { - return fmt.Errorf("mismatch: sample has %d values vs. %d types", len(s.Value), len(p.SampleType)) - } - for _, l := range s.Location { - if l == nil { - return fmt.Errorf("sample has nil location") - } - } - } - - // Check that all mappings/locations/functions are in the tables - // Check that there are no duplicate ids - mappings := make(map[uint64]*Mapping, len(p.Mapping)) - for _, m := range p.Mapping { - if m == nil { - return fmt.Errorf("profile has nil mapping") - } - if m.ID == 0 { - return fmt.Errorf("found mapping with reserved ID=0") - } - if mappings[m.ID] != nil { - return fmt.Errorf("multiple mappings with same id: %d", m.ID) - } - mappings[m.ID] = m - } - functions := make(map[uint64]*Function, len(p.Function)) - for _, f := range p.Function { - if f == nil { - return fmt.Errorf("profile has nil function") - } - if f.ID == 0 { - return fmt.Errorf("found function with reserved ID=0") - } - if functions[f.ID] != nil { - return fmt.Errorf("multiple functions with same id: %d", f.ID) - } - functions[f.ID] = f - } - locations := make(map[uint64]*Location, len(p.Location)) - for _, l := range p.Location { - if l == nil { - return fmt.Errorf("profile has nil location") - } - if l.ID == 0 { - return fmt.Errorf("found location with reserved id=0") - } - if locations[l.ID] != nil { - return fmt.Errorf("multiple locations with same id: %d", l.ID) - } - locations[l.ID] = l - if m := l.Mapping; m != nil { - if m.ID == 0 || mappings[m.ID] != m { - return fmt.Errorf("inconsistent mapping %p: %d", m, m.ID) - } - } - for _, ln := range l.Line { - f := ln.Function - if f == nil { - return fmt.Errorf("location id: %d has a line with nil function", l.ID) - } - if f.ID == 0 || functions[f.ID] != f { - return fmt.Errorf("inconsistent function %p: %d", f, f.ID) - } - } - } - return nil -} - -// Aggregate merges the locations in the profile into equivalence -// classes preserving the request attributes. It also updates the -// samples to point to the merged locations. -func (p *Profile) Aggregate(inlineFrame, function, filename, linenumber, columnnumber, address bool) error { - for _, m := range p.Mapping { - m.HasInlineFrames = m.HasInlineFrames && inlineFrame - m.HasFunctions = m.HasFunctions && function - m.HasFilenames = m.HasFilenames && filename - m.HasLineNumbers = m.HasLineNumbers && linenumber - } - - // Aggregate functions - if !function || !filename { - for _, f := range p.Function { - if !function { - f.Name = "" - f.SystemName = "" - } - if !filename { - f.Filename = "" - } - } - } - - // Aggregate locations - if !inlineFrame || !address || !linenumber || !columnnumber { - for _, l := range p.Location { - if !inlineFrame && len(l.Line) > 1 { - l.Line = l.Line[len(l.Line)-1:] - } - if !linenumber { - for i := range l.Line { - l.Line[i].Line = 0 - l.Line[i].Column = 0 - } - } - if !columnnumber { - for i := range l.Line { - l.Line[i].Column = 0 - } - } - if !address { - l.Address = 0 - } - } - } - - return p.CheckValid() -} - -// NumLabelUnits returns a map of numeric label keys to the units -// associated with those keys and a map of those keys to any units -// that were encountered but not used. -// Unit for a given key is the first encountered unit for that key. If multiple -// units are encountered for values paired with a particular key, then the first -// unit encountered is used and all other units are returned in sorted order -// in map of ignored units. -// If no units are encountered for a particular key, the unit is then inferred -// based on the key. -func (p *Profile) NumLabelUnits() (map[string]string, map[string][]string) { - numLabelUnits := map[string]string{} - ignoredUnits := map[string]map[string]bool{} - encounteredKeys := map[string]bool{} - - // Determine units based on numeric tags for each sample. - for _, s := range p.Sample { - for k := range s.NumLabel { - encounteredKeys[k] = true - for _, unit := range s.NumUnit[k] { - if unit == "" { - continue - } - if wantUnit, ok := numLabelUnits[k]; !ok { - numLabelUnits[k] = unit - } else if wantUnit != unit { - if v, ok := ignoredUnits[k]; ok { - v[unit] = true - } else { - ignoredUnits[k] = map[string]bool{unit: true} - } - } - } - } - } - // Infer units for keys without any units associated with - // numeric tag values. - for key := range encounteredKeys { - unit := numLabelUnits[key] - if unit == "" { - switch key { - case "alignment", "request": - numLabelUnits[key] = "bytes" - default: - numLabelUnits[key] = key - } - } - } - - // Copy ignored units into more readable format - unitsIgnored := make(map[string][]string, len(ignoredUnits)) - for key, values := range ignoredUnits { - units := make([]string, len(values)) - i := 0 - for unit := range values { - units[i] = unit - i++ - } - sort.Strings(units) - unitsIgnored[key] = units - } - - return numLabelUnits, unitsIgnored -} - -// String dumps a text representation of a profile. Intended mainly -// for debugging purposes. -func (p *Profile) String() string { - ss := make([]string, 0, len(p.Comments)+len(p.Sample)+len(p.Mapping)+len(p.Location)) - for _, c := range p.Comments { - ss = append(ss, "Comment: "+c) - } - if url := p.DocURL; url != "" { - ss = append(ss, fmt.Sprintf("Doc: %s", url)) - } - if pt := p.PeriodType; pt != nil { - ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit)) - } - ss = append(ss, fmt.Sprintf("Period: %d", p.Period)) - if p.TimeNanos != 0 { - ss = append(ss, fmt.Sprintf("Time: %v", time.Unix(0, p.TimeNanos))) - } - if p.DurationNanos != 0 { - ss = append(ss, fmt.Sprintf("Duration: %.4v", time.Duration(p.DurationNanos))) - } - - ss = append(ss, "Samples:") - var sh1 string - for _, s := range p.SampleType { - dflt := "" - if s.Type == p.DefaultSampleType { - dflt = "[dflt]" - } - sh1 = sh1 + fmt.Sprintf("%s/%s%s ", s.Type, s.Unit, dflt) - } - ss = append(ss, strings.TrimSpace(sh1)) - for _, s := range p.Sample { - ss = append(ss, s.string()) - } - - ss = append(ss, "Locations") - for _, l := range p.Location { - ss = append(ss, l.string()) - } - - ss = append(ss, "Mappings") - for _, m := range p.Mapping { - ss = append(ss, m.string()) - } - - return strings.Join(ss, "\n") + "\n" -} - -// string dumps a text representation of a mapping. Intended mainly -// for debugging purposes. -func (m *Mapping) string() string { - bits := "" - if m.HasFunctions { - bits = bits + "[FN]" - } - if m.HasFilenames { - bits = bits + "[FL]" - } - if m.HasLineNumbers { - bits = bits + "[LN]" - } - if m.HasInlineFrames { - bits = bits + "[IN]" - } - return fmt.Sprintf("%d: %#x/%#x/%#x %s %s %s", - m.ID, - m.Start, m.Limit, m.Offset, - m.File, - m.BuildID, - bits) -} - -// string dumps a text representation of a location. Intended mainly -// for debugging purposes. -func (l *Location) string() string { - ss := []string{} - locStr := fmt.Sprintf("%6d: %#x ", l.ID, l.Address) - if m := l.Mapping; m != nil { - locStr = locStr + fmt.Sprintf("M=%d ", m.ID) - } - if l.IsFolded { - locStr = locStr + "[F] " - } - if len(l.Line) == 0 { - ss = append(ss, locStr) - } - for li := range l.Line { - lnStr := "??" - if fn := l.Line[li].Function; fn != nil { - lnStr = fmt.Sprintf("%s %s:%d:%d s=%d", - fn.Name, - fn.Filename, - l.Line[li].Line, - l.Line[li].Column, - fn.StartLine) - if fn.Name != fn.SystemName { - lnStr = lnStr + "(" + fn.SystemName + ")" - } - } - ss = append(ss, locStr+lnStr) - // Do not print location details past the first line - locStr = " " - } - return strings.Join(ss, "\n") -} - -// string dumps a text representation of a sample. Intended mainly -// for debugging purposes. -func (s *Sample) string() string { - ss := []string{} - var sv string - for _, v := range s.Value { - sv = fmt.Sprintf("%s %10d", sv, v) - } - sv = sv + ": " - for _, l := range s.Location { - sv = sv + fmt.Sprintf("%d ", l.ID) - } - ss = append(ss, sv) - const labelHeader = " " - if len(s.Label) > 0 { - ss = append(ss, labelHeader+labelsToString(s.Label)) - } - if len(s.NumLabel) > 0 { - ss = append(ss, labelHeader+numLabelsToString(s.NumLabel, s.NumUnit)) - } - return strings.Join(ss, "\n") -} - -// labelsToString returns a string representation of a -// map representing labels. -func labelsToString(labels map[string][]string) string { - ls := []string{} - for k, v := range labels { - ls = append(ls, fmt.Sprintf("%s:%v", k, v)) - } - sort.Strings(ls) - return strings.Join(ls, " ") -} - -// numLabelsToString returns a string representation of a map -// representing numeric labels. -func numLabelsToString(numLabels map[string][]int64, numUnits map[string][]string) string { - ls := []string{} - for k, v := range numLabels { - units := numUnits[k] - var labelString string - if len(units) == len(v) { - values := make([]string, len(v)) - for i, vv := range v { - values[i] = fmt.Sprintf("%d %s", vv, units[i]) - } - labelString = fmt.Sprintf("%s:%v", k, values) - } else { - labelString = fmt.Sprintf("%s:%v", k, v) - } - ls = append(ls, labelString) - } - sort.Strings(ls) - return strings.Join(ls, " ") -} - -// SetLabel sets the specified key to the specified value for all samples in the -// profile. -func (p *Profile) SetLabel(key string, value []string) { - for _, sample := range p.Sample { - if sample.Label == nil { - sample.Label = map[string][]string{key: value} - } else { - sample.Label[key] = value - } - } -} - -// RemoveLabel removes all labels associated with the specified key for all -// samples in the profile. -func (p *Profile) RemoveLabel(key string) { - for _, sample := range p.Sample { - delete(sample.Label, key) - } -} - -// HasLabel returns true if a sample has a label with indicated key and value. -func (s *Sample) HasLabel(key, value string) bool { - for _, v := range s.Label[key] { - if v == value { - return true - } - } - return false -} - -// SetNumLabel sets the specified key to the specified value for all samples in the -// profile. "unit" is a slice that describes the units that each corresponding member -// of "values" is measured in (e.g. bytes or seconds). If there is no relevant -// unit for a given value, that member of "unit" should be the empty string. -// "unit" must either have the same length as "value", or be nil. -func (p *Profile) SetNumLabel(key string, value []int64, unit []string) { - for _, sample := range p.Sample { - if sample.NumLabel == nil { - sample.NumLabel = map[string][]int64{key: value} - } else { - sample.NumLabel[key] = value - } - if sample.NumUnit == nil { - sample.NumUnit = map[string][]string{key: unit} - } else { - sample.NumUnit[key] = unit - } - } -} - -// RemoveNumLabel removes all numerical labels associated with the specified key for all -// samples in the profile. -func (p *Profile) RemoveNumLabel(key string) { - for _, sample := range p.Sample { - delete(sample.NumLabel, key) - delete(sample.NumUnit, key) - } -} - -// DiffBaseSample returns true if a sample belongs to the diff base and false -// otherwise. -func (s *Sample) DiffBaseSample() bool { - return s.HasLabel("pprof::base", "true") -} - -// Scale multiplies all sample values in a profile by a constant and keeps -// only samples that have at least one non-zero value. -func (p *Profile) Scale(ratio float64) { - if ratio == 1 { - return - } - ratios := make([]float64, len(p.SampleType)) - for i := range p.SampleType { - ratios[i] = ratio - } - p.ScaleN(ratios) -} - -// ScaleN multiplies each sample values in a sample by a different amount -// and keeps only samples that have at least one non-zero value. -func (p *Profile) ScaleN(ratios []float64) error { - if len(p.SampleType) != len(ratios) { - return fmt.Errorf("mismatched scale ratios, got %d, want %d", len(ratios), len(p.SampleType)) - } - allOnes := true - for _, r := range ratios { - if r != 1 { - allOnes = false - break - } - } - if allOnes { - return nil - } - fillIdx := 0 - for _, s := range p.Sample { - keepSample := false - for i, v := range s.Value { - if ratios[i] != 1 { - val := int64(math.Round(float64(v) * ratios[i])) - s.Value[i] = val - keepSample = keepSample || val != 0 - } - } - if keepSample { - p.Sample[fillIdx] = s - fillIdx++ - } - } - p.Sample = p.Sample[:fillIdx] - return nil -} - -// HasFunctions determines if all locations in this profile have -// symbolized function information. -func (p *Profile) HasFunctions() bool { - for _, l := range p.Location { - if l.Mapping != nil && !l.Mapping.HasFunctions { - return false - } - } - return true -} - -// HasFileLines determines if all locations in this profile have -// symbolized file and line number information. -func (p *Profile) HasFileLines() bool { - for _, l := range p.Location { - if l.Mapping != nil && (!l.Mapping.HasFilenames || !l.Mapping.HasLineNumbers) { - return false - } - } - return true -} - -// Unsymbolizable returns true if a mapping points to a binary for which -// locations can't be symbolized in principle, at least now. Examples are -// "[vdso]", "[vsyscall]" and some others, see the code. -func (m *Mapping) Unsymbolizable() bool { - name := filepath.Base(m.File) - return strings.HasPrefix(name, "[") || strings.HasPrefix(name, "linux-vdso") || strings.HasPrefix(m.File, "/dev/dri/") || m.File == "//anon" -} - -// Copy makes a fully independent copy of a profile. -func (p *Profile) Copy() *Profile { - pp := &Profile{} - if err := unmarshal(serialize(p), pp); err != nil { - panic(err) - } - if err := pp.postDecode(); err != nil { - panic(err) - } - - return pp -} diff --git a/vendor/github.com/google/pprof/profile/proto.go b/vendor/github.com/google/pprof/profile/proto.go deleted file mode 100644 index a15696ba1..000000000 --- a/vendor/github.com/google/pprof/profile/proto.go +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -// This file is a simple protocol buffer encoder and decoder. -// The format is described at -// https://developers.google.com/protocol-buffers/docs/encoding -// -// A protocol message must implement the message interface: -// decoder() []decoder -// encode(*buffer) -// -// The decode method returns a slice indexed by field number that gives the -// function to decode that field. -// The encode method encodes its receiver into the given buffer. -// -// The two methods are simple enough to be implemented by hand rather than -// by using a protocol compiler. -// -// See profile.go for examples of messages implementing this interface. -// -// There is no support for groups, message sets, or "has" bits. - -package profile - -import ( - "errors" - "fmt" -) - -type buffer struct { - field int // field tag - typ int // proto wire type code for field - u64 uint64 - data []byte - tmp [16]byte - tmpLines []Line // temporary storage used while decoding "repeated Line". -} - -type decoder func(*buffer, message) error - -type message interface { - decoder() []decoder - encode(*buffer) -} - -func marshal(m message) []byte { - var b buffer - m.encode(&b) - return b.data -} - -func encodeVarint(b *buffer, x uint64) { - for x >= 128 { - b.data = append(b.data, byte(x)|0x80) - x >>= 7 - } - b.data = append(b.data, byte(x)) -} - -func encodeLength(b *buffer, tag int, len int) { - encodeVarint(b, uint64(tag)<<3|2) - encodeVarint(b, uint64(len)) -} - -func encodeUint64(b *buffer, tag int, x uint64) { - // append varint to b.data - encodeVarint(b, uint64(tag)<<3) - encodeVarint(b, x) -} - -func encodeUint64s(b *buffer, tag int, x []uint64) { - if len(x) > 2 { - // Use packed encoding - n1 := len(b.data) - for _, u := range x { - encodeVarint(b, u) - } - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) - return - } - for _, u := range x { - encodeUint64(b, tag, u) - } -} - -func encodeUint64Opt(b *buffer, tag int, x uint64) { - if x == 0 { - return - } - encodeUint64(b, tag, x) -} - -func encodeInt64(b *buffer, tag int, x int64) { - u := uint64(x) - encodeUint64(b, tag, u) -} - -func encodeInt64s(b *buffer, tag int, x []int64) { - if len(x) > 2 { - // Use packed encoding - n1 := len(b.data) - for _, u := range x { - encodeVarint(b, uint64(u)) - } - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) - return - } - for _, u := range x { - encodeInt64(b, tag, u) - } -} - -func encodeInt64Opt(b *buffer, tag int, x int64) { - if x == 0 { - return - } - encodeInt64(b, tag, x) -} - -func encodeString(b *buffer, tag int, x string) { - encodeLength(b, tag, len(x)) - b.data = append(b.data, x...) -} - -func encodeStrings(b *buffer, tag int, x []string) { - for _, s := range x { - encodeString(b, tag, s) - } -} - -func encodeBool(b *buffer, tag int, x bool) { - if x { - encodeUint64(b, tag, 1) - } else { - encodeUint64(b, tag, 0) - } -} - -func encodeBoolOpt(b *buffer, tag int, x bool) { - if x { - encodeBool(b, tag, x) - } -} - -func encodeMessage(b *buffer, tag int, m message) { - n1 := len(b.data) - m.encode(b) - n2 := len(b.data) - encodeLength(b, tag, n2-n1) - n3 := len(b.data) - copy(b.tmp[:], b.data[n2:n3]) - copy(b.data[n1+(n3-n2):], b.data[n1:n2]) - copy(b.data[n1:], b.tmp[:n3-n2]) -} - -func unmarshal(data []byte, m message) (err error) { - b := buffer{data: data, typ: 2} - return decodeMessage(&b, m) -} - -func le64(p []byte) uint64 { - return uint64(p[0]) | uint64(p[1])<<8 | uint64(p[2])<<16 | uint64(p[3])<<24 | uint64(p[4])<<32 | uint64(p[5])<<40 | uint64(p[6])<<48 | uint64(p[7])<<56 -} - -func le32(p []byte) uint32 { - return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 -} - -func decodeVarint(data []byte) (uint64, []byte, error) { - var u uint64 - for i := 0; ; i++ { - if i >= 10 || i >= len(data) { - return 0, nil, errors.New("bad varint") - } - u |= uint64(data[i]&0x7F) << uint(7*i) - if data[i]&0x80 == 0 { - return u, data[i+1:], nil - } - } -} - -func decodeField(b *buffer, data []byte) ([]byte, error) { - x, data, err := decodeVarint(data) - if err != nil { - return nil, err - } - b.field = int(x >> 3) - b.typ = int(x & 7) - b.data = nil - b.u64 = 0 - switch b.typ { - case 0: - b.u64, data, err = decodeVarint(data) - if err != nil { - return nil, err - } - case 1: - if len(data) < 8 { - return nil, errors.New("not enough data") - } - b.u64 = le64(data[:8]) - data = data[8:] - case 2: - var n uint64 - n, data, err = decodeVarint(data) - if err != nil { - return nil, err - } - if n > uint64(len(data)) { - return nil, errors.New("too much data") - } - b.data = data[:n] - data = data[n:] - case 5: - if len(data) < 4 { - return nil, errors.New("not enough data") - } - b.u64 = uint64(le32(data[:4])) - data = data[4:] - default: - return nil, fmt.Errorf("unknown wire type: %d", b.typ) - } - - return data, nil -} - -func checkType(b *buffer, typ int) error { - if b.typ != typ { - return errors.New("type mismatch") - } - return nil -} - -func decodeMessage(b *buffer, m message) error { - if err := checkType(b, 2); err != nil { - return err - } - dec := m.decoder() - data := b.data - for len(data) > 0 { - // pull varint field# + type - var err error - data, err = decodeField(b, data) - if err != nil { - return err - } - if b.field >= len(dec) || dec[b.field] == nil { - continue - } - if err := dec[b.field](b, m); err != nil { - return err - } - } - return nil -} - -func decodeInt64(b *buffer, x *int64) error { - if err := checkType(b, 0); err != nil { - return err - } - *x = int64(b.u64) - return nil -} - -func decodeInt64s(b *buffer, x *[]int64) error { - if b.typ == 2 { - // Packed encoding - data := b.data - for len(data) > 0 { - var u uint64 - var err error - - if u, data, err = decodeVarint(data); err != nil { - return err - } - *x = append(*x, int64(u)) - } - return nil - } - var i int64 - if err := decodeInt64(b, &i); err != nil { - return err - } - *x = append(*x, i) - return nil -} - -func decodeUint64(b *buffer, x *uint64) error { - if err := checkType(b, 0); err != nil { - return err - } - *x = b.u64 - return nil -} - -func decodeUint64s(b *buffer, x *[]uint64) error { - if b.typ == 2 { - data := b.data - // Packed encoding - for len(data) > 0 { - var u uint64 - var err error - - if u, data, err = decodeVarint(data); err != nil { - return err - } - *x = append(*x, u) - } - return nil - } - var u uint64 - if err := decodeUint64(b, &u); err != nil { - return err - } - *x = append(*x, u) - return nil -} - -func decodeString(b *buffer, x *string) error { - if err := checkType(b, 2); err != nil { - return err - } - *x = string(b.data) - return nil -} - -func decodeStrings(b *buffer, x *[]string) error { - var s string - if err := decodeString(b, &s); err != nil { - return err - } - *x = append(*x, s) - return nil -} - -func decodeBool(b *buffer, x *bool) error { - if err := checkType(b, 0); err != nil { - return err - } - if int64(b.u64) == 0 { - *x = false - } else { - *x = true - } - return nil -} diff --git a/vendor/github.com/google/pprof/profile/prune.go b/vendor/github.com/google/pprof/profile/prune.go deleted file mode 100644 index b2f9fd546..000000000 --- a/vendor/github.com/google/pprof/profile/prune.go +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2014 Google Inc. All Rights Reserved. -// -// 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. - -// Implements methods to remove frames from profiles. - -package profile - -import ( - "fmt" - "regexp" - "strings" -) - -var ( - reservedNames = []string{"(anonymous namespace)", "operator()"} - bracketRx = func() *regexp.Regexp { - var quotedNames []string - for _, name := range append(reservedNames, "(") { - quotedNames = append(quotedNames, regexp.QuoteMeta(name)) - } - return regexp.MustCompile(strings.Join(quotedNames, "|")) - }() -) - -// simplifyFunc does some primitive simplification of function names. -func simplifyFunc(f string) string { - // Account for leading '.' on the PPC ELF v1 ABI. - funcName := strings.TrimPrefix(f, ".") - // Account for unsimplified names -- try to remove the argument list by trimming - // starting from the first '(', but skipping reserved names that have '('. - for _, ind := range bracketRx.FindAllStringSubmatchIndex(funcName, -1) { - foundReserved := false - for _, res := range reservedNames { - if funcName[ind[0]:ind[1]] == res { - foundReserved = true - break - } - } - if !foundReserved { - funcName = funcName[:ind[0]] - break - } - } - return funcName -} - -// Prune removes all nodes beneath a node matching dropRx, and not -// matching keepRx. If the root node of a Sample matches, the sample -// will have an empty stack. -func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) { - prune := make(map[uint64]bool) - pruneBeneath := make(map[uint64]bool) - - // simplifyFunc can be expensive, so cache results. - // Note that the same function name can be encountered many times due - // different lines and addresses in the same function. - pruneCache := map[string]bool{} // Map from function to whether or not to prune - pruneFromHere := func(s string) bool { - if r, ok := pruneCache[s]; ok { - return r - } - funcName := simplifyFunc(s) - if dropRx.MatchString(funcName) { - if keepRx == nil || !keepRx.MatchString(funcName) { - pruneCache[s] = true - return true - } - } - pruneCache[s] = false - return false - } - - for _, loc := range p.Location { - var i int - for i = len(loc.Line) - 1; i >= 0; i-- { - if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { - if pruneFromHere(fn.Name) { - break - } - } - } - - if i >= 0 { - // Found matching entry to prune. - pruneBeneath[loc.ID] = true - - // Remove the matching location. - if i == len(loc.Line)-1 { - // Matched the top entry: prune the whole location. - prune[loc.ID] = true - } else { - loc.Line = loc.Line[i+1:] - } - } - } - - // Prune locs from each Sample - for _, sample := range p.Sample { - // Scan from the root to the leaves to find the prune location. - // Do not prune frames before the first user frame, to avoid - // pruning everything. - foundUser := false - for i := len(sample.Location) - 1; i >= 0; i-- { - id := sample.Location[i].ID - if !prune[id] && !pruneBeneath[id] { - foundUser = true - continue - } - if !foundUser { - continue - } - if prune[id] { - sample.Location = sample.Location[i+1:] - break - } - if pruneBeneath[id] { - sample.Location = sample.Location[i:] - break - } - } - } -} - -// RemoveUninteresting prunes and elides profiles using built-in -// tables of uninteresting function names. -func (p *Profile) RemoveUninteresting() error { - var keep, drop *regexp.Regexp - var err error - - if p.DropFrames != "" { - if drop, err = regexp.Compile("^(" + p.DropFrames + ")$"); err != nil { - return fmt.Errorf("failed to compile regexp %s: %v", p.DropFrames, err) - } - if p.KeepFrames != "" { - if keep, err = regexp.Compile("^(" + p.KeepFrames + ")$"); err != nil { - return fmt.Errorf("failed to compile regexp %s: %v", p.KeepFrames, err) - } - } - p.Prune(drop, keep) - } - return nil -} - -// PruneFrom removes all nodes beneath the lowest node matching dropRx, not including itself. -// -// Please see the example below to understand this method as well as -// the difference from Prune method. -// -// A sample contains Location of [A,B,C,B,D] where D is the top frame and there's no inline. -// -// PruneFrom(A) returns [A,B,C,B,D] because there's no node beneath A. -// Prune(A, nil) returns [B,C,B,D] by removing A itself. -// -// PruneFrom(B) returns [B,C,B,D] by removing all nodes beneath the first B when scanning from the bottom. -// Prune(B, nil) returns [D] because a matching node is found by scanning from the root. -func (p *Profile) PruneFrom(dropRx *regexp.Regexp) { - pruneBeneath := make(map[uint64]bool) - - for _, loc := range p.Location { - for i := 0; i < len(loc.Line); i++ { - if fn := loc.Line[i].Function; fn != nil && fn.Name != "" { - funcName := simplifyFunc(fn.Name) - if dropRx.MatchString(funcName) { - // Found matching entry to prune. - pruneBeneath[loc.ID] = true - loc.Line = loc.Line[i:] - break - } - } - } - } - - // Prune locs from each Sample - for _, sample := range p.Sample { - // Scan from the bottom leaf to the root to find the prune location. - for i, loc := range sample.Location { - if pruneBeneath[loc.ID] { - sample.Location = sample.Location[i:] - break - } - } - } -} |
