This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-rover.git
The following commit(s) were added to refs/heads/main by this push:
new cd265e6 Support analyze the ".so" file symbols (#20)
cd265e6 is described below
commit cd265e6cc2065935db861229db4d396b91141db5
Author: mrproliu <[email protected]>
AuthorDate: Wed May 4 17:27:48 2022 +0800
Support analyze the ".so" file symbols (#20)
---
.asf.yaml | 2 +-
.github/workflows/rover.yaml | 2 +
README.md | 2 +-
docs/en/concepts-and-designs/overview.md | 4 +-
pkg/process/finders/base/tool.go | 35 ++----
.../{profiling/go_library.go => path/file.go} | 44 +------
pkg/tools/process.go | 131 +++++++++++++++++----
pkg/tools/profiling/api.go | 96 ++++++++++++---
pkg/tools/profiling/go_library.go | 68 ++++++++++-
pkg/tools/profiling/kernel.go | 9 +-
.../profiling/{go_library.go => not_support.go} | 36 ++----
pkg/tools/profiling/objdump.go | 71 -----------
test/e2e/cases/profiling/rust/Dockerfile.sqrt | 23 ++++
.../e2e/cases/profiling/rust/docker-compose.yml | 52 ++++----
test/e2e/cases/profiling/rust/e2e.yaml | 45 +++++++
.../cases/profiling/rust/profiling-analysis.yml | 39 ++----
test/e2e/cases/profiling/rust/sqrt.rs | 28 +++++
17 files changed, 424 insertions(+), 263 deletions(-)
diff --git a/.asf.yaml b/.asf.yaml
index 630b405..a9aa938 100644
--- a/.asf.yaml
+++ b/.asf.yaml
@@ -16,7 +16,7 @@
#
github:
- description: Metrics collector and ebpf-based profiler for C, C++, and Golang
+ description: Metrics collector and ebpf-based profiler for C, C++, Golang,
and Rust
homepage: https://skywalking.apache.org/
labels:
- skywalking
diff --git a/.github/workflows/rover.yaml b/.github/workflows/rover.yaml
index fe767ff..11c2784 100644
--- a/.github/workflows/rover.yaml
+++ b/.github/workflows/rover.yaml
@@ -79,6 +79,8 @@ jobs:
config: test/e2e/cases/profiling/c++/e2e.yaml
- name: C Profiling
config: test/e2e/cases/profiling/c/e2e.yaml
+ - name: Rust Profiling
+ config: test/e2e/cases/profiling/rust/e2e.yaml
- name: go2sky Agent Sensor
config: test/e2e/cases/process/agent_sensor/golang/e2e.yaml
diff --git a/README.md b/README.md
index 351b7bf..790661e 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ Apache SkyWalking Rover
<img src="http://skywalking.apache.org/assets/logo.svg" alt="Sky Walking logo"
height="90px" align="right" />
-**SkyWalking Rover**: Metrics collector and ebpf-based profiler for C, C++,
and Golang.
+**SkyWalking Rover**: Metrics collector and ebpf-based profiler for C, C++,
Golang and Rust.
[](https://github.com/apache/skywalking)
[](https://twitter.com/AsfSkyWalking)
diff --git a/docs/en/concepts-and-designs/overview.md
b/docs/en/concepts-and-designs/overview.md
index b14148c..a072002 100644
--- a/docs/en/concepts-and-designs/overview.md
+++ b/docs/en/concepts-and-designs/overview.md
@@ -1,13 +1,13 @@
# Overview
-SkyWalking Rover is an open-source collector, which provides a metrics
collector and eBPF-based profiler for C, C++, and Golang.
+SkyWalking Rover is an open-source collector, which provides a metrics
collector and eBPF-based profiler for C, C++, Golang and Rust.
## Why use SkyWalking Rover?
On the Linux platform, we could collect a lot of telemetry data. Rover could
collect them based on the eBPF technology,
and upload them to the SkyWalking backend for analysis, aggregate, and
visualize them.
-1. EBPF based profiling for C, C++, and Golang.
+1. EBPF based profiling for C, C++, Golang and Rust.
## Architecture
diff --git a/pkg/process/finders/base/tool.go b/pkg/process/finders/base/tool.go
index 2eba046..5d79e42 100644
--- a/pkg/process/finders/base/tool.go
+++ b/pkg/process/finders/base/tool.go
@@ -19,10 +19,10 @@ package base
import (
"fmt"
- "os"
"github.com/apache/skywalking-rover/pkg/tools"
"github.com/apache/skywalking-rover/pkg/tools/host"
+ "github.com/apache/skywalking-rover/pkg/tools/path"
"github.com/apache/skywalking-rover/pkg/tools/profiling"
"github.com/shirou/gopsutil/process"
@@ -30,39 +30,26 @@ import (
// BuildProfilingStat use to build the profiling info for profiling
func BuildProfilingStat(ps *process.Process) (*profiling.Info, error) {
- path := tryToFindFileExecutePath(ps)
- if path == "" {
+ exePath := tryToFindFileExecutePath(ps)
+ if exePath == "" {
return nil, fmt.Errorf("could not found executable file")
}
// check support profiling
- return tools.ExecutableFileProfilingStat(path)
+ return tools.ProcessProfilingStat(ps.Pid, exePath)
}
func tryToFindFileExecutePath(ps *process.Process) string {
exe, err := ps.Exe()
- if pathExists(exe, err) {
- return exe
+ if err != nil {
+ return ""
}
- cwd, err := ps.Cwd()
- if pathExists(cwd, err) && pathExists(cwd+"/"+exe, err) {
- return cwd + "/" + exe
+ if path.Exists(exe) {
+ return exe
}
- linuxProcessRoot := host.GetFileInHost(fmt.Sprintf("/proc/%d/root",
ps.Pid))
- if pathExists(linuxProcessRoot, nil) {
- if pathExists(linuxProcessRoot+"/"+exe, nil) {
- return linuxProcessRoot + "/" + exe
- } else if pathExists(linuxProcessRoot+"/"+cwd+"/"+exe, nil) {
- return linuxProcessRoot + "/" + cwd + "/" + exe
- }
+ pathInNs := host.GetFileInHost(fmt.Sprintf("/proc/%d/root%s", ps.Pid,
exe))
+ if path.Exists(pathInNs) {
+ return pathInNs
}
return ""
}
-
-func pathExists(exe string, err error) bool {
- if err != nil {
- return false
- }
- _, e := os.Stat(exe)
- return !os.IsNotExist(e)
-}
diff --git a/pkg/tools/profiling/go_library.go b/pkg/tools/path/file.go
similarity index 50%
copy from pkg/tools/profiling/go_library.go
copy to pkg/tools/path/file.go
index 17dba26..84e4017 100644
--- a/pkg/tools/profiling/go_library.go
+++ b/pkg/tools/path/file.go
@@ -15,44 +15,12 @@
// specific language governing permissions and limitations
// under the License.
-package profiling
+package path
-import (
- "debug/elf"
- "fmt"
-)
+import "os"
-// GoLibrary is using build-in elf reader to read
-type GoLibrary struct {
-}
-
-func NewGoLibrary() *GoLibrary {
- return &GoLibrary{}
-}
-
-func (l *GoLibrary) IsSupport(filePath string) bool {
- return true
-}
-
-func (l *GoLibrary) Analyze(filePath string) (*Info, error) {
- // read els file
- file, err := elf.Open(filePath)
- if err != nil {
- return nil, fmt.Errorf("read ELF file error: %v", err)
- }
- defer file.Close()
-
- // exist symbol data
- symbols, err := file.Symbols()
- if err != nil || len(symbols) == 0 {
- return nil, fmt.Errorf("read symbol data failure or no symbole
data: %v", err)
- }
-
- // adapt symbol struct
- data := make([]*Symbol, len(symbols))
- for i, sym := range symbols {
- data[i] = &Symbol{Name: sym.Name, Location: sym.Value}
- }
-
- return newInfo(data), nil
+// Exists to check file exists ignore error
+func Exists(f string) bool {
+ _, e := os.Stat(f)
+ return !os.IsNotExist(e)
}
diff --git a/pkg/tools/process.go b/pkg/tools/process.go
index 46910ff..8aefb20 100644
--- a/pkg/tools/process.go
+++ b/pkg/tools/process.go
@@ -18,11 +18,14 @@
package tools
import (
+ "bufio"
"fmt"
"os"
- "sort"
+ "regexp"
+ "strconv"
"strings"
+ host2 "github.com/apache/skywalking-rover/pkg/tools/host"
"github.com/apache/skywalking-rover/pkg/tools/profiling"
)
@@ -35,11 +38,15 @@ var (
// executable file profiling finders
profilingStatFinderList = []profiling.StatFinder{
- profiling.NewGoLibrary(), profiling.NewObjDump(),
+ 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>\\d+)\\s[a-f\\d]+\\:[a-f\\d]+\\s\\d+\\s+(?P<Name>[^\\n]+)")
)
// KernelFileProfilingStat is works for read the kernel and get is support for
kernel symbol analyze
@@ -50,8 +57,8 @@ func KernelFileProfilingStat() (*profiling.Info, error) {
return kernelFinder.Analyze(profiling.KernelSymbolFilePath)
}
-// ExecutableFileProfilingStat is validating the exe file could be profiling
and get info
-func ExecutableFileProfilingStat(exePath string) (*profiling.Info, error) {
+// ProcessProfilingStat is validating the exe file could be profiling and get
info
+func ProcessProfilingStat(pid int32, exePath string) (*profiling.Info, error) {
stat, err := os.Stat(exePath)
if err != nil {
return nil, fmt.Errorf("check file error: %v", err)
@@ -61,35 +68,111 @@ func ExecutableFileProfilingStat(exePath string)
(*profiling.Info, error) {
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)
+}
+
+func analyzeProfilingInfo(context *analyzeContext, pid int32)
(*profiling.Info, error) {
+ // analyze process mapping
+ mapFile, _ := os.Open(host2.GetFileInHost(fmt.Sprintf("/proc/%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.GetFileInHost(fmt.Sprintf("/proc/%d/root%s", pid, moduleName))
- var lastError error
- for _, finder := range profilingStatFinderList {
- if finder.IsSupport(exePath) {
- if r, err1 := analyzeByFinder(exePath, finder); err1 ==
nil {
- return r, nil
- }
- lastError = err
+ module, err = context.GetFinder(modulePath).ToModule(pid,
moduleName, modulePath, []*profiling.ModuleRange{moduleRange})
+ if err != nil {
+ return nil, fmt.Errorf("could not init the module: %s,
error: %v", moduleName, err)
}
+ modules[moduleName] = module
}
+ return profiling.NewInfo(modules), nil
+}
- if lastError == nil {
- lastError = fmt.Errorf("could not found library to analyze the
file")
+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
+}
- return nil, lastError
+func isIgnoreModuleName(name string) bool {
+ return len(name) > 0 &&
+ (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.HasSuffix(name, ".map"))
}
-func analyzeByFinder(exePath string, finder profiling.StatFinder)
(*profiling.Info, error) {
- // do analyze
- info, err := finder.Analyze(exePath)
- if err != nil {
- return nil, err
+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
}
- // order the symbols by address
- sort.SliceStable(info.Symbols, func(i, j int) bool {
- return info.Symbols[i].Location < info.Symbols[j].Location
- })
+ // find all finders
+ for _, f := range profilingStatFinderList {
+ if f.IsSupport(name) {
+ a.pathToFinder[name] = f
+ return f
+ }
+ }
- return info, nil
+ // not support
+ n := profiling.NewNotSupport()
+ a.pathToFinder[name] = n
+ return n
}
diff --git a/pkg/tools/profiling/api.go b/pkg/tools/profiling/api.go
index 6256e08..5392ca7 100644
--- a/pkg/tools/profiling/api.go
+++ b/pkg/tools/profiling/api.go
@@ -17,16 +17,43 @@
package profiling
-import "github.com/ianlancetaylor/demangle"
+import (
+ "github.com/ianlancetaylor/demangle"
+)
-var KernelSymbolFilePath = "/proc/kallsyms"
+type ModuleType int8
+
+var (
+ KernelSymbolFilePath = "/proc/kallsyms"
+)
+
+const (
+ ModuleTypeExec ModuleType = iota
+ ModuleTypeSo
+ ModuleTypePerfMap
+ ModuleTypeVDSO
+ ModuleTypeUnknown
+)
// Info of profiling process
type Info struct {
- Symbols []*Symbol
+ 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
@@ -36,12 +63,18 @@ type Symbol struct {
type StatFinder interface {
// IsSupport to stat the executable file for profiling
IsSupport(filePath string) bool
- // Analyze the executable file
- Analyze(filePath string) (*Info, error)
+ // 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(symbols []*Symbol) *Info {
- return &Info{Symbols: symbols, cacheAddrToSymbol:
make(map[uint64]string)}
+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
@@ -68,35 +101,62 @@ func (i *Info) FindSymbolName(address uint64) string {
if d := i.cacheAddrToSymbol[address]; d != "" {
return d
}
- symbols := i.Symbols
+ for _, mod := range i.Modules {
+ offset, c := mod.contains(address)
+ if !c {
+ continue
+ }
+
+ if sym := mod.findAddr(offset); sym != nil {
+ name := processSymbolName(sym.Name)
+ i.cacheAddrToSymbol[address] = name
+ return name
+ }
+ }
+ return ""
+}
+
+func (m *Module) contains(addr uint64) (uint64, bool) {
+ for _, r := range m.Ranges {
+ if addr >= r.StartAddr && addr < r.EndAddr {
+ if m.Type == ModuleTypeSo || m.Type == ModuleTypeVDSO {
+ offset := addr - r.StartAddr + r.FileOffset
+ offset += m.SoAddr - m.SoOffset
+ return offset, true
+ }
+ return addr, true
+ }
+ }
+ return 0, false
+}
+func (m *Module) findAddr(offset uint64) *Symbol {
start := 0
- end := len(symbols) - 1
+ end := len(m.Symbols) - 1
for start < end {
mid := start + (end-start)/2
- result := int64(address) - int64(symbols[mid].Location)
+ result := int64(offset) - int64(m.Symbols[mid].Location)
if result < 0 {
end = mid
} else if result > 0 {
start = mid + 1
} else {
- s := processSymbolName(symbols[mid].Name)
- i.cacheAddrToSymbol[address] = s
- return s
+ return m.Symbols[mid]
}
}
- if start >= 1 && symbols[start-1].Location < address && address <
symbols[start].Location {
- s := processSymbolName(symbols[start-1].Name)
- i.cacheAddrToSymbol[address] = s
- return s
+ if start >= 1 && m.Symbols[start-1].Location < offset && offset <
m.Symbols[start].Location {
+ return m.Symbols[start-1]
}
- return ""
+ 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] == '$' {
diff --git a/pkg/tools/profiling/go_library.go
b/pkg/tools/profiling/go_library.go
index 17dba26..9284d74 100644
--- a/pkg/tools/profiling/go_library.go
+++ b/pkg/tools/profiling/go_library.go
@@ -20,6 +20,10 @@ package profiling
import (
"debug/elf"
"fmt"
+ "sort"
+ "strings"
+
+ "github.com/apache/skywalking-rover/pkg/tools/path"
)
// GoLibrary is using build-in elf reader to read
@@ -31,22 +35,29 @@ func NewGoLibrary() *GoLibrary {
}
func (l *GoLibrary) IsSupport(filePath string) bool {
+ f, err := elf.Open(filePath)
+ if err != nil {
+ return false
+ }
+ _ = f.Close()
return true
}
-func (l *GoLibrary) Analyze(filePath string) (*Info, error) {
+func (l *GoLibrary) AnalyzeSymbols(filePath string) ([]*Symbol, error) {
// read els file
file, err := elf.Open(filePath)
if err != nil {
- return nil, fmt.Errorf("read ELF file error: %v", err)
+ return nil, err
}
defer file.Close()
// exist symbol data
symbols, err := file.Symbols()
if err != nil || len(symbols) == 0 {
- return nil, fmt.Errorf("read symbol data failure or no symbole
data: %v", err)
+ return nil, nil
}
+ dySyms, _ := file.DynamicSymbols()
+ symbols = append(symbols, dySyms...)
// adapt symbol struct
data := make([]*Symbol, len(symbols))
@@ -54,5 +65,54 @@ func (l *GoLibrary) Analyze(filePath string) (*Info, error) {
data[i] = &Symbol{Name: sym.Name, Location: sym.Value}
}
- return newInfo(data), nil
+ sort.SliceStable(data, func(i, j int) bool {
+ return data[i].Location < data[j].Location
+ })
+
+ return data, nil
+}
+
+func (l *GoLibrary) ToModule(pid int32, modName, modPath string, moduleRange
[]*ModuleRange) (*Module, error) {
+ res := &Module{}
+ res.Name = modName
+ res.Path = modPath
+ res.Ranges = moduleRange
+ file, err := elf.Open(modPath)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ header := file.FileHeader
+ mType := ModuleTypeUnknown
+ switch header.Type {
+ case elf.ET_EXEC:
+ mType = ModuleTypeExec
+ case elf.ET_DYN:
+ mType = ModuleTypeSo
+ }
+
+ if mType == ModuleTypeUnknown {
+ if strings.HasSuffix(modPath, ".map") && path.Exists(modPath) {
+ mType = ModuleTypePerfMap
+ } else if modName == "[vdso]" {
+ mType = ModuleTypeVDSO
+ }
+ } else if mType == ModuleTypeSo {
+ section := file.Section(".text")
+ if section == nil {
+ return nil, fmt.Errorf("could not found .text section
in so file: %s", modName)
+ }
+ res.SoAddr = section.Addr
+ res.SoOffset = section.Offset
+ }
+ res.Type = mType
+
+ // load all symbols
+ res.Symbols, err = l.AnalyzeSymbols(modPath)
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
}
diff --git a/pkg/tools/profiling/kernel.go b/pkg/tools/profiling/kernel.go
index ebf7d4e..0b1a513 100644
--- a/pkg/tools/profiling/kernel.go
+++ b/pkg/tools/profiling/kernel.go
@@ -65,5 +65,12 @@ func (k *KernelFinder) Analyze(filepath string) (*Info,
error) {
})
}
- return newInfo(symbols), nil
+ kernelModule := &Module{
+ Name: "kernel",
+ Symbols: symbols,
+ }
+
+ return NewInfo(map[string]*Module{
+ "kernel": kernelModule,
+ }), nil
}
diff --git a/pkg/tools/profiling/go_library.go
b/pkg/tools/profiling/not_support.go
similarity index 53%
copy from pkg/tools/profiling/go_library.go
copy to pkg/tools/profiling/not_support.go
index 17dba26..6791c3f 100644
--- a/pkg/tools/profiling/go_library.go
+++ b/pkg/tools/profiling/not_support.go
@@ -18,41 +18,25 @@
package profiling
import (
- "debug/elf"
"fmt"
)
-// GoLibrary is using build-in elf reader to read
-type GoLibrary struct {
+type NotSupport struct {
}
-func NewGoLibrary() *GoLibrary {
- return &GoLibrary{}
+func NewNotSupport() *NotSupport {
+ return &NotSupport{}
}
-func (l *GoLibrary) IsSupport(filePath string) bool {
+func (l *NotSupport) IsSupport(filePath string) bool {
+ // handle all not support file
return true
}
-func (l *GoLibrary) Analyze(filePath string) (*Info, error) {
- // read els file
- file, err := elf.Open(filePath)
- if err != nil {
- return nil, fmt.Errorf("read ELF file error: %v", err)
- }
- defer file.Close()
-
- // exist symbol data
- symbols, err := file.Symbols()
- if err != nil || len(symbols) == 0 {
- return nil, fmt.Errorf("read symbol data failure or no symbole
data: %v", err)
- }
-
- // adapt symbol struct
- data := make([]*Symbol, len(symbols))
- for i, sym := range symbols {
- data[i] = &Symbol{Name: sym.Name, Location: sym.Value}
- }
+func (l *NotSupport) AnalyzeSymbols(filePath string) ([]*Symbol, error) {
+ return nil, fmt.Errorf("not support")
+}
- return newInfo(data), nil
+func (l *NotSupport) ToModule(pid int32, modName, modPath string, moduleRange
[]*ModuleRange) (*Module, error) {
+ return nil, fmt.Errorf("not support")
}
diff --git a/pkg/tools/profiling/objdump.go b/pkg/tools/profiling/objdump.go
deleted file mode 100644
index 6d906f8..0000000
--- a/pkg/tools/profiling/objdump.go
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 (
- "bufio"
- "bytes"
- "fmt"
- "os/exec"
- "regexp"
- "strconv"
-)
-
-var objDumpOutputFormat =
"^([0-9a-f]+)\\s+\\w?\\s+\\w?\\s+\\S+\\s*[0-9a-f]*\\s+(\\S+)$"
-
-// ObjDump is using `objdump` command to read
-type ObjDump struct {
- commandPath string
- outputRegex *regexp.Regexp
-}
-
-func NewObjDump() *ObjDump {
- path, _ := exec.LookPath("objdump")
- compile := regexp.MustCompile(objDumpOutputFormat)
- return &ObjDump{commandPath: path, outputRegex: compile}
-}
-
-func (o *ObjDump) IsSupport(filepath string) bool {
- return o.commandPath != ""
-}
-
-func (o *ObjDump) Analyze(filepath string) (*Info, error) {
- resBytes, err := exec.Command(o.commandPath, "--syms",
filepath).Output() // #nosec G204
- if err != nil {
- return nil, err
- }
-
- symbols := make([]*Symbol, 0)
- scanner := bufio.NewScanner(bytes.NewReader(resBytes))
- for scanner.Scan() {
- text := scanner.Text()
- if text == "no symbols" {
- return nil, fmt.Errorf("no symbols")
- }
- submatch := o.outputRegex.FindStringSubmatch(text)
- if len(submatch) == 0 {
- continue
- }
- atoi, err := strconv.ParseUint(submatch[1], 16, 64)
- if err != nil {
- continue
- }
- symbols = append(symbols, &Symbol{Name: submatch[2], Location:
atoi})
- }
- return newInfo(symbols), nil
-}
diff --git a/test/e2e/cases/profiling/rust/Dockerfile.sqrt
b/test/e2e/cases/profiling/rust/Dockerfile.sqrt
new file mode 100644
index 0000000..402b8b1
--- /dev/null
+++ b/test/e2e/cases/profiling/rust/Dockerfile.sqrt
@@ -0,0 +1,23 @@
+# Licensed to the 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. The 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.
+
+FROM rust:1.60
+
+WORKDIR /
+COPY sqrt.rs /sqrt.rs
+RUN rustc -o sqrt sqrt.rs
+
+CMD ["/sqrt"]
\ No newline at end of file
diff --git a/.asf.yaml b/test/e2e/cases/profiling/rust/docker-compose.yml
similarity index 57%
copy from .asf.yaml
copy to test/e2e/cases/profiling/rust/docker-compose.yml
index 630b405..0fc3d94 100644
--- a/.asf.yaml
+++ b/test/e2e/cases/profiling/rust/docker-compose.yml
@@ -1,4 +1,3 @@
-#
# Licensed to the 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.
@@ -13,30 +12,31 @@
# 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.
-#
-github:
- description: Metrics collector and ebpf-based profiler for C, C++, and Golang
- homepage: https://skywalking.apache.org/
- labels:
- - skywalking
- - observability
- - apm
- - service-mesh
- - ebpf
- - profile
- - network
- enabled_merge_buttons:
- squash: true
- merge: false
- rebase: false
- protected_branches:
- main:
- required_status_checks:
- strict: true
- contexts:
- - Required
- required_pull_request_reviews:
- dismiss_stale_reviews: true
- required_approving_review_count: 1
+version: '2.1'
+
+services:
+ oap:
+ extends:
+ file: ../../../base/base-compose.yml
+ service: oap
+ ports:
+ - 12800:12800
+
+ rover:
+ extends:
+ file: ../../../base/base-compose.yml
+ service: rover
+ depends_on:
+ oap:
+ condition: service_healthy
+
+ sqrt_rust:
+ build:
+ context: .
+ dockerfile: Dockerfile.sqrt
+ networks:
+ - e2e
+networks:
+ e2e:
\ No newline at end of file
diff --git a/test/e2e/cases/profiling/rust/e2e.yaml
b/test/e2e/cases/profiling/rust/e2e.yaml
new file mode 100644
index 0000000..0d79f55
--- /dev/null
+++ b/test/e2e/cases/profiling/rust/e2e.yaml
@@ -0,0 +1,45 @@
+# Licensed to the 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.
+# The 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.
+
+setup:
+ env: compose
+ file: docker-compose.yml
+ timeout: 20m
+ init-system-environment: ../../../base/env
+ steps:
+ - name: set PATH
+ command: export PATH=/tmp/skywalking-infra-e2e/bin:$PATH
+ - name: install yq
+ command: bash test/e2e/base/scripts/prepare/setup-e2e-shell/install.sh yq
+ - name: install swctl
+ command: bash test/e2e/base/scripts/prepare/setup-e2e-shell/install.sh
swctl
+
+verify:
+ # verify with retry strategy
+ retry:
+ # max retry count
+ count: 20
+ # the interval between two retries, in millisecond.
+ interval: 10s
+ cases:
+ - includes:
+ - ../profiling-cases.yaml
+ - query: |
+ taskid=$(swctl --base-url=http://${oap_host}:${oap_12800}/graphql
--display yaml profiling ebpf list --service-name sqrt |yq e '.[0].taskid' -)
+ scheduleid=$(swctl --base-url=http://${oap_host}:${oap_12800}/graphql
--display yaml profiling ebpf schedules --task-id=$taskid |yq e
'.[0].scheduleid' -);
+ start=$(swctl --base-url=http://${oap_host}:${oap_12800}/graphql
--display yaml profiling ebpf schedules --task-id=$taskid | yq e
'.[0].starttime' -)
+ end=$(swctl --base-url=http://${oap_host}:${oap_12800}/graphql
--display yaml profiling ebpf schedules --task-id=$taskid | yq e '.[0].endtime'
-)
+ swctl --base-url=http://${oap_host}:${oap_12800}/graphql --display
yaml profiling ebpf analysis --schedule-id=$scheduleid --time-ranges=$start-$end
+ expected: profiling-analysis.yml
\ No newline at end of file
diff --git a/.asf.yaml b/test/e2e/cases/profiling/rust/profiling-analysis.yml
similarity index 57%
copy from .asf.yaml
copy to test/e2e/cases/profiling/rust/profiling-analysis.yml
index 630b405..0475b24 100644
--- a/.asf.yaml
+++ b/test/e2e/cases/profiling/rust/profiling-analysis.yml
@@ -1,4 +1,3 @@
-#
# Licensed to the 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.
@@ -13,30 +12,16 @@
# 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.
-#
-
-github:
- description: Metrics collector and ebpf-based profiler for C, C++, and Golang
- homepage: https://skywalking.apache.org/
- labels:
- - skywalking
- - observability
- - apm
- - service-mesh
- - ebpf
- - profile
- - network
- enabled_merge_buttons:
- squash: true
- merge: false
- rebase: false
- protected_branches:
- main:
- required_status_checks:
- strict: true
- contexts:
- - Required
- required_pull_request_reviews:
- dismiss_stale_reviews: true
- required_approving_review_count: 1
+tip: null
+trees:
+{{- contains .trees }}
+- elements:
+ {{- contains .elements }}
+ - id: "{{ notEmpty .id }}"
+ parentid: "{{ notEmpty .parentid }}"
+ symbol: {{if ne .symbol "[MISSING]" }}{{.symbol}}{{else}}contains not
missing{{end}}
+ stacktype: USER_SPACE
+ dumpcount: {{ gt .dumpcount 0 }}
+{{- end }}
+{{- end }}
\ No newline at end of file
diff --git a/test/e2e/cases/profiling/rust/sqrt.rs
b/test/e2e/cases/profiling/rust/sqrt.rs
new file mode 100644
index 0000000..d9657ad
--- /dev/null
+++ b/test/e2e/cases/profiling/rust/sqrt.rs
@@ -0,0 +1,28 @@
+// Licensed to the 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. The 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.
+
+#[inline(never)]
+fn sqrt() {
+ let positive = 4.0_f64;
+ loop {
+ _ = positive.sqrt();
+ }
+}
+
+#[inline(never)]
+fn main() {
+ sqrt()
+}
\ No newline at end of file