pkg/tools/process/process.go (148 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 process import ( "bufio" "fmt" "os" "regexp" "strconv" "strings" "github.com/apache/skywalking-rover/pkg/logger" host2 "github.com/apache/skywalking-rover/pkg/tools/host" "github.com/apache/skywalking-rover/pkg/tools/path" "github.com/apache/skywalking-rover/pkg/tools/profiling" ) var ( // NotSupportProfilingExe mean which program are not support for profiling // Not Support JIT and Script language for now NotSupportProfilingExe = []string{ "java", "python", "node", "bash", "ruby", "ssh", } // executable file profiling finders profilingStatFinderList = []profiling.StatFinder{ profiling.NewGoLibrary(), } // kernel profiling finder kernelFinder = profiling.NewKernelFinder() // process map file analyze(/proc/{pid}/maps) mapFileContentRegex = regexp.MustCompile("(?P<StartAddr>[a-f\\d]+)\\-(?P<EndAddr>[a-f\\d]+)\\s(?P<Perm>[^\\s]+)" + "\\s(?P<Offset>[a-f\\d]+)\\s[a-f\\d]+\\:[a-f\\d]+\\s\\d+\\s+(?P<Name>[^\\n]+)") log = logger.GetLogger("tools", "process") ) // KernelFileProfilingStat is works for read the kernel and get is support for kernel symbol analyze func KernelFileProfilingStat() (*profiling.Info, error) { if !kernelFinder.IsSupport(profiling.KernelProcSymbolFilePath) { return nil, fmt.Errorf("not support kernel space profiling") } return kernelFinder.Analyze(profiling.KernelProcSymbolFilePath) } // ProfilingStat is validating the exe file could be profiling and get info func ProfilingStat(pid int32, exePath string) (*profiling.Info, error) { stat, err := os.Stat(exePath) if err != nil { return nil, fmt.Errorf("check file error: %v", err) } for _, notSupport := range NotSupportProfilingExe { if strings.HasPrefix(stat.Name(), notSupport) { return nil, fmt.Errorf("not support %s language profiling", notSupport) } } context := newAnalyzeContext() // the executable file must have the symbols symbols, err := context.GetFinder(exePath).AnalyzeSymbols(exePath) if err != nil || len(symbols) == 0 { return nil, fmt.Errorf("could not found any symbol in the execute file: %s, error: %v", exePath, err) } return analyzeProfilingInfo(context, pid) } // Modules Read the profiling info of the process, without the symbol check func Modules(pid int32) ([]*profiling.Module, error) { context := newAnalyzeContext() info, err := analyzeProfilingInfo(context, pid) if err != nil { return nil, err } return info.Modules, nil } func analyzeProfilingInfo(context *analyzeContext, pid int32) (*profiling.Info, error) { // analyze process mapping mapFile, _ := os.Open(host2.GetHostProcInHost(fmt.Sprintf("%d/maps", pid))) scanner := bufio.NewScanner(mapFile) modules := make(map[string]*profiling.Module) for scanner.Scan() { submatch := mapFileContentRegex.FindStringSubmatch(scanner.Text()) if len(submatch) != 6 { continue } if len(submatch[3]) > 2 && submatch[3][2] != 'x' { continue } moduleName := submatch[5] if isIgnoreModuleName(moduleName) { continue } // parsing range var err error moduleRange := &profiling.ModuleRange{} moduleRange.StartAddr, err = parseUInt64InModule(err, moduleName, "start address", submatch[1]) moduleRange.EndAddr, err = parseUInt64InModule(err, moduleName, "end address", submatch[2]) moduleRange.FileOffset, err = parseUInt64InModule(err, moduleName, "file offset", submatch[4]) if err != nil { return nil, err } module := modules[moduleName] if module != nil { module.Ranges = append(module.Ranges, moduleRange) continue } modulePath := host2.GetHostProcInHost(fmt.Sprintf("%d/root%s", pid, moduleName)) if !path.Exists(modulePath) { log.Debugf("could not found the module, ignore. name: %s, path: %s", moduleName, modulePath) continue } module, err = context.GetFinder(modulePath).ToModule(pid, moduleName, modulePath, []*profiling.ModuleRange{moduleRange}) if err != nil { if err == profiling.ErrNotSupport { log.Warnf("not support the module in process(%d): %s, path: %s", pid, moduleName, modulePath) continue } return nil, fmt.Errorf("could not init the module: %s, error: %v", moduleName, err) } modules[moduleName] = module } return profiling.NewInfo(modules), nil } func parseUInt64InModule(err error, moduleName, key, val string) (uint64, error) { if err != nil { return 0, err } res, err := strconv.ParseUint(val, 16, 64) if err != nil { return 0, fmt.Errorf("error parsing the %s in maps file: %s", key, moduleName) } return res, nil } func isIgnoreModuleName(name string) bool { return name != "" && (strings.HasPrefix(name, "//anon") || strings.HasPrefix(name, "/dev/zero") || strings.HasPrefix(name, "/anon_hugepage") || strings.HasPrefix(name, "[stack") || strings.HasPrefix(name, "/SYSV") || strings.HasPrefix(name, "[heap]") || strings.HasPrefix(name, "/memfd:") || strings.HasPrefix(name, "[vdso]") || strings.HasPrefix(name, "[vsyscall]") || strings.HasPrefix(name, "[uprobes]") || strings.HasSuffix(name, ".map")) } type analyzeContext struct { pathToFinder map[string]profiling.StatFinder } func newAnalyzeContext() *analyzeContext { return &analyzeContext{ pathToFinder: make(map[string]profiling.StatFinder), } } func (a *analyzeContext) GetFinder(name string) profiling.StatFinder { if f := a.pathToFinder[name]; f != nil { return f } // find all finders for _, f := range profilingStatFinderList { if f.IsSupport(name) { a.pathToFinder[name] = f return f } } // not support n := profiling.NewNotSupport() a.pathToFinder[name] = n return n }