Record the selected sample type as default_sample_type in -proto output. (#1055) `pprof -proto -sample_index=X` (and aliases such as `-alloc_space`) emits the profile with its original `default_sample_type`, so the selection is lost: anything consuming the output opens on the original default and the user has to re-select the metric by hand. Set `default_sample_type` in the emitted profile to the sample type the report was generated for. Without `-sample_index` that is the profile's own default (or the last sample type if none), so this only normalizes the field in that case. To make `Options.SampleType` usable as data, it now carries the real sample type name; the `mean_` prefix that `-mean` reports show is added at the display sites instead of being baked into the option. Fixes b/341528366.
diff --git a/internal/driver/driver.go b/internal/driver/driver.go index 99e6ba4..6d182b7 100644 --- a/internal/driver/driver.go +++ b/internal/driver/driver.go
@@ -288,11 +288,6 @@ return nil, err } - stype := sample.Type - if mean { - stype = "mean_" + stype - } - if cfg.DivideBy == 0 { return nil, fmt.Errorf("zero divisor specified") } @@ -330,7 +325,7 @@ SampleValue: value, SampleMeanDivisor: meanDiv, - SampleType: stype, + SampleType: sample.Type, SampleUnit: sample.Unit, OutputUnit: cfg.Unit,
diff --git a/internal/driver/driver_test.go b/internal/driver/driver_test.go index d4efa75..21c8c61 100644 --- a/internal/driver/driver_test.go +++ b/internal/driver/driver_test.go
@@ -88,6 +88,8 @@ {"traces", "cpu"}, {"traces,addresses", "cpu"}, {"traces", "heap_tags"}, + {"raw", "heap"}, + {"raw,sample_index=inuse_objects", "heap"}, {"dot,alloc_space,flat,focus=[234]00", "heap_alloc"}, {"dot,alloc_space,flat,tagshow=[2]00", "heap_alloc"}, {"dot,alloc_space,flat,hide=line.*1?23?", "heap_alloc"}, @@ -131,6 +133,13 @@ f.bools["topproto"] = true f.bools["addresses"] = true } + // The sample index also applies to the proto-generating invocation, + // so that the generated profile records the selection. + for _, flag := range flags { + if si, ok := strings.CutPrefix(flag, "sample_index="); ok { + f.strings["sample_index"] = si + } + } // First pprof invocation to save the profile into a profile.proto. // Pass in flag set hen setting defaults, because otherwise default @@ -192,7 +201,9 @@ if err != nil { t.Fatalf("reading solution file %s: %v", solution, err) } - if runtime.GOOS == "windows" { + // The raw output prints file names as stored in the profile, so it + // does not use OS-specific path separators. + if runtime.GOOS == "windows" && flags[0] != "raw" { if flags[0] == "dot" { // The .dot test has the paths inside strings, so \ must be escaped. sbuf = bytes.ReplaceAll(sbuf, []byte("testdata/"), []byte(`testdata\\`)) @@ -267,10 +278,13 @@ name = addString(name, f, []string{"functions", "filefunctions", "files", "lines", "addresses"}) name = addString(name, f, []string{"noinlines"}) name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"}) + if f.strings["sample_index"] != "" { + name = append(name, "sample_index") + } name = addString(name, f, []string{"relative_percentages"}) name = addString(name, f, []string{"seconds"}) name = addString(name, f, []string{"call_tree"}) - name = addString(name, f, []string{"text", "tree", "callgrind", "dot", "svg", "tags", "dot", "traces", "disasm", "peek", "weblist", "topproto", "comments"}) + name = addString(name, f, []string{"text", "tree", "callgrind", "dot", "svg", "tags", "dot", "traces", "disasm", "peek", "weblist", "topproto", "comments", "raw"}) name = addString(name, f, []string{"all_frames"}) if f.strings["focus"] != "" || f.strings["tagfocus"] != "" { name = append(name, "focus")
diff --git a/internal/driver/testdata/pprof.heap.raw b/internal/driver/testdata/pprof.heap.raw new file mode 100644 index 0000000..936cfad --- /dev/null +++ b/internal/driver/testdata/pprof.heap.raw
@@ -0,0 +1,27 @@ +Comment: comment +Comment: #hidden comment +PeriodType: allocations bytes +Period: 524288 +Samples: +inuse_objects/count inuse_space/bytes[dflt] + 10 1024000: 1000 2000 3000 + bytes:[102400] + 20 4096000: 1000 3001 + bytes:[204800] + 40 65536000: 2000 3002 + bytes:[1638400] + 80 32768000: 3000 + bytes:[409600] +Locations + 1000: 0x1000 M=1 line1000 testdata/file1000.src:1:0 s=0(mangled1000) + 2000: 0x2000 M=1 line2001 testdata/file2000.src:2:0 s=0(mangled2001) + line2000 testdata/file2000.src:3:0 s=0(mangled2000) + 3000: 0x3000 M=1 line3002 testdata/file3000.src:3:0 s=0(mangled3002) + line3001 testdata/file3000.src:2:0 s=0(mangled3001) + line3000 testdata/file3000.src:4:0 s=0(mangled3000) + 3001: 0x3001 M=1 line3001 testdata/file3000.src:2:0 s=0(mangled3001) + line3000 testdata/file3000.src:4:0 s=0(mangled3000) + 3002: 0x3002 M=1 line3002 testdata/file3000.src:3:0 s=0(mangled3002) + line3000 testdata/file3000.src:4:0 s=0(mangled3000) +Mappings +1: 0x1000/0x4000/0x0 buildid [FN][FL][LN][IN]
diff --git a/internal/driver/testdata/pprof.heap.sample_index.raw b/internal/driver/testdata/pprof.heap.sample_index.raw new file mode 100644 index 0000000..2c118ea --- /dev/null +++ b/internal/driver/testdata/pprof.heap.sample_index.raw
@@ -0,0 +1,27 @@ +Comment: comment +Comment: #hidden comment +PeriodType: allocations bytes +Period: 524288 +Samples: +inuse_objects/count[dflt] inuse_space/bytes + 10 1024000: 1000 2000 3000 + bytes:[102400] + 20 4096000: 1000 3001 + bytes:[204800] + 40 65536000: 2000 3002 + bytes:[1638400] + 80 32768000: 3000 + bytes:[409600] +Locations + 1000: 0x1000 M=1 line1000 testdata/file1000.src:1:0 s=0(mangled1000) + 2000: 0x2000 M=1 line2001 testdata/file2000.src:2:0 s=0(mangled2001) + line2000 testdata/file2000.src:3:0 s=0(mangled2000) + 3000: 0x3000 M=1 line3002 testdata/file3000.src:3:0 s=0(mangled3002) + line3001 testdata/file3000.src:2:0 s=0(mangled3001) + line3000 testdata/file3000.src:4:0 s=0(mangled3000) + 3001: 0x3001 M=1 line3001 testdata/file3000.src:2:0 s=0(mangled3001) + line3000 testdata/file3000.src:4:0 s=0(mangled3000) + 3002: 0x3002 M=1 line3002 testdata/file3000.src:3:0 s=0(mangled3002) + line3000 testdata/file3000.src:4:0 s=0(mangled3000) +Mappings +1: 0x1000/0x4000/0x0 buildid [FN][FL][LN][IN]
diff --git a/internal/report/report.go b/internal/report/report.go index 47155ba..8bf6457 100644 --- a/internal/report/report.go +++ b/internal/report/report.go
@@ -306,9 +306,21 @@ } } } + // Make the selected sample type the default of the saved profile, so that + // its consumers open on the same sample type. + p.DefaultSampleType = o.SampleType return p.Write(w) } +// sampleTypeLabel returns the sample type as displayed in reports, i.e. with a +// "mean_" prefix if the report shows per-sample means. +func (o *Options) sampleTypeLabel() string { + if o.SampleMeanDivisor != nil { + return "mean_" + o.SampleType + } + return o.SampleType +} + // printTopProto writes a list of the hottest routines in a profile as a profile.proto. func printTopProto(w io.Writer, rpt *Report) error { p := rpt.prof @@ -946,7 +958,7 @@ nodeNames := getDisambiguatedNames(g) fmt.Fprintln(w, "positions: instr line") - fmt.Fprintln(w, "events:", o.SampleType+"("+o.OutputUnit+")") + fmt.Fprintln(w, "events:", o.sampleTypeLabel()+"("+o.OutputUnit+")") objfiles := make(map[string]int) files := make(map[string]int) @@ -1182,7 +1194,7 @@ } } if o.SampleType != "" { - label = append(label, "Type: "+o.SampleType) + label = append(label, "Type: "+o.sampleTypeLabel()) } if url := prof.DocURL; url != "" { label = append(label, "Doc: "+url)
diff --git a/internal/report/stacks.go b/internal/report/stacks.go index dbf20be..b40c91f 100644 --- a/internal/report/stacks.go +++ b/internal/report/stacks.go
@@ -92,7 +92,7 @@ s := &StackSet{ Total: rpt.total, Scale: scale, - Type: rpt.options.SampleType, + Type: rpt.options.sampleTypeLabel(), Unit: unit, Stacks: []Stack{}, // Ensure non-nil Sources: []StackSource{}, // Ensure non-nil