golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/binutils/addr2liner_llvm.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package binutils 16 17 import ( 18 "bufio" 19 "fmt" 20 "io" 21 "os/exec" 22 "strconv" 23 "strings" 24 25 "github.com/google/pprof/internal/plugin" 26 ) 27 28 const ( 29 defaultLLVMSymbolizer = "llvm-symbolizer" 30 ) 31 32 // llvmSymbolizer is a connection to an llvm-symbolizer command for 33 // obtaining address and line number information from a binary. 34 type llvmSymbolizer struct { 35 filename string 36 rw lineReaderWriter 37 base uint64 38 } 39 40 type llvmSymbolizerJob struct { 41 cmd *exec.Cmd 42 in io.WriteCloser 43 out *bufio.Reader 44 } 45 46 func (a *llvmSymbolizerJob) write(s string) error { 47 _, err := fmt.Fprint(a.in, s+"\n") 48 return err 49 } 50 51 func (a *llvmSymbolizerJob) readLine() (string, error) { 52 return a.out.ReadString('\n') 53 } 54 55 // close releases any resources used by the llvmSymbolizer object. 56 func (a *llvmSymbolizerJob) close() { 57 a.in.Close() 58 a.cmd.Wait() 59 } 60 61 // newLlvmSymbolizer starts the given llvmSymbolizer command reporting 62 // information about the given executable file. If file is a shared 63 // library, base should be the address at which it was mapped in the 64 // program under consideration. 65 func newLLVMSymbolizer(cmd, file string, base uint64) (*llvmSymbolizer, error) { 66 if cmd == "" { 67 cmd = defaultLLVMSymbolizer 68 } 69 70 j := &llvmSymbolizerJob{ 71 cmd: exec.Command(cmd, "-inlining", "-demangle=false"), 72 } 73 74 var err error 75 if j.in, err = j.cmd.StdinPipe(); err != nil { 76 return nil, err 77 } 78 79 outPipe, err := j.cmd.StdoutPipe() 80 if err != nil { 81 return nil, err 82 } 83 84 j.out = bufio.NewReader(outPipe) 85 if err := j.cmd.Start(); err != nil { 86 return nil, err 87 } 88 89 a := &llvmSymbolizer{ 90 filename: file, 91 rw: j, 92 base: base, 93 } 94 95 return a, nil 96 } 97 98 func (d *llvmSymbolizer) readString() (string, error) { 99 s, err := d.rw.readLine() 100 if err != nil { 101 return "", err 102 } 103 return strings.TrimSpace(s), nil 104 } 105 106 // readFrame parses the llvm-symbolizer output for a single address. It 107 // returns a populated plugin.Frame and whether it has reached the end of the 108 // data. 109 func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) { 110 funcname, err := d.readString() 111 if err != nil { 112 return plugin.Frame{}, true 113 } 114 115 switch funcname { 116 case "": 117 return plugin.Frame{}, true 118 case "??": 119 funcname = "" 120 } 121 122 fileline, err := d.readString() 123 if err != nil { 124 return plugin.Frame{Func: funcname}, true 125 } 126 127 linenumber := 0 128 if fileline == "??:0" { 129 fileline = "" 130 } else { 131 switch split := strings.Split(fileline, ":"); len(split) { 132 case 1: 133 // filename 134 fileline = split[0] 135 case 2, 3: 136 // filename:line , or 137 // filename:line:disc , or 138 fileline = split[0] 139 if line, err := strconv.Atoi(split[1]); err == nil { 140 linenumber = line 141 } 142 default: 143 // Unrecognized, ignore 144 } 145 } 146 147 return plugin.Frame{Func: funcname, File: fileline, Line: linenumber}, false 148 } 149 150 // addrInfo returns the stack frame information for a specific program 151 // address. It returns nil if the address could not be identified. 152 func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) { 153 if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil { 154 return nil, err 155 } 156 157 var stack []plugin.Frame 158 for { 159 frame, end := d.readFrame() 160 if end { 161 break 162 } 163 164 if frame != (plugin.Frame{}) { 165 stack = append(stack, frame) 166 } 167 } 168 169 return stack, nil 170 }