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