github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/binary_info/find_function.go (about)

     1  // The MIT License (MIT)
     2  
     3  // Copyright (c) 2014 Derek Parker
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy of
     6  // this software and associated documentation files (the "Software"), to deal in
     7  // the Software without restriction, including without limitation the rights to
     8  // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     9  // the Software, and to permit persons to whom the Software is furnished to do so,
    10  // subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    17  // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    18  // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    19  // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    20  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  
    22  package binary_info
    23  
    24  import (
    25  	"debug/dwarf"
    26  	"sort"
    27  	"strings"
    28  
    29  	"github.com/Rookout/GoSDK/pkg/rookoutErrors"
    30  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/godwarf"
    31  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/line"
    32  )
    33  
    34  
    35  
    36  func (bi *BinaryInfo) FindFileLocation(filename string, lineno int) ([]uint64, string, *Function, rookoutErrors.RookoutError) {
    37  	possibleCUs, matchedFile, searchFileErr := bi.getBestMatchingFile(filename)
    38  	if searchFileErr != nil {
    39  		return nil, "", nil, searchFileErr
    40  	}
    41  	// got a file
    42  	filename = matchedFile
    43  	
    44  	
    45  	
    46  
    47  	
    48  	var pcs []line.PCStmt
    49  	for _, cu := range possibleCUs {
    50  		if cu.lineInfo == nil || cu.lineInfo.Lookup[filename] == nil {
    51  			continue
    52  		}
    53  
    54  		pcs = append(pcs, cu.lineInfo.LineToPCs(filename, lineno)...)
    55  	}
    56  
    57  	if len(pcs) == 0 {
    58  		
    59  		
    60  		
    61  		
    62  		for _, pc := range bi.inlinedCallLines[fileLine{filename, lineno}] {
    63  			pcs = append(pcs, line.PCStmt{PC: pc, Stmt: true})
    64  		}
    65  	}
    66  
    67  	if len(pcs) == 0 {
    68  		return nil, "", nil, rookoutErrors.NewLineNotFound(filename, lineno)
    69  	}
    70  
    71  	
    72  
    73  	pcByFunc := map[*Function][]line.PCStmt{}
    74  	sort.Slice(pcs, func(i, j int) bool { return pcs[i].PC < pcs[j].PC })
    75  	var fn *Function
    76  	for _, pcstmt := range pcs {
    77  		if fn == nil || (pcstmt.PC < fn.Entry) || (pcstmt.PC >= fn.End) {
    78  			fn = bi.PCToFunc(pcstmt.PC)
    79  		}
    80  		if fn != nil {
    81  			pcByFunc[fn] = append(pcByFunc[fn], pcstmt)
    82  		}
    83  	}
    84  
    85  	var selectedPCs []uint64
    86  
    87  	for fn, pcs := range pcByFunc {
    88  
    89  		
    90  
    91  		if strings.Contains(fn.Name, "·dwrap·") || fn.trampoline {
    92  			
    93  			continue
    94  		}
    95  
    96  		dwtree, err := fn.cu.image.GetDwarfTree(fn.Offset)
    97  		if err != nil {
    98  			return nil, "", nil, rookoutErrors.NewFailedToGetDWARFTree(err)
    99  		}
   100  		inlrngs := allInlineCallRanges(dwtree)
   101  
   102  		
   103  		
   104  		
   105  		
   106  		findInlRng := func(pc uint64) dwarf.Offset {
   107  			for _, inlrng := range inlrngs {
   108  				if inlrng.rng[0] <= pc && pc < inlrng.rng[1] {
   109  					return inlrng.off
   110  				}
   111  			}
   112  			return fn.Offset
   113  		}
   114  
   115  		pcsByOff := map[dwarf.Offset][]line.PCStmt{}
   116  
   117  		for _, pc := range pcs {
   118  			off := findInlRng(pc.PC)
   119  			pcsByOff[off] = append(pcsByOff[off], pc)
   120  		}
   121  
   122  		
   123  		
   124  		
   125  
   126  		for off, pcs := range pcsByOff {
   127  			sort.Slice(pcs, func(i, j int) bool { return pcs[i].PC < pcs[j].PC })
   128  
   129  			var selectedPC uint64
   130  			for _, pc := range pcs {
   131  				if pc.Stmt {
   132  					selectedPC = pc.PC
   133  					break
   134  				}
   135  			}
   136  
   137  			if selectedPC == 0 && len(pcs) > 0 {
   138  				selectedPC = pcs[0].PC
   139  			}
   140  
   141  			if selectedPC == 0 {
   142  				continue
   143  			}
   144  
   145  			
   146  
   147  			if off == fn.Offset && fn.Entry == selectedPC {
   148  				return bi.FindFileLocation(filename, lineno+1)
   149  			}
   150  
   151  			selectedPCs = append(selectedPCs, selectedPC)
   152  		}
   153  	}
   154  
   155  	sort.Slice(selectedPCs, func(i, j int) bool { return selectedPCs[i] < selectedPCs[j] })
   156  
   157  	return selectedPCs, filename, fn, nil
   158  }
   159  
   160  
   161  type inlRange struct {
   162  	off   dwarf.Offset
   163  	depth uint32
   164  	rng   [2]uint64
   165  }
   166  
   167  
   168  
   169  
   170  
   171  func allInlineCallRanges(tree *godwarf.Tree) []inlRange {
   172  	var r []inlRange
   173  
   174  	var visit func(*godwarf.Tree, uint32)
   175  	visit = func(n *godwarf.Tree, depth uint32) {
   176  		if n.Tag == dwarf.TagInlinedSubroutine {
   177  			for _, rng := range n.Ranges {
   178  				r = append(r, inlRange{off: n.Offset, depth: depth, rng: rng})
   179  			}
   180  		}
   181  		for _, child := range n.Children {
   182  			visit(child, depth+1)
   183  		}
   184  	}
   185  	visit(tree, 0)
   186  
   187  	sort.SliceStable(r, func(i, j int) bool { return r[i].depth > r[j].depth })
   188  	return r
   189  }