123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- // Copyright 2014 Google Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package binutils
-
- import (
- "bufio"
- "fmt"
- "io"
- "os/exec"
- "strconv"
- "strings"
-
- "github.com/google/pprof/internal/plugin"
- )
-
- const (
- defaultLLVMSymbolizer = "llvm-symbolizer"
- )
-
- // llvmSymbolizer is a connection to an llvm-symbolizer command for
- // obtaining address and line number information from a binary.
- type llvmSymbolizer struct {
- filename string
- rw lineReaderWriter
- base uint64
- }
-
- type llvmSymbolizerJob struct {
- cmd *exec.Cmd
- in io.WriteCloser
- out *bufio.Reader
- }
-
- func (a *llvmSymbolizerJob) write(s string) error {
- _, err := fmt.Fprint(a.in, s+"\n")
- return err
- }
-
- func (a *llvmSymbolizerJob) readLine() (string, error) {
- return a.out.ReadString('\n')
- }
-
- // close releases any resources used by the llvmSymbolizer object.
- func (a *llvmSymbolizerJob) close() {
- a.in.Close()
- a.cmd.Wait()
- }
-
- // newLlvmSymbolizer starts the given llvmSymbolizer command reporting
- // information about the given executable file. If file is a shared
- // library, base should be the address at which it was mapped in the
- // program under consideration.
- func newLLVMSymbolizer(cmd, file string, base uint64) (*llvmSymbolizer, error) {
- if cmd == "" {
- cmd = defaultLLVMSymbolizer
- }
-
- j := &llvmSymbolizerJob{
- cmd: exec.Command(cmd, "-inlining", "-demangle=false"),
- }
-
- var err error
- if j.in, err = j.cmd.StdinPipe(); err != nil {
- return nil, err
- }
-
- outPipe, err := j.cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
-
- j.out = bufio.NewReader(outPipe)
- if err := j.cmd.Start(); err != nil {
- return nil, err
- }
-
- a := &llvmSymbolizer{
- filename: file,
- rw: j,
- base: base,
- }
-
- return a, nil
- }
-
- func (d *llvmSymbolizer) readString() (string, error) {
- s, err := d.rw.readLine()
- if err != nil {
- return "", err
- }
- return strings.TrimSpace(s), nil
- }
-
- // readFrame parses the llvm-symbolizer output for a single address. It
- // returns a populated plugin.Frame and whether it has reached the end of the
- // data.
- func (d *llvmSymbolizer) readFrame() (plugin.Frame, bool) {
- funcname, err := d.readString()
- if err != nil {
- return plugin.Frame{}, true
- }
-
- switch funcname {
- case "":
- return plugin.Frame{}, true
- case "??":
- funcname = ""
- }
-
- fileline, err := d.readString()
- if err != nil {
- return plugin.Frame{funcname, "", 0}, true
- }
-
- linenumber := 0
- if fileline == "??:0" {
- fileline = ""
- } else {
- switch split := strings.Split(fileline, ":"); len(split) {
- case 1:
- // filename
- fileline = split[0]
- case 2, 3:
- // filename:line , or
- // filename:line:disc , or
- fileline = split[0]
- if line, err := strconv.Atoi(split[1]); err == nil {
- linenumber = line
- }
- default:
- // Unrecognized, ignore
- }
- }
-
- return plugin.Frame{funcname, fileline, linenumber}, false
- }
-
- // addrInfo returns the stack frame information for a specific program
- // address. It returns nil if the address could not be identified.
- func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
- if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
- return nil, err
- }
-
- var stack []plugin.Frame
- for {
- frame, end := d.readFrame()
- if end {
- break
- }
-
- if frame != (plugin.Frame{}) {
- stack = append(stack, frame)
- }
- }
-
- return stack, nil
- }
|