Browse Source

Add source_path option to point pprof to source files

Currently pprof will look for source files only on the current directory
and its parents. This makes it hard to examine sources on jobs where
there are multiple source trees (eg from different libraries).

Add a variable to provide a search path for source files. It will default
to the cwd, so there will be no change in behavior by default.
Raul Silvera 9 years ago
parent
commit
303a27381d

+ 1
- 0
internal/driver/commands.go View File

152
 		" For memory profiles, use megabytes, kilobytes, bytes, etc.",
152
 		" For memory profiles, use megabytes, kilobytes, bytes, etc.",
153
 		" auto will scale each value independently to the most natural unit.")},
153
 		" auto will scale each value independently to the most natural unit.")},
154
 	"compact_labels": &variable{boolKind, "f", "", "Show minimal headers"},
154
 	"compact_labels": &variable{boolKind, "f", "", "Show minimal headers"},
155
+	"source_path":    &variable{stringKind, "", "", "Search path for source files"},
155
 
156
 
156
 	// Filtering options
157
 	// Filtering options
157
 	"nodecount": &variable{intKind, "-1", "", helpText(
158
 	"nodecount": &variable{intKind, "-1", "", helpText(

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

235
 		SampleUnit:  sample.Unit,
235
 		SampleUnit:  sample.Unit,
236
 
236
 
237
 		OutputUnit: vars["unit"].value,
237
 		OutputUnit: vars["unit"].value,
238
+
239
+		SourcePath: vars["source_path"].stringValue(),
238
 	}
240
 	}
239
 
241
 
240
 	if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
242
 	if len(p.Mapping) > 0 && p.Mapping[0].File != "" {

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

167
 			if o.boolValue() == false {
167
 			if o.boolValue() == false {
168
 				continue
168
 				continue
169
 			}
169
 			}
170
+		case n == "source_path":
171
+			continue
170
 		}
172
 		}
171
 		args = append(args, fmt.Sprintf("  %-25s : %s", n, v))
173
 		args = append(args, fmt.Sprintf("  %-25s : %s", n, v))
172
 	}
174
 	}

+ 2
- 1
internal/report/report.go View File

881
 
881
 
882
 	OutputUnit string // Units for data formatting in report.
882
 	OutputUnit string // Units for data formatting in report.
883
 
883
 
884
-	Symbol *regexp.Regexp // Symbols to include on disassembly report.
884
+	Symbol     *regexp.Regexp // Symbols to include on disassembly report.
885
+	SourcePath string         // Search path for source files.
885
 }
886
 }
886
 
887
 
887
 // New builds a new report indexing the sample values interpreting the
888
 // New builds a new report indexing the sample values interpreting the

+ 39
- 17
internal/report/source.go View File

55
 	}
55
 	}
56
 	functions.Sort(graph.NameOrder)
56
 	functions.Sort(graph.NameOrder)
57
 
57
 
58
+	sourcePath := o.SourcePath
59
+	if sourcePath == "" {
60
+		wd, err := os.Getwd()
61
+		if err != nil {
62
+			return fmt.Errorf("Could not stat current dir: %v", err)
63
+		}
64
+		sourcePath = wd
65
+	}
66
+
58
 	fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total))
67
 	fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total))
59
 	for _, fn := range functions {
68
 	for _, fn := range functions {
60
 		name := fn.Info.Name
69
 		name := fn.Info.Name
86
 			fns := fileNodes[filename]
95
 			fns := fileNodes[filename]
87
 			flatSum, cumSum := fns.Sum()
96
 			flatSum, cumSum := fns.Sum()
88
 
97
 
89
-			fnodes, path, err := getFunctionSource(name, filename, fns, 0, 0)
98
+			fnodes, path, err := getFunctionSource(name, filename, sourcePath, fns, 0, 0)
90
 			fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path)
99
 			fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path)
91
 			fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
100
 			fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
92
 				rpt.formatValue(flatSum), rpt.formatValue(cumSum),
101
 				rpt.formatValue(flatSum), rpt.formatValue(cumSum),
118
 		address = &hex
127
 		address = &hex
119
 	}
128
 	}
120
 
129
 
