Procházet zdrojové kódy

Add default_sample_type field

Add field for profile sources to indicate what is the primary
sample value, which visualizers should display by default.
Previously there was a convention for pprof to use the last
sample_index by default, this provides more flexibility.
Raul Silvera před 9 roky
rodič
revize
d1874856a4

+ 25
- 8
internal/driver/commands.go Zobrazit soubor

@@ -60,6 +60,10 @@ func (c *command) help(name string) string {
60 60
 	return message + "\n"
61 61
 }
62 62
 
63
+// AddCommand adds an additional command to the set of commands
64
+// accepted by pprof. This enables extensions to add new commands for
65
+// specialized visualization formats. If the command specified already
66
+// exists, it is overwritten.
63 67
 func AddCommand(cmd string, format int, post PostProcessor, desc, usage string) {
64 68
 	pprofCommands[cmd] = &command{format, post, false, desc, usage}
65 69
 }
@@ -196,9 +200,9 @@ var pprofVariables = variables{
196 200
 		"For memory profiles, report average memory per allocation.",
197 201
 		"For time-based profiles, report average time per event.")},
198 202
 	"sample_index": &variable{stringKind, "", "", helpText(
199
-		"Sample value to report",
203
+		"Sample value to report (0-based index or name)",
200 204
 		"Profiles contain multiple values per sample.",
201
-		"Use sample_value=index to select the ith value or select it by name.")},
205
+		"Use sample_index=i to select the ith value (starting at 0).")},
202 206
 
203 207
 	// Data sorting criteria
204 208
 	"flat": &variable{boolKind, "t", "cumulative", helpText("Sort entries based on own weight")},
@@ -325,9 +329,9 @@ func browsers() []string {
325 329
 	case "windows":
326 330
 		return append(cmds, "cmd /c start")
327 331
 	default:
328
-		user_browser := os.Getenv("BROWSER")
329
-		if user_browser != "" {
330
-			cmds = append([]string{user_browser, "sensible-browser"}, cmds...)
332
+		userBrowser := os.Getenv("BROWSER")
333
+		if userBrowser != "" {
334
+			cmds = append([]string{userBrowser, "sensible-browser"}, cmds...)
331 335
 		} else {
332 336
 			cmds = append([]string{"sensible-browser"}, cmds...)
333 337
 		}
@@ -439,6 +443,13 @@ func invokeVisualizer(format PostProcessor, suffix string, visualizers []string)
439 443
 // profile sample types.
440 444
 func locateSampleIndex(p *profile.Profile, sampleIndex string) (int, error) {
441 445
 	if sampleIndex == "" {
446
+		if dst := p.DefaultSampleType; dst != "" {
447
+			for i, t := range sampleTypes(p) {
448
+				if t == dst {
449
+					return i, nil
450
+				}
451
+			}
452
+		}
442 453
 		// By default select the last sample value
443 454
 		return len(p.SampleType) - 1, nil
444 455
 	}
@@ -453,15 +464,21 @@ func locateSampleIndex(p *profile.Profile, sampleIndex string) (int, error) {
453 464
 	// "inuse_space" and "inuse_objects" for profiles containing types
454 465
 	// "space" and "objects".
455 466
 	noInuse := strings.TrimPrefix(sampleIndex, "inuse_")
456
-	sampleTypes := make([]string, len(p.SampleType))
457 467
 	for i, t := range p.SampleType {
458 468
 		if t.Type == sampleIndex || t.Type == noInuse {
459 469
 			return i, nil
460 470
 		}
461
-		sampleTypes[i] = t.Type
462 471
 	}
463 472
 
464
-	return 0, fmt.Errorf("sample_index %q must be one of: %v", sampleIndex, sampleTypes)
473
+	return 0, fmt.Errorf("sample_index %q must be one of: %v", sampleIndex, sampleTypes(p))
474
+}
475
+
476
+func sampleTypes(p *profile.Profile) []string {
477
+	types := make([]string, len(p.SampleType))
478
+	for i, t := range p.SampleType {
479
+		types[i] = t.Type
480
+	}
481
+	return types
465 482
 }
466 483
 
467 484
 // variables describe the configuration parameters recognized by pprof.

+ 2
- 0
internal/driver/interactive.go Zobrazit soubor

@@ -31,6 +31,7 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
31 31
 	// Enter command processing loop.
32 32
 	o.UI.SetAutoComplete(newCompleter(functionNames(p)))
33 33
 	pprofVariables.set("compact_labels", "true")
34
+	pprofVariables["sample_index"].help += fmt.Sprintf("Or use sample_index=name, with name in %v.\n", sampleTypes(p))
34 35
 
35 36
 	// Do not wait for the visualizer to complete, to allow multiple
36 37
 	// graphs to be visualized simultaneously.
@@ -99,6 +100,7 @@ func greetings(p *profile.Profile, ui plugin.UI) {
99 100
 	ropt, err := reportOptions(p, pprofVariables)
100 101
 	if err == nil {
101 102
 		ui.Print(strings.Join(report.ProfileLabels(report.New(p, ropt)), "\n"))
103
+		ui.Print(fmt.Sprintf("Sample types: %v\n", sampleTypes(p)))
102 104
 	}
103 105
 	ui.Print("Entering interactive mode (type \"help\" for commands)")
104 106
 }

+ 6
- 0
profile/encode.go Zobrazit soubor

@@ -114,6 +114,8 @@ func (p *Profile) preEncode() {
114 114
 		p.commentX = append(p.commentX, addString(strings, c))
115 115
 	}
116 116
 
117
+	p.defaultSampleTypeX = addString(strings, p.DefaultSampleType)
118
+
117 119
 	p.stringTable = make([]string, len(strings))
118 120
 	for s, i := range strings {
119 121
 		p.stringTable[i] = s
@@ -146,6 +148,7 @@ func (p *Profile) encode(b *buffer) {
146 148
 	}
147 149
 	encodeInt64Opt(b, 12, p.Period)
148 150
 	encodeInt64s(b, 13, p.commentX)
151
+	encodeInt64(b, 14, p.defaultSampleTypeX)
149 152
 }
150 153
 
151 154
 var profileDecoder = []decoder{
@@ -215,6 +218,8 @@ var profileDecoder = []decoder{
215 218
 	func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).Period) },
216 219
 	// repeated int64 comment = 13
217 220
 	func(b *buffer, m message) error { return decodeInt64s(b, &m.(*Profile).commentX) },
221
+	// int64 defaultSampleType = 14
222
+	func(b *buffer, m message) error { return decodeInt64(b, &m.(*Profile).defaultSampleTypeX) },
218 223
 }
219 224
 
220 225
 // postDecode takes the unexported fields populated by decode (with
@@ -303,6 +308,7 @@ func (p *Profile) postDecode() error {
303 308
 	}
304 309
 
305 310
 	p.commentX = nil
311
+	p.DefaultSampleType, err = getString(p.stringTable, &p.defaultSampleTypeX, err)
306 312
 	p.stringTable = nil
307 313
 	return err
308 314
 }

