diff options
| author | Taras Madan <tarasmadan@google.com> | 2025-02-28 13:57:50 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2025-03-07 10:23:01 +0000 |
| commit | 7e3bd60dd6c8f783f5a418c64aa75f6818236dc4 (patch) | |
| tree | 24dce56dc037521e340e73f45ba41c8b6c264af2 /pkg/symbolizer/addr2line.go | |
| parent | 831e3629115be4f5e68e6e0df3a5b07eed26daa4 (diff) | |
pkg/symbolizer: introduce Symbolizer interface
To simplify interface Read*Symbols were moved out from symbolizer.Symbolizer.
Diffstat (limited to 'pkg/symbolizer/addr2line.go')
| -rw-r--r-- | pkg/symbolizer/addr2line.go | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/pkg/symbolizer/addr2line.go b/pkg/symbolizer/addr2line.go new file mode 100644 index 000000000..5187f72ed --- /dev/null +++ b/pkg/symbolizer/addr2line.go @@ -0,0 +1,184 @@ +// Copyright 2016 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +// TODO: strip " (discriminator N)", "constprop", "isra" from function names. + +package symbolizer + +import ( + "bufio" + "fmt" + "io" + "os/exec" + "strconv" + "strings" + + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/sys/targets" +) + +type addr2Line struct { + target *targets.Target + subprocs map[string]*subprocess + interner Interner +} + +type subprocess struct { + cmd *exec.Cmd + stdin io.Closer + stdout io.Closer + input *bufio.Writer + scanner *bufio.Scanner +} + +func (s *addr2Line) Symbolize(bin string, pcs ...uint64) ([]Frame, error) { + sub, err := s.getSubprocess(bin) + if err != nil { + return nil, err + } + return symbolize(&s.interner, sub.input, sub.scanner, pcs) +} + +func (s *addr2Line) Close() { + for _, sub := range s.subprocs { + sub.stdin.Close() + sub.stdout.Close() + sub.cmd.Process.Kill() + sub.cmd.Wait() + } +} + +func (s *addr2Line) getSubprocess(bin string) (*subprocess, error) { + if sub := s.subprocs[bin]; sub != nil { + return sub, nil + } + addr2line, err := s.target.Addr2Line() + if err != nil { + return nil, err + } + cmd := osutil.Command(addr2line, "-afi", "-e", bin) + stdin, err := cmd.StdinPipe() + if err != nil { + return nil, err + } + stdout, err := cmd.StdoutPipe() + if err != nil { + stdin.Close() + return nil, err + } + if err := cmd.Start(); err != nil { + stdin.Close() + stdout.Close() + return nil, err + } + sub := &subprocess{ + cmd: cmd, + stdin: stdin, + stdout: stdout, + input: bufio.NewWriter(stdin), + scanner: bufio.NewScanner(stdout), + } + if s.subprocs == nil { + s.subprocs = make(map[string]*subprocess) + } + s.subprocs[bin] = sub + return sub, nil +} + +func symbolize(interner *Interner, input *bufio.Writer, scanner *bufio.Scanner, pcs []uint64) ([]Frame, error) { + var frames []Frame + done := make(chan error, 1) + go func() { + var err error + defer func() { + done <- err + }() + if !scanner.Scan() { + if err = scanner.Err(); err == nil { + err = io.EOF + } + return + } + for range pcs { + var frames1 []Frame + frames1, err = parse(interner, scanner) + if err != nil { + return + } + frames = append(frames, frames1...) + } + for i := 0; i < 2; i++ { + scanner.Scan() + } + }() + + for _, pc := range pcs { + if _, err := fmt.Fprintf(input, "0x%x\n", pc); err != nil { + return nil, err + } + } + // Write an invalid PC so that parse doesn't block reading input. + // We read out result for this PC at the end of the function. + if _, err := fmt.Fprintf(input, "0xffffffffffffffff\n"); err != nil { + return nil, err + } + input.Flush() + + if err := <-done; err != nil { + return nil, err + } + return frames, nil +} + +func parse(interner *Interner, s *bufio.Scanner) ([]Frame, error) { + pc, err := strconv.ParseUint(s.Text(), 0, 64) + if err != nil { + return nil, fmt.Errorf("failed to parse pc '%v' in addr2line output: %w", s.Text(), err) + } + var frames []Frame + for { + if !s.Scan() { + break + } + ln := s.Text() + if len(ln) > 3 && ln[0] == '0' && ln[1] == 'x' { + break + } + fn := ln + if !s.Scan() { + err := s.Err() + if err == nil { + err = io.EOF + } + return nil, fmt.Errorf("failed to read file:line from addr2line: %w", err) + } + ln = s.Text() + colon := strings.LastIndexByte(ln, ':') + if colon == -1 { + return nil, fmt.Errorf("failed to parse file:line in addr2line output: %v", ln) + } + lineEnd := colon + 1 + for lineEnd < len(ln) && ln[lineEnd] >= '0' && ln[lineEnd] <= '9' { + lineEnd++ + } + file := ln[:colon] + line, err := strconv.Atoi(ln[colon+1 : lineEnd]) + if err != nil || fn == "" || fn == "??" || file == "" || file == "??" || line <= 0 { + continue + } + frames = append(frames, Frame{ + PC: pc, + Func: interner.Do(fn), + File: interner.Do(file), + Line: line, + Inline: true, + }) + } + if err := s.Err(); err != nil { + return nil, err + } + if len(frames) != 0 { + frames[len(frames)-1].Inline = false + } + return frames, nil +} |
