go 1.26
require (
- github.com/google/pprof v0.0.0-20250208200701-d0013a598941
+ github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5
golang.org/x/arch v0.20.1-0.20250808194827-46ba08e3ae58
golang.org/x/build v0.0.0-20250806225920-b7c66c047964
golang.org/x/mod v0.27.0
)
require (
- github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd // indirect
+ github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b // indirect
golang.org/x/text v0.28.0 // indirect
rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef // indirect
)
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc=
-github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
-github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd h1:EVX1s+XNss9jkRW9K6XGJn2jL2lB1h5H804oKPsxOec=
-github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
+github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
+github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
+github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA=
+github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/arch v0.20.1-0.20250808194827-46ba08e3ae58 h1:uxPa6+/WsUfzikIAPMqpTho10y4qtYpINBurU+6NrHE=
import (
"io"
+ "maps"
"net/http"
"regexp"
"time"
func (s *internalSymbolizer) Symbolize(mode string, srcs plugin.MappingSources, prof *profile.Profile) error {
isrcs := MappingSources{}
- for m, s := range srcs {
- isrcs[m] = s
- }
+ maps.Copy(isrcs, srcs)
return s.Symbolizer.Symbolize(mode, isrcs, prof)
}
"fmt"
"net/url"
"reflect"
+ "slices"
"strconv"
"strings"
"sync"
case *string:
if len(f.choices) > 0 {
// Verify that value is one of the allowed choices.
- for _, choice := range f.choices {
- if choice == value {
- *ptr = value
- return nil
- }
+ if slices.Contains(f.choices, value) {
+ *ptr = value
+ return nil
}
return fmt.Errorf("invalid %q value %q", f.name, value)
}
import (
"fmt"
"regexp"
+ "slices"
"strconv"
"strings"
return func(s *profile.Sample) bool {
if vals, ok := s.Label[wantKey]; ok {
for _, rx := range rfx {
- for _, val := range vals {
- if rx.MatchString(val) {
- return true
- }
+ if slices.ContainsFunc(vals, rx.MatchString) {
+ return true
}
}
}
var count int
for start := 0; start < len(sources); start += chunkSize {
- end := start + chunkSize
- if end > len(sources) {
- end = len(sources)
- }
+ end := min(start+chunkSize, len(sources))
chunkP, chunkMsrc, chunkSave, chunkCount, chunkErr := concurrentGrab(sources[start:end], fetch, obj, ui, tr)
switch {
case chunkErr != nil:
}
#graph {
overflow: hidden;
+ width: 100%;
+ height: 100%;
}
#graph svg {
width: 100%;
- height: auto;
+ height: 100%;
padding: 10px;
}
#content.source .filename {
}
// percentText returns text that displays v in appropriate units alongside its
- // percentange.
+ // percentage.
function percentText(v) {
function percent(v, total) {
return Number(((100.0 * v) / total).toFixed(1)) + '%';
"fmt"
"html/template"
"io"
+ "maps"
"net"
"net/http"
gourl "net/url"
"os"
"os/exec"
+ "slices"
"strconv"
"strings"
"time"
for n, c := range pprofCommands {
ui.help[n] = c.description
}
- for n, help := range configHelp {
- ui.help[n] = help
- }
+ maps.Copy(ui.help, configHelp)
ui.help["details"] = "Show information about the profile and this view"
ui.help["graph"] = "Display profile as a directed graph"
ui.help["flamegraph"] = "Display profile as a flame graph"
}
func isLocalhost(host string) bool {
- for _, v := range []string{"localhost", "127.0.0.1", "[::1]", "::1"} {
- if host == v {
- return true
- }
- }
- return false
+ return slices.Contains([]string{"localhost", "127.0.0.1", "[::1]", "::1"}, host)
}
func openBrowser(url string, o *plugin.Options) {
}
if stextOffset == nil && start > 0 && start < 0x8000000000000000 {
// A regular user-mode executable. Compute the base offset using same
- // arithmetics as in ET_DYN case below, see the explanation there.
+ // arithmetic as in ET_DYN case below, see the explanation there.
// Ideally, the condition would just be "stextOffset == nil" as that
// represents the address of _stext symbol in the vmlinux image. Alas,
// the caller may skip reading it from the binary (it's expensive to scan
// value is dependent on the memory management unit of the CPU. The page
// size is 4KB virtually on all the architectures that we care about, so we
// define this metric as a constant. If we encounter architectures where
- // page sie is not 4KB, we must try to guess the page size on the system
+ // page size is not 4KB, we must try to guess the page size on the system
// where the profile was collected, possibly using the architecture
// specified in the ELF file header.
pageSize = 4096
if dw == 0 && w == 0 {
continue
}
- for k := range seenNode {
- delete(seenNode, k)
- }
- for k := range seenEdge {
- delete(seenEdge, k)
- }
+ clear(seenNode)
+ clear(seenEdge)
var parent *Node
// A residual edge goes over one or more nodes that were not kept.
residual := false
// If generating a visual graph, count tags as nodes. Update
// maxNodes to account for them.
for i, n := range g.Nodes {
- tags := countTags(n)
- if tags > maxNodelets {
- tags = maxNodelets
- }
+ tags := min(countTags(n), maxNodelets)
if count += tags + 1; count >= maxNodes {
maxNodes = i + 1
break
import (
"fmt"
"math"
+ "slices"
"strings"
"time"
// nil if the unit with such alias is not found.
func (ut UnitType) findByAlias(alias string) *Unit {
for _, u := range ut.Units {
- for _, a := range u.aliases {
- if alias == a {
- return &u
- }
+ if slices.Contains(u.aliases, alias) {
+ return &u
}
}
return nil
}
-// sniffUnit simpifies the input alias and returns the unit associated with the
+// sniffUnit simplifies the input alias and returns the unit associated with the
// specified alias. It returns nil if the unit with such alias is not found.
func (ut UnitType) sniffUnit(unit string) *Unit {
unit = strings.ToLower(unit)
return objSyms
}
-// objSym represents a symbol identified from a binary. It includes
+// objSymbol represents a symbol identified from a binary. It includes
// the SymbolInfo from the disasm package and the base that must be
// added to correspond to sample addresses
type objSymbol struct {
"os"
"path/filepath"
"regexp"
+ "slices"
"sort"
"strconv"
"strings"
file.lines[f.Line] = append(file.lines[f.Line], sourceInst{addr, stack})
// Remember the first function name encountered per source line
- // and assume that that line belongs to that function.
+ // and assume that line belongs to that function.
if _, ok := file.funcName[f.Line]; !ok {
file.funcName[f.Line] = f.Func
}
unprocessed = append(unprocessed, addr)
}
}
- sort.Slice(addrs, func(i, j int) bool { return addrs[i] < addrs[j] })
+ slices.Sort(addrs)
const expand = 500 // How much to expand range to pick up nearby addresses.
var result []addressRange
}
} else {
// Find gap from predecessor and divide between predecessor and f.
- halfGap := (f.begin - funcs[i-1].end) / 2
- if halfGap > expand {
- halfGap = expand
- }
+ halfGap := min((f.begin-funcs[i-1].end)/2, expand)
funcs[i-1].end += halfGap
f.begin -= halfGap
}
panic(fmt.Sprintf("unknown demanglerMode %s", demanglerMode))
}
-func demangleSingleFunction(fn *profile.Function, options []demangle.Option) {
+func demangleSingleFunction(fn *profile.Function, opts []demangle.Option) {
if fn.Name != "" && fn.SystemName != fn.Name {
return // Already demangled.
}
- // Copy the options because they may be updated by the call.
- o := make([]demangle.Option, len(options))
- copy(o, options)
- if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
+ if demangled := demangle.Filter(fn.SystemName, opts...); demangled != fn.SystemName {
fn.Name = demangled
return
}
// OSX has all the symbols prefixed with extra '_' so lets try
// once more without it
if strings.HasPrefix(fn.SystemName, "_") {
- if demangled := demangle.Filter(fn.SystemName[1:], o...); demangled != fn.SystemName {
+ if demangled := demangle.Filter(fn.SystemName[1:], opts...); demangled != fn.SystemName[1:] {
fn.Name = demangled
return
}
// already demangled.
name := fn.SystemName
if looksLikeDemangledCPlusPlus(name) {
- for _, o := range options {
+ for _, o := range opts {
switch o {
case demangle.NoParams:
name = removeMatching(name, '(', ')')
import (
"encoding/binary"
"fmt"
+ "slices"
"sort"
"strconv"
"strings"
}
}
- 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})
- }
+ if slices.ContainsFunc(p.Sample, isZeroSample) {
+ // If there are any zero samples, re-merge the profile to GC
+ // them.
+ return Merge([]*Profile{p})
}
return p, nil
"math"
"path/filepath"
"regexp"
+ "slices"
"sort"
"strings"
"sync"
// 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
+ return slices.Contains(s.Label[key], value)
}
// SetNumLabel sets the specified key to the specified value for all samples in the
// "[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"
+ switch {
+ case strings.HasPrefix(name, "["):
+ case strings.HasPrefix(name, "linux-vdso"):
+ case strings.HasPrefix(m.File, "/dev/dri/"):
+ case m.File == "//anon":
+ case m.File == "":
+ case strings.HasPrefix(m.File, "/memfd:"):
+ default:
+ return false
+ }
+ return true
}
// Copy makes a fully independent copy of a profile.
import (
"fmt"
"regexp"
+ "slices"
"strings"
)
// 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
- }
- }
+ foundReserved := slices.Contains(reservedNames, funcName[ind[0]:ind[1]])
if !foundReserved {
funcName = funcName[:ind[0]]
break
* ======================
*
* Given an unique existing element with id "viewport" (or when missing, the
- * first g-element), including the the library into any SVG adds the following
+ * first g-element), including the library into any SVG adds the following
* capabilities:
*
* - Mouse panning
i := 0
for i < len(options) {
if options[i] == NoParams {
- options = append(options[:i], options[i+1:]...)
+ options = append(options[:i:i], options[i+1:]...)
} else {
i++
}
-# github.com/google/pprof v0.0.0-20250208200701-d0013a598941
-## explicit; go 1.22
+# github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5
+## explicit; go 1.23.0
github.com/google/pprof/driver
github.com/google/pprof/internal/binutils
github.com/google/pprof/internal/driver
github.com/google/pprof/internal/transport
github.com/google/pprof/profile
github.com/google/pprof/third_party/svgpan
-# github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd
+# github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b
## explicit; go 1.13
github.com/ianlancetaylor/demangle
# golang.org/x/arch v0.20.1-0.20250808194827-46ba08e3ae58