Pārlūkot izejas kodu

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 gadus atpakaļ
vecāks
revīzija
303a27381d

+ 1
- 0
internal/driver/commands.go Parādīt failu

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

+ 2
- 0
internal/driver/driver.go Parādīt failu

@@ -235,6 +235,8 @@ func reportOptions(p *profile.Profile, vars variables) (*report.Options, error)
235 235
 		SampleUnit:  sample.Unit,
236 236
 
237 237
 		OutputUnit: vars["unit"].value,
238
+
239
+		SourcePath: vars["source_path"].stringValue(),
238 240
 	}
239 241
 
240 242
 	if len(p.Mapping) > 0 && p.Mapping[0].File != "" {

+ 2
- 0
internal/driver/interactive.go Parādīt failu

@@ -167,6 +167,8 @@ func pprofPrompt(p *profile.Profile) string {
167 167
 			if o.boolValue() == false {
168 168
 				continue
169 169
 			}
170
+		case n == "source_path":
171
+			continue
170 172
 		}
171 173
 		args = append(args, fmt.Sprintf("  %-25s : %s", n, v))
172 174
 	}

+ 2
- 1
internal/report/report.go Parādīt failu

@@ -881,7 +881,8 @@ type Options struct {
881 881
 
882 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 888
 // New builds a new report indexing the sample values interpreting the

+ 39
- 17
internal/report/source.go Parādīt failu

@@ -55,6 +55,15 @@ func printSource(w io.Writer, rpt *Report) error {
55 55
 	}
56 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 67
 	fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total))
59 68
 	for _, fn := range functions {
60 69
 		name := fn.Info.Name
@@ -86,7 +95,7 @@ func printSource(w io.Writer, rpt *Report) error {
86 95
 			fns := fileNodes[filename]
87 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 99
 			fmt.Fprintf(w, "ROUTINE ======================== %s in %s\n", name, path)
91 100
 			fmt.Fprintf(w, "%10s %10s (flat, cum) %s of Total\n",
92 101
 				rpt.formatValue(flatSum), rpt.formatValue(cumSum),
@@ -118,6 +127,15 @@ func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
118 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 139
 	// Extract interesting symbols from binary files in the profile and
122 140
 	// classify samples per symbol.
123 141
 	symbols := symbolsFromBinaries(rpt.prof, g, o.Symbol, address, obj)
@@ -166,7 +184,7 @@ func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
166 184
 			asm := assemblyPerSourceLine(symbols, fns, filename, obj)
167 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 188
 			if err != nil {
171 189
 				fnodes, path = getMissingFunctionSource(filename, asm, start, end)
172 190
 			}
@@ -319,8 +337,8 @@ func printPageClosing(w io.Writer) {
319 337
 // getFunctionSource collects the sources of a function from a source
320 338
 // file and annotates it with the samples in fns. Returns the sources
321 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 342
 	if err != nil {
325 343
 		return nil, file, err
326 344
 	}
@@ -411,31 +429,35 @@ func getMissingFunctionSource(filename string, asm map[int]graph.Nodes, start, e
411 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 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 447
 		for {
448
+			filename := filepath.Join(dir, path)
449
+			if f, err := os.Open(filename); err == nil {
450
+				return f, filename, nil
451
+			}
426 452
 			parent := filepath.Dir(dir)
427 453
 			if parent == dir {
428 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 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 463
 // trimPath cleans up a path by removing prefixes that are commonly