blob: 842b3e9875855913a1a8fbdcc958955239a5f8f2 [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 (
"context"
"fmt"
"log"
)
type lineSource interface{}
type process uint64
type dummySource struct{}
type lineHeader interface{}
type logHeader struct {
time float64
process uint64
thread uint64
}
type LogLine struct {
lineno uint64
header lineHeader
source lineSource
}
type InputLine struct {
LogLine
msg string
}
// Later this will be more general.
type Segment struct {
mod uint64
vaddr uint64
size uint64
flags string
modRelAddr uint64
}
type AddressInfo struct {
locs []SourceLocation
mod Module
seg Segment
addr uint64
}
type Module struct {
name string
build string
id uint64
}
type OutputLine struct {
LogLine
line Node
}
// Filter represents the state needed to process a log.
type Filter struct {
// handles for llvm-symbolizer
symbolizer Symbolizer
// Symbolizer context
symContext MappingStore
modules map[uint64]Module
// Symbolizer repository
repo *SymbolizerRepo
}
// FindInfoForAddress takes a process an in memory address and converts it to a source location.
func (s *Filter) FindInfoForAddress(vaddr uint64) (AddressInfo, error) {
info := AddressInfo{addr: vaddr}
seg := s.symContext.Find(vaddr)
if seg == nil {
return info, fmt.Errorf("could not find segment that covers 0x%x", vaddr)
}
info.seg = *seg
if mod, ok := s.modules[info.seg.mod]; ok {
info.mod = mod
} else {
return info, fmt.Errorf("could not find module for 0x%x", vaddr)
}
modRelAddr := vaddr - seg.vaddr + seg.modRelAddr
mod, ok := s.modules[seg.mod]
if !ok {
return info, fmt.Errorf("could not find module with module id %d", seg.mod)
}
modPath, err := s.repo.GetBuildObject(mod.build)
if err != nil {
return info, fmt.Errorf("could not find module for build id %s: %v", s.modules[seg.mod].build, err)
}
result := <-s.symbolizer.FindSrcLoc(modPath, mod.build, modRelAddr)
if result.Err != nil {
return info, result.Err
}
info.locs = result.Locs
return info, nil
}
// NewFilter creates a new filter
func NewFilter(repo *SymbolizerRepo, symbo Symbolizer) *Filter {
return &Filter{
modules: make(map[uint64]Module),
repo: repo,
symbolizer: symbo,
}
}
// Reset resets the filter so that it can work for a new process
func (s *Filter) Reset() {
s.modules = make(map[uint64]Module)
s.symContext.Clear()
}
// AddModule updates the filter state to inform it of a new module
func (s *Filter) AddModule(m Module) {
s.modules[m.id] = m
}
// AddSegment updates the filter state to inform it of a new memory mapped location.
func (s *Filter) AddSegment(seg Segment) {
s.symContext.Add(seg)
}
// Start tells the filter to start consuming input and produce output.
func (f *Filter) Start(ctx context.Context, input <-chan InputLine) <-chan OutputLine {
out := make(chan OutputLine)
go func() {
for {
select {
case <-ctx.Done():
return
case elem, ok := <-input:
if !ok {
return
}
var res OutputLine
if res.line = ParseLine(elem.msg); res.line == nil {
res.line = &Text{text: elem.msg}
log.Printf("warning malformed input %s on line %d", elem.msg, elem.lineno)
}
// Update AST with source locations.
res.line.Accept(&FilterVisitor{filter: f, lineno: elem.lineno})
res.LogLine = elem.LogLine
out <- res
}
}
}()
return out
}
type FilterVisitor struct {
filter *Filter
lineno uint64
}
func (f *FilterVisitor) VisitBt(elem *BacktraceElement) {
info, err := f.filter.FindInfoForAddress(elem.vaddr)
if err != nil {
log.Printf("warning on line %d: %v", f.lineno, err)
}
elem.info = info
}
func (f *FilterVisitor) VisitPc(elem *PCElement) {
info, err := f.filter.FindInfoForAddress(elem.vaddr)
if err != nil {
log.Printf("warning on line %d: %v", f.lineno, err)
}
elem.info = info
}
func (f *FilterVisitor) VisitColor(group *ColorGroup) {
for _, child := range group.children {
child.Accept(f)
}
}
func (f *FilterVisitor) VisitText(_ *Text) {
// This must be implemented in order to meet the interface but it has no effect.
// This visitor is supposed to do all of the non-parsing parts of constructing the AST.
// There is nothing to do for Text however.
}
func (f *FilterVisitor) VisitReset(elem *ResetElement) {
// TODO: Check if Reset had an effect and output that a pid reuse occured.
f.filter.Reset()
}
func (f *FilterVisitor) VisitGroup(group *PresentationGroup) {
for _, child := range group.children {
child.Accept(f)
}
}
func (f *FilterVisitor) VisitModule(elem *ModuleElement) {
f.filter.AddModule(elem.mod)
}
func (f *FilterVisitor) VisitMapping(elem *MappingElement) {
f.filter.AddSegment(elem.seg)
}