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 }