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  }