You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@skywalking.apache.org by wu...@apache.org on 2022/05/04 09:27:53 UTC

[skywalking-rover] branch main updated: Support analyze the ".so" file symbols (#20)

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 <74...@qq.com>
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.
 
 [![GitHub stars](https://img.shields.io/github/stars/apache/skywalking.svg?style=for-the-badge&label=Stars&logo=github)](https://github.com/apache/skywalking)
 [![Twitter Follow](https://img.shields.io/twitter/follow/asfskywalking.svg?style=for-the-badge&label=Follow&logo=twitter)](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