`go tool pprof` web UI Disassemble sends `^func$`, but `disasm` matches PrintableName
- Dominant language
- Go
- Stars
- 9.3k
- Forks
- 671
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 10
Description
## Summary
I hit this through `go tool pprof` (Go's vendored copy of this repo), not the standalone `pprof` binary.
In the web UI (`go tool pprof -http=...`), clicking a function and then Disassemble fails with
```
no matches found for regexp ^mypkg\.MyFunc$
```
Source for the same `f=` works. Unanchored `disasm MyFunc` in the CLI also works.
This is not darwin PIE / missing-binary. The binary is passed in, and `list ^MyFunc$` succeeds.
## Environment
- `go version go1.24.4 darwin/arm64` (also reproduced with toolchain `go1.26.2`)
- Invocation: `go tool pprof -http=:8080 ./foo.test cpu.prof`
## When it broke
Until Go 1.22, the web UI escaped the selected name but did not anchor it
```js
// Go 1.22 and earlier (vendored pprof)
function quotemeta(str) {
return str.replace(/([\\\.?+*\[\](){}|^$])/g, '\\$1');
}
```
Click → Disassemble produced `f=mypkg.MyFunc`, which still matched as a substring of PrintableName (`ADDRESS mypkg.MyFunc file.go:N`).
Exact-match quoting was added in google/pprof on 2023-08-16:
- PR: https://github.com/google/pprof/pull/796
- Commit: https://github.com/google/pprof/commit/76846d4d6a3b69af9b9331821b7d40d2f5f4b58d (`Fix flamegraph name matching`)
```js
// convert a string to a regexp that matches exactly that string.
function pprofQuoteMeta(str) {
return '^' + str.replace(/([\\\.?+*\[\](){}|^$])/g, '\\$1') + '$';
}
```
The intent was Focus/Hide on flame graphs (UniqueName vs FunctionName). The same helper is used for the Disassemble button.
Go vendored that snapshot on 2024-02-16
- Commit: https://github.com/golang/go/commit/63dd79c07b0026b58f421a5273c41e705ccb73d1 (`cmd/pprof: update vendored github.com/google/pprof`)
- CL: https://go-review.googlesource.com/c/go/+/564636
- `github.com/google/pprof` `v0.0.0-20230811205829-9131a7e9cc17` → `v0.0.0-20240207164012-fb44976bdcd5`
That landed on tip during the 1.23 cycle. Go 1.22 still has unanchored `quotemeta`. Go 1.23.0 (2024-08-13) is the first release where `go tool pprof` web UI Disassemble is broken this way. Still present in 1.24 / 1.26.
`disasm` matching PrintableName (address granularity) is older; the UI change is what made click → Disassemble fail.
## Reproduction (`go tool pprof`)
```go
package foo
import "testing"
func work() int {
s := 0
for i := 0; i < 1_000_000; i++ {
s += i
}
return s
}
func BenchmarkWork(b *testing.B) {
for i := 0; i < b.N; i++ {
work()
}
}
```
```bash
go test -c -o foo.test
./foo.test -test.bench=BenchmarkWork -test.cpuprofile=cpu.prof
go tool pprof -http=:8080 ./foo.test cpu.prof
```
1. Open Top, click `example.com/foo.BenchmarkWork`.
2. Click Source → annotated source appears.
3. Click Disassemble → `no matches found for regexp ^example.com/foo\.BenchmarkWork$`.
CLI:
```
$ go tool pprof ./foo.test cpu.prof
(pprof) list ^example.com/foo\.BenchmarkWork$ # works
(pprof) disasm ^example.com/foo\.BenchmarkWork$ # fails
(pprof) disasm example.com/foo.BenchmarkWork # works
```
`go tool nm ./foo.test` shows the symbol.
## Expected
The regexp the web UI puts in `f=` (`^name$`) should work for both Source and Disassemble.
## Actual
`disasm` matches `NodeInfo.PrintableName()`. With address granularity that string is
```
000000010010bfb4 example.com/foo.BenchmarkWork /tmp/foo_test.go:14
```
so `^example.com/foo\.BenchmarkWork$` cannot match.
`list` / weblist compare `n.Info.Name` only, which is why Source still works.
`internal/report.symbolsFromBinaries` skips mappings unless PrintableName matches:
```go
if name := n.Info.PrintableName(); rx.MatchString(name) && n.Info.Objfile != "" {
fileHasSamplesAndMatched[n.Info.Objfile] = true
}
```
For `disasm`, `applyCommandOverrides` sets `Granularity = "addresses"`. If that filter fails, `ObjFile.Symbols(rx)` is never called. That API matches binary symbol names (`mypkg.MyFunc`), which would match `^mypkg\.MyFunc$`.
The function is in the profile and in the binary; the filter uses the wrong name.
## Related (not duplicates)
- google/pprof#789: `/disasm` with empty/nil `f=`. This report is `f=^funcname$` from the `go tool pprof` web UI; Source still works.
- golang/go#50891 / google/pprof#709: darwin PIE address mapping. Unanchored `go tool pprof` `disasm Func` works here once the test binary is passed.
- google/pprof#914: OSX weblist vs `_`-prefixed Mach-O names.
- google/pprof#230: inlined functions have no standalone symbol.
- google/pprof#796: introduced `pprofQuoteMeta` (`^name$`) for Focus; that is what the Disassemble button now sends.
Contributor guide
Research direction
Trace internal/report.symbolsFromBinaries and applyCommandOverrides, then inspect how the web UI's pprofQuoteMeta-generated f= value reaches Disassemble. Run the provided go test -c and go tool pprof reproduction; done means clicking Disassemble works for the selected function while Source and existing CLI matching remain functional.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, javascript
- Domain
- cli, performance, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100