130
+	sourcePath := o.SourcePath
131
+	if sourcePath == "" {
132
+		wd, err := os.Getwd()
133
+		if err != nil {
134
+			return fmt.Errorf("Could not stat current dir: %v", err)
135
+		}
136
+		sourcePath = wd
137
+	}
138
+
121
 	// Extract interesting symbols from binary files in the profile and
139
 	// Extract interesting symbols from binary files in the profile and
122
 	// classify samples per symbol.
140
 	// classify samples per symbol.
123
 	symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj)
141
 	symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj)
166
 			asm := assemblyPerSourceLine(symbols, fns, filename, obj)
184
 			asm := assemblyPerSourceLine(symbols, fns, filename, obj)
167
 			start, end := sourceCoordinates(asm)
185
 			start, end := sourceCoordinates(asm)
168
 
186
 
169
-			fnodes, path, err := getFunctionSource(name, filename, fns, start, end)
187
+			fnodes, path, err := getFunctionSource(name, filename, sourcePath, fns, start, end)
170
 			if err != nil {
188
 			if err != nil {
171
 				fnodes, path = getMissingFunctionSource(filename, asm, start, end)
189
 				fnodes, path = getMissingFunctionSource(filename, asm, start, end)
172
 			}
190
 			}
319
 // getFunctionSource collects the sources of a function from a source
337
 // getFunctionSource collects the sources of a function from a source
320
 // file and annotates it with the samples in fns. Returns the sources
338
 // file and annotates it with the samples in fns. Returns the sources
321
 // as nodes, using the info.name field to hold the source code.
339
 // as nodes, using the info.name field to hold the source code.
322
-func getFunctionSource(fun, file string, fns graph.Nodes, start, end int) (graph.Nodes, string, error) {
323
-	f, file, err := adjustSourcePath(file)
340
+func getFunctionSource(fun, file, sourcePath string, fns graph.Nodes, start, end int) (graph.Nodes, string, error) {
341
+	f, file, err := openSourceFile(file, sourcePath)
324
 	if err != nil {
342
 	if err != nil {
325
 		return nil, file, err
343
 		return nil, file, err
326
 	}
344
 	}
411
 	return fnodes, filename
429
 	return fnodes, filename
412
 }
430
 }
413
 
431
 
414
-// adjustSourcePath adjusts the path for a source file by trimmming
415
-// known prefixes and searching for the file on all parents of the
416
-// current working dir.
417
-func adjustSourcePath(path string) (*os.File, string, error) {
432
+// openSourceFile opens a source file from a name encoded in a
433
+// profile. File names in a profile after often relative paths, so
434
+// search them in each of the paths in sourcePath (or CWD by default),
435
+// and their parents.
436
+func openSourceFile(path string, sourcePath string) (*os.File, string, error) {
418
 	path = trimPath(path)
437
 	path = trimPath(path)
419
-	f, err := os.Open(path)
420
-	if err == nil {
421
-		return f, path, nil
438
+
439
+	if filepath.IsAbs(path) {
440
+		f, err := os.Open(path)
441
+		return f, path, err
422
 	}
442
 	}
423
 
443
 
424
-	if dir, wderr := os.Getwd(); wderr == nil {
444
+	// Scan each component of the path
445
+	for _, dir := range strings.Split(sourcePath, ":") {
446
+		// Search up for every parent of each possible path.
425
 		for {
447
 		for {
448
+			filename := filepath.Join(dir, path)
449
+			if f, err := os.Open(filename); err == nil {
450
+				return f, filename, nil
451
+			}
426
 			parent := filepath.Dir(dir)
452
 			parent := filepath.Dir(dir)
427
 			if parent == dir {
453
 			if parent == dir {
428
 				break
454
 				break
429
 			}
455
 			}
430
-			if f, err := os.Open(filepath.Join(parent, path)); err == nil {
431
-				return f, filepath.Join(parent, path), nil
432
-			}
433
-
434
 			dir = parent
456
 			dir = parent
435
 		}
457
 		}
436
 	}
458
 	}
437
 
459
 
438
-	return nil, path, err
460
+	return nil, "", fmt.Errorf("Could not find file %s on path %s", path, sourcePath)
439
 }
461
 }
440
 
462
 
441
 // trimPath cleans up a path by removing prefixes that are commonly
463
 // trimPath cleans up a path by removing prefixes that are commonly