blob: abd6574dac2d943e134c848517cdb8fe7f4421a2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package symbolize
import (
"bufio"
"context"
"fmt"
"io"
"os/exec"
"strconv"
"strings"
)
// Symbolizer is an interface to an object that maps addresses in a bianry to source locations
type Symbolizer interface {
FindSrcLoc(file, build string, modRelAddr uint64) <-chan LLVMSymbolizeResult
}
type llvmSymboArgs struct {
file string
build string
modRelAddr uint64
output chan LLVMSymbolizeResult
}
type LLVMSymbolizeResult struct {
Locs []SourceLocation
Err error
}
type LLVMSymbolizer struct {
path string
stdin io.WriteCloser
stdout io.ReadCloser
symbolizer *exec.Cmd
input chan llvmSymboArgs
}
func NewLLVMSymbolizer(llvmSymboPath string) *LLVMSymbolizer {
var out LLVMSymbolizer
out.path = llvmSymboPath
out.symbolizer = exec.Command(llvmSymboPath)
out.input = make(chan llvmSymboArgs)
return &out
}
func unknownStr(str string) OptStr {
if str == "??" || str == "" {
return EmptyOptStr()
}
return NewOptStr(str)
}
func (s *LLVMSymbolizer) handle(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case args, ok := <-s.input:
if !ok {
return
}
if len(strings.TrimSpace(args.file)) == 0 {
args.output <- LLVMSymbolizeResult{nil, fmt.Errorf("Attempt to request code location of unnamed file")}
continue
}
// Before sending a binary off to llvm-symbolizer, veryify the binary
if err := VerifyBinary(args.file, args.build); err != nil {
args.output <- LLVMSymbolizeResult{nil, err}
continue
}
fmt.Fprintf(s.stdin, "%s 0x%x\n", args.file, args.modRelAddr)
out := []SourceLocation{}
scanner := bufio.NewScanner(s.stdout)
for scanner.Scan() {
function := scanner.Text()
if len(function) == 0 {
break
}
good := scanner.Scan()
if !good {
panic(fmt.Sprintf("%s output ended too soon", s.path))
}
location := scanner.Text()
parts := strings.SplitN(location, ":", 3)
if len(parts) < 2 {
panic(fmt.Sprintf("%s output unrecgonized format", s.path))
}
line, _ := strconv.Atoi(parts[1])
out = append(out, SourceLocation{unknownStr(parts[0]), line, unknownStr(function)})
}
args.output <- LLVMSymbolizeResult{out, nil}
}
}
}
func (s *LLVMSymbolizer) Start(ctx context.Context) error {
var err error
if s.stdin, err = s.symbolizer.StdinPipe(); err != nil {
return err
}
if s.stdout, err = s.symbolizer.StdoutPipe(); err != nil {
return err
}
if err = s.symbolizer.Start(); err != nil {
return err
}
go s.handle(ctx)
return nil
}
func (s *LLVMSymbolizer) FindSrcLoc(file, build string, modRelAddr uint64) <-chan LLVMSymbolizeResult {
// Buffer the return chanel so we don't block handle().
out := make(chan LLVMSymbolizeResult, 1)
args := llvmSymboArgs{file, build, modRelAddr, out}
s.input <- args
return out
}