+ 6
- 1
profile/merge.go Zobrazit soubor

@@ -336,6 +336,7 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
336 336
 
337 337
 	var timeNanos, durationNanos, period int64
338 338
 	var comments []string
339
+	var defaultSampleType string
339 340
 	for _, s := range srcs {
340 341
 		if timeNanos == 0 || s.TimeNanos < timeNanos {
341 342
 			timeNanos = s.TimeNanos
@@ -345,6 +346,9 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
345 346
 			period = s.Period
346 347
 		}
347 348
 		comments = append(comments, s.Comments...)
349
+		if defaultSampleType == "" {
350
+			defaultSampleType = s.DefaultSampleType
351
+		}
348 352
 	}
349 353
 
350 354
 	p := &Profile{
@@ -358,7 +362,8 @@ func combineHeaders(srcs []*Profile) (*Profile, error) {
358 362
 		PeriodType:    srcs[0].PeriodType,
359 363
 		Period:        period,
360 364
 
361
-		Comments: comments,
365
+		Comments:          comments,
366
+		DefaultSampleType: defaultSampleType,
362 367
 	}
363 368
 	copy(p.SampleType, srcs[0].SampleType)
364 369
 	return p, nil

+ 17
- 11
profile/profile.go Zobrazit soubor

@@ -29,12 +29,13 @@ import (
29 29
 
30 30
 // Profile is an in-memory representation of profile.proto.
31 31
 type Profile struct {
32
-	SampleType []*ValueType
33
-	Sample     []*Sample
34
-	Mapping    []*Mapping
35
-	Location   []*Location
36
-	Function   []*Function
37
-	Comments   []string
32
+	SampleType        []*ValueType
33
+	DefaultSampleType string
34
+	Sample            []*Sample
35
+	Mapping           []*Mapping
36
+	Location          []*Location
37
+	Function          []*Function
38
+	Comments          []string
38 39
 
39 40
 	DropFrames string
40 41
 	KeepFrames string
@@ -44,10 +45,11 @@ type Profile struct {
44 45
 	PeriodType    *ValueType
45 46
 	Period        int64
46 47
 
47
-	commentX    []int64
48
-	dropFramesX int64
49
-	keepFramesX int64
50
-	stringTable []string
48
+	commentX           []int64
49
+	dropFramesX        int64
50
+	keepFramesX        int64
51
+	stringTable        []string
52
+	defaultSampleTypeX int64
51 53
 }
52 54
 
53 55
 // ValueType corresponds to Profile.ValueType
@@ -404,7 +406,11 @@ func (p *Profile) String() string {
404 406
 	ss = append(ss, "Samples:")
405 407
 	var sh1 string
406 408
 	for _, s := range p.SampleType {
407
-		sh1 = sh1 + fmt.Sprintf("%s/%s ", s.Type, s.Unit)
409
+		dflt := ""
410
+		if s.Type == p.DefaultSampleType {
411
+			dflt = "[dflt]"
412
+		}
413
+		sh1 = sh1 + fmt.Sprintf("%s/%s%s ", s.Type, s.Unit, dflt)
408 414
 	}
409 415
 	ss = append(ss, strings.TrimSpace(sh1))
410 416
 	for _, s := range p.Sample {

+ 4
- 1
proto/profile.proto Zobrazit soubor

@@ -50,7 +50,7 @@ message Profile {
50 50
   repeated Function function = 5;
51 51
   // A common table for strings referenced by various messages.
52 52
   // string_table[0] must always be "".
53
-  repeated string string_table = 6 [enforce_utf8 = false];
53
+  repeated string string_table = 6;
54 54
   // frames with Function.function_name fully matching the following
55 55
   // regexp will be dropped from the samples, along with their successors.
56 56
   int64 drop_frames = 7;   // Index into string table.
@@ -72,6 +72,9 @@ message Profile {
72 72
   int64 period = 12;
73 73
   // Freeform text associated to the profile.
74 74
   repeated int64 comment = 13; // Indices into string table.
75
+  // Index into the string table of the type of the preferred sample
76
+  // value. If unset, clients should default to the last sample value.
77
+  int64 default_sample_type = 14;
75 78
 }
76 79
 
77 80
 // ValueType describes the semantics and measurement units of a value.