cmd: update vendored pprof

go get github.com/google/pprof@latest
  go mod vendor

Plus a tiny change to the pprof command to match an upstream interface
change.

Updates #36905.

Change-Id: I4c7bbe8c317a6eeb217fce971b208f96ab0727fa
Reviewed-on: https://go-review.googlesource.com/c/go/+/393370
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
Auto-Submit: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
Heschi Kreinick 2022-03-16 18:17:58 -04:00 committed by Gopher Robot
parent 9f252a0462
commit 7e5804cb70
30 changed files with 1361 additions and 5845 deletions

View File

@ -3,7 +3,7 @@ module cmd
go 1.19
require (
github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31
github.com/google/pprof v0.0.0-20220314021825-5bba342933ea
golang.org/x/arch v0.0.0-20210923205945-b76863e36670
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c

View File

@ -1,8 +1,8 @@
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31 h1:YvpxjnjGhf/vDEeYOysNbsrtB///PKS8lqkFNSDm1p8=
github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20220314021825-5bba342933ea h1:yVDp4C9ff/qoEAhHNf5cqk/YTxTGECSzGYzahdhEKBI=
github.com/google/pprof v0.0.0-20220314021825-5bba342933ea/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d h1:uGg2frlt3IcT7kbV6LEp5ONv4vmoO2FW4qSO+my/aoM=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=

View File

@ -149,7 +149,7 @@ type objTool struct {
disasmCache map[string]*objfile.Disasm
}
func (*objTool) Open(name string, start, limit, offset uint64) (driver.ObjFile, error) {
func (*objTool) Open(name string, start, limit, offset uint64, relocationSymbol string) (driver.ObjFile, error) {
of, err := objfile.Open(name)
if err != nil {
return nil, err

View File

@ -137,8 +137,10 @@ type MappingSources map[string][]struct {
type ObjTool interface {
// Open opens the named object file. If the object is a shared
// library, start/limit/offset are the addresses where it is mapped
// into memory in the address space being inspected.
Open(file string, start, limit, offset uint64) (ObjFile, error)
// into memory in the address space being inspected. If the object
// is a linux kernel, relocationSymbol is the name of the symbol
// corresponding to the start address.
Open(file string, start, limit, offset uint64, relocationSymbol string) (ObjFile, error)
// Disasm disassembles the named object file, starting at
// the start address and stopping at (before) the end address.
@ -232,8 +234,8 @@ type internalObjTool struct {
ObjTool
}
func (o *internalObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
f, err := o.ObjTool.Open(file, start, limit, offset)
func (o *internalObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
f, err := o.ObjTool.Open(file, start, limit, offset, relocationSymbol)
if err != nil {
return nil, err
}

View File

@ -284,7 +284,7 @@ func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax bool) ([]
}
// Open satisfies the plugin.ObjTool interface.
func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
func (bu *Binutils) Open(name string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
b := bu.get()
// Make sure file is a supported executable.
@ -316,7 +316,7 @@ func (bu *Binutils) Open(name string, start, limit, offset uint64) (plugin.ObjFi
// Match against supported file types.
if elfMagic == elf.ELFMAG {
f, err := b.openELF(name, start, limit, offset)
f, err := b.openELF(name, start, limit, offset, relocationSymbol)
if err != nil {
return nil, fmt.Errorf("error reading ELF file %s: %v", name, err)
}
@ -425,7 +425,7 @@ func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.Obj
return b.openMachOCommon(name, of, start, limit, offset)
}
func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
func (b *binrep) openELF(name string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
ef, err := elfOpen(name)
if err != nil {
return nil, fmt.Errorf("error parsing %s: %v", name, err)
@ -440,8 +440,8 @@ func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFi
}
var (
stextOffset *uint64
pageAligned = func(addr uint64) bool { return addr%4096 == 0 }
kernelOffset *uint64
pageAligned = func(addr uint64) bool { return addr%4096 == 0 }
)
if strings.Contains(name, "vmlinux") || !pageAligned(start) || !pageAligned(limit) || !pageAligned(offset) {
// Reading all Symbols is expensive, and we only rarely need it so
@ -455,10 +455,18 @@ func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFi
if err != nil && err != elf.ErrNoSymbols {
return nil, err
}
// The kernel relocation symbol (the mapping start address) can be either
// _text or _stext. When profiles are generated by `perf`, which one was used is
// distinguished by the mapping name for the kernel image:
// '[kernel.kallsyms]_text' or '[kernel.kallsyms]_stext', respectively. If we haven't
// been able to parse it from the mapping, we default to _stext.
if relocationSymbol == "" {
relocationSymbol = "_stext"
}
for _, s := range symbols {
if s.Name == "_stext" {
// The kernel may use _stext as the mapping start address.
stextOffset = &s.Value
if s.Name == relocationSymbol {
kernelOffset = &s.Value
break
}
}
@ -469,7 +477,7 @@ func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFi
// value until we have a sample address for this mapping, so that we can
// correctly identify the associated program segment that is needed to compute
// the base.
if _, err := elfexec.GetBase(&ef.FileHeader, elfexec.FindTextProgHeader(ef), stextOffset, start, limit, offset); err != nil {
if _, err := elfexec.GetBase(&ef.FileHeader, elfexec.FindTextProgHeader(ef), kernelOffset, start, limit, offset); err != nil {
return nil, fmt.Errorf("could not identify base for %s: %v", name, err)
}
@ -478,14 +486,14 @@ func (b *binrep) openELF(name string, start, limit, offset uint64) (plugin.ObjFi
b: b,
name: name,
buildID: buildID,
m: &elfMapping{start: start, limit: limit, offset: offset, stextOffset: stextOffset},
m: &elfMapping{start: start, limit: limit, offset: offset, kernelOffset: kernelOffset},
}}, nil
}
return &fileAddr2Line{file: file{
b: b,
name: name,
buildID: buildID,
m: &elfMapping{start: start, limit: limit, offset: offset, stextOffset: stextOffset},
m: &elfMapping{start: start, limit: limit, offset: offset, kernelOffset: kernelOffset},
}}, nil
}
@ -521,8 +529,8 @@ func (b *binrep) openPE(name string, start, limit, offset uint64) (plugin.ObjFil
type elfMapping struct {
// Runtime mapping parameters.
start, limit, offset uint64
// Offset of _stext symbol. Only defined for kernel images, nil otherwise.
stextOffset *uint64
// Offset of kernel relocation symbol. Only defined for kernel images, nil otherwise.
kernelOffset *uint64
}
// findProgramHeader returns the program segment that matches the current
@ -535,7 +543,7 @@ func (m *elfMapping) findProgramHeader(ef *elf.File, addr uint64) (*elf.ProgHead
// it's a kernel / .ko module mapping, because with quipper address remapping
// enabled, the address would be in the lower half of the address space.
if m.stextOffset != nil || m.start >= m.limit || m.limit >= (uint64(1)<<63) {
if m.kernelOffset != nil || m.start >= m.limit || m.limit >= (uint64(1)<<63) {
// For the kernel, find the program segment that includes the .text section.
return elfexec.FindTextProgHeader(ef), nil
}
@ -601,7 +609,7 @@ func (f *file) computeBase(addr uint64) error {
return fmt.Errorf("failed to find program header for file %q, ELF mapping %#v, address %x: %v", f.name, *f.m, addr, err)
}
base, err := elfexec.GetBase(&ef.FileHeader, ph, f.m.stextOffset, f.m.start, f.m.limit, f.m.offset)
base, err := elfexec.GetBase(&ef.FileHeader, ph, f.m.kernelOffset, f.m.start, f.m.limit, f.m.offset)
if err != nil {
return err
}

View File

@ -98,7 +98,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
// Recognize first argument as an executable or buildid override.
if len(args) > 1 {
arg0 := args[0]
if file, err := o.Obj.Open(arg0, 0, ^uint64(0), 0); err == nil {
if file, err := o.Obj.Open(arg0, 0, ^uint64(0), 0, ""); err == nil {
file.Close()
execName = arg0
args = args[1:]

View File

@ -420,12 +420,14 @@ mapping:
fileNames = append(fileNames, filepath.Join(path, m.File))
}
for _, name := range fileNames {
if f, err := obj.Open(name, m.Start, m.Limit, m.Offset); err == nil {
if f, err := obj.Open(name, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol); err == nil {
defer f.Close()
fileBuildID := f.BuildID()
if m.BuildID != "" && m.BuildID != fileBuildID {
ui.PrintErr("Ignoring local file " + name + ": build-id mismatch (" + m.BuildID + " != " + fileBuildID + ")")
} else {
// Explicitly do not update KernelRelocationSymbol --
// the new local file name is most likely missing it.
m.File = name
continue mapping
}
@ -449,6 +451,8 @@ mapping:
if execName, buildID := s.ExecName, s.BuildID; execName != "" || buildID != "" {
m := p.Mapping[0]
if execName != "" {
// Explicitly do not update KernelRelocationSymbol --
// the source override is most likely missing it.
m.File = execName
}
if buildID != "" {

View File

@ -17,13 +17,11 @@ package driver
import (
"html/template"
"github.com/google/pprof/third_party/d3"
"github.com/google/pprof/third_party/d3flamegraph"
)
// addTemplates adds a set of template definitions to templates.
func addTemplates(templates *template.Template) {
template.Must(templates.Parse(`{{define "d3script"}}` + d3.JSSource + `{{end}}`))
template.Must(templates.Parse(`{{define "d3flamegraphscript"}}` + d3flamegraph.JSSource + `{{end}}`))
template.Must(templates.Parse(`{{define "d3flamegraphcss"}}` + d3flamegraph.CSSSource + `{{end}}`))
template.Must(templates.Parse(`
@ -1329,40 +1327,29 @@ function viewer(baseUrl, nodes) {
</div>
{{template "script" .}}
<script>viewer(new URL(window.location.href), {{.Nodes}});</script>
<script>{{template "d3script" .}}</script>
<script>{{template "d3flamegraphscript" .}}</script>
<script>
var data = {{.FlameGraph}};
var width = document.getElementById('chart').clientWidth;
var flameGraph = d3.flamegraph()
var flameGraph = flamegraph()
.width(width)
.cellHeight(18)
.minFrameSize(1)
.transitionDuration(750)
.transitionEase(d3.easeCubic)
.inverted(true)
.sort(true)
.title('')
.tooltip(false)
.details(document.getElementById('flamegraphdetails'));
.setDetailsElement(document.getElementById('flamegraphdetails'));
// <full name> (percentage, value)
flameGraph.label((d) => d.data.f + ' (' + d.data.p + ', ' + d.data.l + ')');
(function(flameGraph) {
var oldColorMapper = flameGraph.color();
function colorMapper(d) {
// Hack to force default color mapper to use 'warm' color scheme by not passing libtype
const { data, highlight } = d;
return oldColorMapper({ data: { n: data.n }, highlight });
}
flameGraph.setColorHue('warm');
flameGraph.color(colorMapper);
}(flameGraph));
d3.select('#chart')
select('#chart')
.datum(data)
.call(flameGraph);

View File

@ -165,9 +165,9 @@ func GetBuildID(binary io.ReaderAt) ([]byte, error) {
return nil, nil
}
// kernelBase caluclates the base for kernel mappings, which usually require
// kernelBase calculates the base for kernel mappings, which usually require
// special handling. For kernel mappings, tools (like perf) use the address of
// the kernel relocation symbol (_text or _stext) as the mmap start. Additionaly,
// the kernel relocation symbol (_text or _stext) as the mmap start. Additionally,
// for obfuscation, ChromeOS profiles have the kernel image remapped to the 0-th page.
func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, bool) {
const (
@ -217,7 +217,7 @@ func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit,
// GetBase determines the base address to subtract from virtual
// address to get symbol table address. For an executable, the base
// is 0. Otherwise, it's a shared library, and the base is the
// address where the mapping starts. The kernel needs special hanldling.
// address where the mapping starts. The kernel needs special handling.
func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) {
if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) {

View File

@ -126,7 +126,7 @@ func (b *builder) addLegend() {
return
}
title := labels[0]
fmt.Fprintf(b, `subgraph cluster_L { "%s" [shape=box fontsize=16`, title)
fmt.Fprintf(b, `subgraph cluster_L { "%s" [shape=box fontsize=16`, escapeForDot(title))
fmt.Fprintf(b, ` label="%s\l"`, strings.Join(escapeAllForDot(labels), `\l`))
if b.config.LegendURL != "" {
fmt.Fprintf(b, ` URL="%s" target="_blank"`, b.config.LegendURL)
@ -485,7 +485,7 @@ func escapeAllForDot(in []string) []string {
// escapeForDot escapes double quotes and backslashes, and replaces Graphviz's
// "center" character (\n) with a left-justified character.
// See https://graphviz.org/doc/info/attrs.html#k:escString for more info.
// See https://graphviz.org/docs/attr-types/escString/ for more info.
func escapeForDot(str string) string {
return strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(str, `\`, `\\`), `"`, `\"`), "\n", `\l`)
}

View File

@ -109,8 +109,10 @@ type MappingSources map[string][]struct {
type ObjTool interface {
// Open opens the named object file. If the object is a shared
// library, start/limit/offset are the addresses where it is mapped
// into memory in the address space being inspected.
Open(file string, start, limit, offset uint64) (ObjFile, error)
// into memory in the address space being inspected. If the object
// is a linux kernel, relocationSymbol is the name of the symbol
// corresponding to the start address.
Open(file string, start, limit, offset uint64, relocationSymbol string) (ObjFile, error)
// Disasm disassembles the named object file, starting at
// the start address and stopping at (before) the end address.

View File

@ -526,7 +526,7 @@ func symbolsFromBinaries(prof *profile.Profile, g *graph.Graph, rx *regexp.Regex
}
}
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
if err != nil {
fmt.Printf("%v\n", err)
continue

View File

@ -744,7 +744,7 @@ func (sp *sourcePrinter) objectFile(m *profile.Mapping) plugin.ObjFile {
if object, ok := sp.objects[m.File]; ok {
return object // May be nil if we detected an error earlier.
}
object, err := sp.objectTool.Open(m.File, m.Start, m.Limit, m.Offset)
object, err := sp.objectTool.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
if err != nil {
object = nil
}

View File

@ -325,7 +325,10 @@ func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force b
}
name := filepath.Base(m.File)
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
if m.BuildID != "" {
name += fmt.Sprintf(" (build ID %s)", m.BuildID)
}
f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset, m.KernelRelocationSymbol)
if err != nil {
ui.PrintErr("Local symbolization failed for ", name, ": ", err)
missingBinaries = true

View File

@ -17,6 +17,7 @@ package profile
import (
"errors"
"sort"
"strings"
)
func (p *Profile) decoder() []decoder {
@ -252,6 +253,14 @@ func (p *Profile) postDecode() error {
} 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.
if strings.HasPrefix(m.File, "[kernel.kallsyms]") {
m.KernelRelocationSymbol = strings.ReplaceAll(m.File, "[kernel.kallsyms]", "")
}
}
functions := make(map[uint64]*Function, len(p.Function))

View File

@ -303,16 +303,17 @@ func (pm *profileMerger) mapMapping(src *Mapping) mapInfo {
return mi
}
m := &Mapping{
ID: uint64(len(pm.p.Mapping) + 1),
Start: src.Start,
Limit: src.Limit,
Offset: src.Offset,
File: src.File,
BuildID: src.BuildID,
HasFunctions: src.HasFunctions,
HasFilenames: src.HasFilenames,
HasLineNumbers: src.HasLineNumbers,
HasInlineFrames: src.HasInlineFrames,
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)

View File

@ -106,6 +106,15 @@ type Mapping struct {
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

View File

@ -1,27 +0,0 @@
Copyright 2010-2017 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,119 +0,0 @@
# Building a customized D3.js bundle
The D3.js version distributed with pprof is customized to only include the modules required by pprof.
## Dependencies
First, it's necessary to pull all bundle dependencies. We will use a JavaScript package manager, [npm](https://www.npmjs.com/), to accomplish that. npm dependencies are declared in a `package.json` file, so create one with the following configuration:
```js
{
"name": "d3-pprof",
"version": "1.0.0",
"description": "A d3.js bundle for pprof.",
"scripts": {
"prepare": "rollup -c && uglifyjs d3.js -c -m -o d3.min.js"
},
"license": "Apache-2.0",
"devDependencies": {
"d3-selection": "1.1.0",
"d3-hierarchy": "1.1.5",
"d3-scale": "1.0.6",
"d3-format": "1.2.0",
"d3-ease": "1.0.3",
"d3-array": "1.2.1",
"d3-collection": "1.0.4",
"d3-transition": "1.1.0",
"rollup": "0.51.8",
"rollup-plugin-node-resolve": "3",
"uglify-js": "3.1.10"
}
}
```
Besides the bundle dependencies, the `package.json` file also specifies a script called `prepare`, which will be executed to create the bundle after `Rollup` is installed.
## Bundler
The simplest way of creating a custom bundle is to use a bundler, such as [Rollup](https://rollupjs.org/) or [Webpack](https://webpack.js.org/). Rollup will be used in this example.
First, create a `rollup.config.js` file, containing the configuration Rollup should use to build the bundle.
```js
import node from "rollup-plugin-node-resolve";
export default {
input: "index.js",
output: {
format: "umd",
file: "d3.js"
},
name: "d3",
plugins: [node()],
sourcemap: false
};
```
Then create an `index.js` file containing all the functions that need to be exported in the bundle.
```js
export {
select,
selection,
event,
} from "d3-selection";
export {
hierarchy,
partition,
} from "d3-hierarchy";
export {
scaleLinear,
} from "d3-scale";
export {
format,
} from "d3-format";
export {
easeCubic,
} from "d3-ease";
export {
ascending,
} from "d3-array";
export {
map,
} from "d3-collection";
export {
transition,
} from "d3-transition";
```
## Building
Once all files were created, execute the following commands to pull all dependencies and build the bundle.
```
% npm install
% npm run prepare
```
This will create two files, `d3.js` and `d3.min.js`, the custom D3.js bundle and its minified version respectively.
# References
## D3 Custom Bundle
A demonstration of building a custom D3 4.0 bundle using ES2015 modules and Rollup.
[bl.ocks.org/mbostock/bb09af4c39c79cffcde4](https://bl.ocks.org/mbostock/bb09af4c39c79cffcde4)
## d3-pprof
A repository containing all previously mentioned configuration files and the generated custom bundle.
[github.com/spiermar/d3-pprof](https://github.com/spiermar/d3-pprof)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
Copyright 2010-2021 Mike Bostock
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

View File

@ -0,0 +1,33 @@
# Building a customized D3.js bundle
The D3.js version distributed with pprof is customized to only include the
modules required by pprof.
## Dependencies
- Install [npm](https://www.npmjs.com).
## Building
- Run `update.sh` to:
- Download npm package dependencies (declared in `package.json` and `package-lock.json`)
- Create a d3.js bundle containing the JavScript of d3 and d3-flame-graph (by running `webpack`)
This will `d3_flame_graph.go`, the minified custom D3.js bundle as Go source code.
# References / Appendix
## D3 Custom Bundle
A demonstration of building a custom D3 4.0 bundle using ES2015 modules and Rollup.
[bl.ocks.org/mbostock/bb09af4c39c79cffcde4](https://bl.ocks.org/mbostock/bb09af4c39c79cffcde4)
## Old version of d3-pprof
A previous verison of d3-flame-graph bundled for pprof used Rollup instead of
Webpack. This has now been migrated directly into this directory.
The repository configuring Rollup was here:
[github.com/spiermar/d3-pprof](https://github.com/spiermar/d3-pprof)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
// This file exports a stripped-down API surface of d3 and d3-flame-graph,
// using only the functions used by pprof.
export {
select,
} from "d3-selection";
export {
default as flamegraph
// If we export from "d3-flame-graph" that exports the "dist" version which
// includes another copy of d3-selection. To avoid including d3-selection
// twice in the output, instead import the "src" version.
} from "d3-flame-graph/src/flamegraph";

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
{
"name": "d3-pprof",
"version": "2.0.0",
"description": "A d3.js bundle for pprof.",
"scripts": {
"prepare": "webpack --mode production"
},
"license": "Apache-2.0",
"dependencies": {
"d3-flame-graph": "^4.1.3",
"d3-selection": "^3.0.0"
},
"devDependencies": {
"webpack": "^5.64.4",
"webpack-cli": "^4.9.1"
}
}

View File

@ -0,0 +1,62 @@
# Copyright 2021 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.
#!/usr/bin/env bash
set -eu
set -o pipefail
D3FLAMEGRAPH_CSS="d3-flamegraph.css"
cd $(dirname $0)
generate_d3_flame_graph_go() {
npm install
# https://stackoverflow.com/a/21199041/171898
local d3_js=$(cat d3.js | sed 's/`/`+"`"+`/g')
local d3_css=$(cat "node_modules/d3-flame-graph/dist/${D3FLAMEGRAPH_CSS}")
cat <<-EOF > d3_flame_graph.go
// D3.js is a JavaScript library for manipulating documents based on data.
// https://github.com/d3/d3
// See D3_LICENSE file for license details
// d3-flame-graph is a D3.js plugin that produces flame graphs from hierarchical data.
// https://github.com/spiermar/d3-flame-graph
// See D3_FLAME_GRAPH_LICENSE file for license details
package d3flamegraph
// JSSource returns the d3 and d3-flame-graph JavaScript bundle
const JSSource = \`
$d3_js
\`
// CSSSource returns the $D3FLAMEGRAPH_CSS file
const CSSSource = \`
$d3_css
\`
EOF
gofmt -w d3_flame_graph.go
}
get_licenses() {
cp node_modules/d3-selection/LICENSE D3_LICENSE
cp node_modules/d3-flame-graph/LICENSE D3_FLAME_GRAPH_LICENSE
}
get_licenses
generate_d3_flame_graph_go

View File

@ -0,0 +1,13 @@
// Minimal webpack config to package a minified JS bundle (including
// dependencies) for execution in a <script> tag in the browser.
module.exports = {
entry: './index.js',
output: {
path: __dirname, // Directory containing this webpack.config.js file.
filename: 'd3.js',
// Arbitrary; many module formats could be used, just keeping Universal
// Module Definition as it's the same as what we used in a previous
// version.
libraryTarget: 'umd',
},
};

View File

@ -1,4 +1,4 @@
# github.com/google/pprof v0.0.0-20211104044539-f987b9c94b31
# github.com/google/pprof v0.0.0-20220314021825-5bba342933ea
## explicit; go 1.14
github.com/google/pprof/driver
github.com/google/pprof/internal/binutils
@ -12,7 +12,6 @@ github.com/google/pprof/internal/symbolizer
github.com/google/pprof/internal/symbolz
github.com/google/pprof/internal/transport
github.com/google/pprof/profile
github.com/google/pprof/third_party/d3
github.com/google/pprof/third_party/d3flamegraph
github.com/google/pprof/third_party/svgpan
# github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d