| // 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) |
| } |