Browse Source

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 9 years ago
parent
commit
d1874856a4
6 changed files with 60 additions and 21 deletions
  1. 25
    8
      internal/driver/commands.go
  2. 2
    0
      internal/driver/interactive.go
  3. 6
    0
      profile/encode.go
  4. 6
    1
      profile/merge.go
  5. 17
    11
      profile/profile.go
  6. 4
    1
      proto/profile.proto

+ 25
- 8
internal/driver/commands.go View File

60
 	return message + "\n"
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
 func AddCommand(cmd string, format int, post PostProcessor, desc, usage string) {
67
 func AddCommand(cmd string, format int, post PostProcessor, desc, usage string) {
64
 	pprofCommands[cmd] = &command{format, post, false, desc, usage}
68
 	pprofCommands[cmd] = &command{format, post, false, desc, usage}
65
 }
69
 }
196
 		"For memory profiles, report average memory per allocation.",
200
 		"For memory profiles, report average memory per allocation.",
197
 		"For time-based profiles, report average time per event.")},
201
 		"For time-based profiles, report average time per event.")},
198
 	"sample_index": &variable{stringKind, "", "", helpText(
202
 	"sample_index": &variable{stringKind, "", "", helpText(
199
-		"Sample value to report",
203
+		"Sample value to report (0-based index or name)",
200
 		"Profiles contain multiple values per sample.",
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
 	// Data sorting criteria
207
 	// Data sorting criteria
204
 	"flat": &variable{boolKind, "t", "cumulative", helpText("Sort entries based on own weight")},
208
 	"flat": &variable{boolKind, "t", "cumulative", helpText("Sort entries based on own weight")},
325
 	case "windows":
329
 	case "windows":
326
 		return append(cmds, "cmd /c start")
330
 		return append(cmds, "cmd /c start")
327
 	default:
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
 		} else {
335
 		} else {
332
 			cmds = append([]string{"sensible-browser"}, cmds...)
336
 			cmds = append([]string{"sensible-browser"}, cmds...)
333
 		}
337
 		}
439
 // profile sample types.
443
 // profile sample types.
440
 func locateSampleIndex(p *profile.Profile, sampleIndex string) (int, error) {
444
 func locateSampleIndex(p *profile.Profile, sampleIndex string) (int, error) {
441
 	if sampleIndex == "" {
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
 		// By default select the last sample value
453
 		// By default select the last sample value
443
 		return len(p.SampleType) - 1, nil
454
 		return len(p.SampleType) - 1, nil
444
 	}
455
 	}
453
 	// "inuse_space" and "inuse_objects" for profiles containing types
464
 	// "inuse_space" and "inuse_objects" for profiles containing types
454
 	// "space" and "objects".
465
 	// "space" and "objects".
455
 	noInuse := strings.TrimPrefix(sampleIndex, "inuse_")
466
 	noInuse := strings.TrimPrefix(sampleIndex, "inuse_")
456
-	sampleTypes := make([]string, len(p.SampleType))
457
 	for i, t := range p.SampleType {
467
 	for i, t := range p.SampleType {
458
 		if t.Type == sampleIndex || t.Type == noInuse {
468
 		if t.Type == sampleIndex || t.Type == noInuse {
459
 			return i, nil
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
 // variables describe the configuration parameters recognized by pprof.
484
 // variables describe the configuration parameters recognized by pprof.

+ 2
- 0
internal/driver/interactive.go View File

31
 	// Enter command processing loop.
31
 	// Enter command processing loop.
32
 	o.UI.SetAutoComplete(newCompleter(functionNames(p)))
32
 	o.UI.SetAutoComplete(newCompleter(functionNames(p)))
33
 	pprofVariables.set("compact_labels", "true")
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
 	// Do not wait for the visualizer to complete, to allow multiple
36
 	// Do not wait for the visualizer to complete, to allow multiple
36
 	// graphs to be visualized simultaneously.
37
 	// graphs to be visualized simultaneously.
99
 	ropt, err := reportOptions(p, pprofVariables)
100
 	ropt, err := reportOptions(p, pprofVariables)
100
 	if err == nil {
101
 	if err == nil {
101
 		ui.Print(strings.Join(report.ProfileLabels(report.New(p, ropt)), "\n"))
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
 	ui.Print("Entering interactive mode (type \"help\" for commands)")
105
 	ui.Print("Entering interactive mode (type \"help\" for commands)")
104
 }
106
 }

+ 6
- 0
profile/encode.go View File

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

+ 6
- 1
profile/merge.go View File

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

+ 17
- 11
profile/profile.go View File

29
 
29
 
30
 // Profile is an in-memory representation of profile.proto.
30
 // Profile is an in-memory representation of profile.proto.
31
 type Profile struct {
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
 	DropFrames string
40
 	DropFrames string
40
 	KeepFrames string
41
 	KeepFrames string
44
 	PeriodType    *ValueType
45
 	PeriodType    *ValueType
45
 	Period        int64
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
 // ValueType corresponds to Profile.ValueType
55
 // ValueType corresponds to Profile.ValueType
404
 	ss = append(ss, "Samples:")
406
 	ss = append(ss, "Samples:")
405
 	var sh1 string
407
 	var sh1 string
406
 	for _, s := range p.SampleType {
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
 	ss = append(ss, strings.TrimSpace(sh1))
415
 	ss = append(ss, strings.TrimSpace(sh1))
410
 	for _, s := range p.Sample {
416
 	for _, s := range p.Sample {

+ 4
- 1
proto/profile.proto View File

50
   repeated Function function = 5;
50
   repeated Function function = 5;
51
   // A common table for strings referenced by various messages.
51
   // A common table for strings referenced by various messages.
52
   // string_table[0] must always be "".
52
   // string_table[0] must always be "".
53
-  repeated string string_table = 6 [enforce_utf8 = false];
53
+  repeated string string_table = 6;
54
   // frames with Function.function_name fully matching the following
54
   // frames with Function.function_name fully matching the following
55
   // regexp will be dropped from the samples, along with their successors.
55
   // regexp will be dropped from the samples, along with their successors.
56
   int64 drop_frames = 7;   // Index into string table.
56
   int64 drop_frames = 7;   // Index into string table.
72
   int64 period = 12;
72
   int64 period = 12;
73
   // Freeform text associated to the profile.
73
   // Freeform text associated to the profile.
74
   repeated int64 comment = 13; // Indices into string table.
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
 // ValueType describes the semantics and measurement units of a value.
80
 // ValueType describes the semantics and measurement units of a value.