|
@@ -63,7 +63,7 @@ func printSource(w io.Writer, rpt *Report) error {
|
63
|
63
|
}
|
64
|
64
|
sourcePath = wd
|
65
|
65
|
}
|
66
|
|
- reader := newSourceReader(sourcePath)
|
|
66
|
+ reader := newSourceReader(sourcePath, o.TrimPath)
|
67
|
67
|
|
68
|
68
|
fmt.Fprintf(w, "Total: %s\n", rpt.formatValue(rpt.total))
|
69
|
69
|
for _, fn := range functions {
|
|
@@ -146,7 +146,7 @@ func PrintWebList(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFiles int) er
|
146
|
146
|
}
|
147
|
147
|
sourcePath = wd
|
148
|
148
|
}
|
149
|
|
- reader := newSourceReader(sourcePath)
|
|
149
|
+ reader := newSourceReader(sourcePath, o.TrimPath)
|
150
|
150
|
|
151
|
151
|
type fileFunction struct {
|
152
|
152
|
fileName, functionName string
|
|
@@ -263,7 +263,7 @@ func assemblyPerSourceLine(objSyms []*objSymbol, rs graph.Nodes, src string, obj
|
263
|
263
|
//
|
264
|
264
|
// E.g., suppose we are printing source code for F and this
|
265
|
265
|
// instruction is from H where F called G called H and both
|
266
|
|
- // of those calls were inlined. We want to use the line
|
|
266
|
+ // of those calls were inlined. We want to use the line
|
267
|
267
|
// number from F, not from H (which is what Disasm gives us).
|
268
|
268
|
//
|
269
|
269
|
// So find the outer-most linenumber in the source file.
|
|
@@ -391,8 +391,7 @@ func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyIns
|
391
|
391
|
continue
|
392
|
392
|
}
|
393
|
393
|
curCalls = nil
|
394
|
|
- fname := trimPath(c.file)
|
395
|
|
- fline, ok := reader.line(fname, c.line)
|
|
394
|
+ fline, ok := reader.line(c.file, c.line)
|
396
|
395
|
if !ok {
|
397
|
396
|
fline = ""
|
398
|
397
|
}
|
|
@@ -400,7 +399,7 @@ func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyIns
|
400
|
399
|
fmt.Fprintf(w, " %8s %10s %10s %8s <span class=inlinesrc>%s</span> <span class=unimportant>%s:%d</span>\n",
|
401
|
400
|
"", "", "", "",
|
402
|
401
|
template.HTMLEscapeString(fmt.Sprintf("%-80s", text)),
|
403
|
|
- template.HTMLEscapeString(filepath.Base(fname)), c.line)
|
|
402
|
+ template.HTMLEscapeString(filepath.Base(c.file)), c.line)
|
404
|
403
|
}
|
405
|
404
|
curCalls = an.inlineCalls
|
406
|
405
|
text := strings.Repeat(" ", srcIndent+4+4*len(curCalls)) + an.instruction
|
|
@@ -426,7 +425,6 @@ func printPageClosing(w io.Writer) {
|
426
|
425
|
// file and annotates it with the samples in fns. Returns the sources
|
427
|
426
|
// as nodes, using the info.name field to hold the source code.
|
428
|
427
|
func getSourceFromFile(file string, reader *sourceReader, fns graph.Nodes, start, end int) (graph.Nodes, string, error) {
|
429
|
|
- file = trimPath(file)
|
430
|
428
|
lineNodes := make(map[int]graph.Nodes)
|
431
|
429
|
|
432
|
430
|
// Collect source coordinates from profile.
|
|
@@ -516,20 +514,26 @@ func getMissingFunctionSource(filename string, asm map[int][]assemblyInstruction
|
516
|
514
|
|
517
|
515
|
// sourceReader provides access to source code with caching of file contents.
|
518
|
516
|
type sourceReader struct {
|
|
517
|
+ // searchPath is a filepath.ListSeparator-separated list of directories where
|
|
518
|
+ // source files should be searched.
|
519
|
519
|
searchPath string
|
520
|
520
|
|
|
521
|
+ // trimPath is a filepath.ListSeparator-separated list of paths to trim.
|
|
522
|
+ trimPath string
|
|
523
|
+
|
521
|
524
|
// files maps from path name to a list of lines.
|
522
|
525
|
// files[*][0] is unused since line numbering starts at 1.
|
523
|
526
|
files map[string][]string
|
524
|
527
|
|
525
|
|
- // errors collects errors encountered per file. These errors are
|
|
528
|
+ // errors collects errors encountered per file. These errors are
|
526
|
529
|
// consulted before returning out of these module.
|
527
|
530
|
errors map[string]error
|
528
|
531
|
}
|
529
|
532
|
|
530
|
|
-func newSourceReader(searchPath string) *sourceReader {
|
|
533
|
+func newSourceReader(searchPath, trimPath string) *sourceReader {
|
531
|
534
|
return &sourceReader{
|
532
|
535
|
searchPath,
|
|
536
|
+ trimPath,
|
533
|
537
|
make(map[string][]string),
|
534
|
538
|
make(map[string]error),
|
535
|
539
|
}
|
|
@@ -544,7 +548,7 @@ func (reader *sourceReader) line(path string, lineno int) (string, bool) {
|
544
|
548
|
if !ok {
|
545
|
549
|
// Read and cache file contents.
|
546
|
550
|
lines = []string{""} // Skip 0th line
|
547
|
|
- f, err := openSourceFile(path, reader.searchPath)
|
|
551
|
+ f, err := openSourceFile(path, reader.searchPath, reader.trimPath)
|
548
|
552
|
if err != nil {
|
549
|
553
|
reader.errors[path] = err
|
550
|
554
|
} else {
|
|
@@ -565,17 +569,20 @@ func (reader *sourceReader) line(path string, lineno int) (string, bool) {
|
565
|
569
|
return lines[lineno], true
|
566
|
570
|
}
|
567
|
571
|
|
568
|
|
-// openSourceFile opens a source file from a name encoded in a
|
569
|
|
-// profile. File names in a profile after often relative paths, so
|
570
|
|
-// search them in each of the paths in searchPath (or CWD by default),
|
571
|
|
-// and their parents.
|
572
|
|
-func openSourceFile(path, searchPath string) (*os.File, error) {
|
|
572
|
+// openSourceFile opens a source file from a name encoded in a profile. File
|
|
573
|
+// names in a profile after can be relative paths, so search them in each of
|
|
574
|
+// the paths in searchPath and their parents. In case the profile contains
|
|
575
|
+// absolute paths, additional paths may be configured to trim from the source
|
|
576
|
+// paths in the profile. This effectively turns the path into a relative path
|
|
577
|
+// searching it using searchPath as usual).
|
|
578
|
+func openSourceFile(path, searchPath, trim string) (*os.File, error) {
|
|
579
|
+ path = trimPath(path, trim, searchPath)
|
|
580
|
+ // If file is still absolute, require file to exist.
|
573
|
581
|
if filepath.IsAbs(path) {
|
574
|
582
|
f, err := os.Open(path)
|
575
|
583
|
return f, err
|
576
|
584
|
}
|
577
|
|
-
|
578
|
|
- // Scan each component of the path
|
|
585
|
+ // Scan each component of the path.
|
579
|
586
|
for _, dir := range filepath.SplitList(searchPath) {
|
580
|
587
|
// Search up for every parent of each possible path.
|
581
|
588
|
for {
|
|
@@ -595,18 +602,34 @@ func openSourceFile(path, searchPath string) (*os.File, error) {
|
595
|
602
|
}
|
596
|
603
|
|
597
|
604
|
// trimPath cleans up a path by removing prefixes that are commonly
|
598
|
|
-// found on profiles.
|
599
|
|
-func trimPath(path string) string {
|
600
|
|
- basePaths := []string{
|
601
|
|
- "/proc/self/cwd/./",
|
602
|
|
- "/proc/self/cwd/",
|
|
605
|
+// found on profiles plus configured prefixes.
|
|
606
|
+// TODO(aalexand): Consider optimizing out the redundant work done in this
|
|
607
|
+// function if it proves to matter.
|
|
608
|
+func trimPath(path, trimPath, searchPath string) string {
|
|
609
|
+ // Keep path variable intact as it's used below to form the return value.
|
|
610
|
+ sPath, searchPath := filepath.ToSlash(path), filepath.ToSlash(searchPath)
|
|
611
|
+ if trimPath == "" {
|
|
612
|
+ // If the trim path is not configured, try to guess it heuristically:
|
|
613
|
+ // search for basename of each search path in the original path and, if
|
|
614
|
+ // found, strip everything up to and including the basename. So, for
|
|
615
|
+ // example, given original path "/some/remote/path/my-project/foo/bar.c"
|
|
616
|
+ // and search path "/my/local/path/my-project" the heuristic will return
|
|
617
|
+ // "/my/local/path/my-project/foo/bar.c".
|
|
618
|
+ for _, dir := range filepath.SplitList(searchPath) {
|
|
619
|
+ want := "/" + filepath.Base(dir) + "/"
|
|
620
|
+ if found := strings.Index(sPath, want); found != -1 {
|
|
621
|
+ return path[found+len(want):]
|
|
622
|
+ }
|
|
623
|
+ }
|
603
|
624
|
}
|
604
|
|
-
|
605
|
|
- sPath := filepath.ToSlash(path)
|
606
|
|
-
|
607
|
|
- for _, base := range basePaths {
|
608
|
|
- if strings.HasPrefix(sPath, base) {
|
609
|
|
- return filepath.FromSlash(sPath[len(base):])
|
|
625
|
+ // Trim configured trim prefixes.
|
|
626
|
+ trimPaths := append(filepath.SplitList(filepath.ToSlash(trimPath)), "/proc/self/cwd/./", "/proc/self/cwd/")
|
|
627
|
+ for _, trimPath := range trimPaths {
|
|
628
|
+ if !strings.HasSuffix(trimPath, "/") {
|
|
629
|
+ trimPath += "/"
|
|
630
|
+ }
|
|
631
|
+ if strings.HasPrefix(sPath, trimPath) {
|
|
632
|
+ return path[len(trimPath):]
|
610
|
633
|
}
|
611
|
634
|
}
|
612
|
635
|
return path
|