pkg/tools/profiling/api.go (171 lines of code) (raw):

// Licensed to Apache Software Foundation (ASF) under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Apache Software Foundation (ASF) licenses this file to you 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 profiling import ( "fmt" "regexp" "github.com/apache/skywalking-rover/pkg/logger" "github.com/ianlancetaylor/demangle" ) type ModuleType int8 var ( KernelProcSymbolFilePath = "kallsyms" // after host.GetHostProcInHost, should be "/proc/kallsyms" log = logger.GetLogger("tools", "profiling") ) const ( ModuleTypeExec ModuleType = iota ModuleTypeSo ModuleTypePerfMap ModuleTypeVDSO ModuleTypeUnknown ) // Info of profiling process type Info struct { Modules []*Module cacheAddrToSymbol map[uint64]string } type Module struct { Ranges []*ModuleRange Name string Path string Type ModuleType SoOffset, SoAddr uint64 Symbols []*Symbol } type ModuleRange struct { StartAddr, EndAddr, FileOffset uint64 } // Symbol of executable file type Symbol struct { Name string Location uint64 Size uint64 } type StatFinder interface { // IsSupport to stat the executable file for profiling IsSupport(filePath string) bool // AnalyzeSymbols in the file AnalyzeSymbols(filePath string) ([]*Symbol, error) // ToModule to init a new module ToModule(pid int32, modName, modPath string, moduleRange []*ModuleRange) (*Module, error) } func NewInfo(modules map[string]*Module) *Info { ls := make([]*Module, 0) for _, m := range modules { ls = append(ls, m) } return &Info{Modules: ls, cacheAddrToSymbol: make(map[uint64]string)} } // FindSymbols from address list, if could not found symbol name then append default symbol to array func (i *Info) FindSymbols(addresses []uint64, defaultSymbol string) []string { if len(addresses) == 0 { return nil } result := make([]string, 0) for _, addr := range addresses { if addr <= 0 { continue } s := i.FindSymbolName(addr) if s == "" { s = defaultSymbol } result = append(result, s) } return result } // FindSymbolName by address func (i *Info) FindSymbolName(address uint64) string { if d := i.cacheAddrToSymbol[address]; d != "" { return d } log.Debugf("ready to find the symbol from address: %d", address) foundModule := false for _, mod := range i.Modules { offset, c := mod.contains(address) if !c { continue } foundModule = true if sym := mod.findAddr(offset); sym != nil { name := processSymbolName(sym.Name) i.cacheAddrToSymbol[address] = name return name } } if !foundModule { log.Debugf("could not found any module to handle address: %d", address) } return "" } func (i *Info) FindSymbolAddress(name string) uint64 { for _, m := range i.Modules { for _, sym := range m.Symbols { if sym.Name == name { return sym.Location } } } return 0 } func (i *Info) FindSymbolByRegex(rep string) (string, error) { vals, err := i.FindMultipleSymbolByRegex(rep) if err != nil { return "", err } return vals[0], nil } func (i *Info) FindMultipleSymbolByRegex(rep string) ([]string, error) { compile, err := regexp.Compile(rep) if err != nil { return nil, err } result := make([]string, 0) for _, m := range i.Modules { for _, sym := range m.Symbols { if compile.MatchString(sym.Name) { result = append(result, sym.Name) } } } if len(result) == 0 { return nil, fmt.Errorf("cannot found any matches symbol: %s", rep) } return result, nil } func (m *Module) contains(addr uint64) (uint64, bool) { for _, r := range m.Ranges { if addr >= r.StartAddr && addr < r.EndAddr { log.Debugf("found module %s could hanlde address: %d", m.Name, addr) if m.Type == ModuleTypeSo || m.Type == ModuleTypeVDSO { offset := addr - r.StartAddr + r.FileOffset offset += m.SoAddr - m.SoOffset log.Debugf("update address %d to offset %d", addr, offset) return offset, true } return addr, true } } return 0, false } func (m *Module) findAddr(offset uint64) *Symbol { start := 0 end := len(m.Symbols) - 1 for start < end { mid := start + (end-start)/2 result := int64(offset) - int64(m.Symbols[mid].Location) if result < 0 { end = mid } else if result > 0 { start = mid + 1 } else { return m.Symbols[mid] } } if start >= 1 && m.Symbols[start-1].Location < offset && offset < m.Symbols[start].Location { return m.Symbols[start-1] } log.Debugf("could not found the address: %d in module %s", offset, m.Name) return nil } func processSymbolName(name string) string { if name == "" { return "" } // fix process demangle symbol name, such as c++ language symbol skip := 0 if name[0] == '.' || name[0] == '$' { skip++ } return demangle.Filter(name[skip:]) }