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.
 
 [![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

Reply via email to