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 a11d119  Support OFF CPU Profiling (#34)
a11d119 is described below

commit a11d119de35944f3bd17103efb69263a18b7c6e2
Author: mrproliu <[email protected]>
AuthorDate: Wed May 18 23:35:06 2022 +0800

    Support OFF CPU Profiling (#34)
---
 CHANGES.md                                         |  11 ++
 bpf/include/api.h                                  |  27 ++-
 bpf/profiling/offcpu.c                             |  82 ++++++++
 .../task/base/target.go => bpf/profiling/offcpu.h  |  52 +++---
 docker/Dockerfile.base                             |   6 +-
 go.mod                                             |   2 +-
 go.sum                                             |   4 +-
 pkg/profiling/task/base/runner.go                  |  65 +++++++
 pkg/profiling/task/base/target.go                  |   5 +-
 pkg/profiling/task/offcpu/runner.go                | 206 +++++++++++++++++++++
 pkg/profiling/task/oncpu/runner.go                 |  51 ++---
 pkg/profiling/task/registion.go                    |   2 +
 12 files changed, 431 insertions(+), 82 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index c545b0e..a6812cc 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,6 +2,17 @@ Changes by Version
 ==================
 Release Notes.
 
+0.2.0
+------------------
+#### Features
+* Support `OFF_CPU` Profiling.
+
+#### Bug Fixes
+
+#### Issues and PR
+- All issues are 
[here](https://github.com/apache/skywalking/milestone/134?closed=1)
+- All and pull requests are 
[here](https://github.com/apache/skywalking-rover/milestone/2?closed=1)
+
 0.1.0
 ------------------
 #### Features
diff --git a/bpf/include/api.h b/bpf/include/api.h
index f058e20..a136dc7 100644
--- a/bpf/include/api.h
+++ b/bpf/include/api.h
@@ -19,22 +19,19 @@
 #define __BPF_API__
 
 // include linux relate bpf
+#include <stddef.h>
+#include <linux/sched.h>
 #include <linux/bpf.h>
+#include <linux/ptrace.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
 
-#define __uint(name, val) int (*name)[val]
-#define __type(name, val) typeof(val) *name
-#define __array(name, val) typeof(val) *name[]
-
-// Method Selection
-#define SEC(name) \
-       _Pragma("GCC diagnostic push")                                      \
-       _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"")          \
-       __attribute__((section(name), used))                                \
-       _Pragma("GCC diagnostic pop")                                       \
-
-// which reference what we need
-struct pt_regs;
-static long (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void 
*data, __u64 size) = (void *) 25;
-static long (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 
27;
+#define _(P)                                                                   
\
+       ({                                                                     \
+               typeof(P) val;                                                 \
+               bpf_probe_read_kernel(&val, sizeof(val), &(P));                \
+               val;                                                           \
+       })
 
 #endif
\ No newline at end of file
diff --git a/bpf/profiling/offcpu.c b/bpf/profiling/offcpu.c
new file mode 100644
index 0000000..b1a7ff1
--- /dev/null
+++ b/bpf/profiling/offcpu.c
@@ -0,0 +1,82 @@
+// 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.
+
+#include "api.h"
+#include "offcpu.h"
+
+char __license[] SEC("license") = "Dual MIT/GPL";
+
+SEC("kprobe/finish_task_switch")
+int do_finish_task_switch(struct pt_regs *ctx) {
+    int monitor_pid;
+    asm("%0 = MONITOR_PID ll" : "=r"(monitor_pid));
+
+    __u32 pid, tgid;
+    __u64 ts, *tsp;
+
+    struct task_struct *prev = (void *) PT_REGS_PARM1(ctx);
+    pid = _(prev->pid);
+    tgid = _(prev->tgid);
+
+    // in kernel, tgid means the process id
+    // monitor_pid is the same concept with process id
+    // only the monitor pid is same with previous process id could record the 
start time
+    if (tgid == monitor_pid) {
+        ts = bpf_ktime_get_ns();
+        bpf_map_update_elem(&starts, &pid, &ts, BPF_ANY);
+    }
+
+    // if current process have record start time in the map
+    // means the monitored process switch to the on-cpu
+    struct task_struct *current = (void *)bpf_get_current_task();
+    pid = _(current->pid);
+    tgid = _(current->tgid);
+    if (tgid != monitor_pid) {
+        return 0;
+    }
+    tsp = bpf_map_lookup_elem(&starts, &pid);
+    if (tsp == 0) {
+        return 0;        // missed start or filtered
+    }
+
+    // calculate the duration(on->off to off->on)
+    __u64 t_start = *tsp;
+    __u64 t_end = bpf_ktime_get_ns();
+    bpf_map_delete_elem(&starts, &pid);
+    if (t_start > t_end) {
+        return 0;
+    }
+
+    // create map key
+    struct key_t key = {};
+    key.kernel_stack_id = bpf_get_stackid(ctx, &stacks, 0);
+    key.user_stack_id = bpf_get_stackid(ctx, &stacks, (1ULL << 8));
+
+    // add counters
+    struct value_t *val;
+    val = bpf_map_lookup_elem(&counts, &key);
+    if (!val) {
+        struct value_t value = {};
+         bpf_map_update_elem(&counts, &key, &value, BPF_NOEXIST);
+         val = bpf_map_lookup_elem(&counts, &key);
+         if (!val)
+             return 0;
+    }
+    (*val).counts += 1;
+    (*val).deltas += t_end - t_start;
+    return 0;
+}
\ No newline at end of file
diff --git a/pkg/profiling/task/base/target.go b/bpf/profiling/offcpu.h
similarity index 53%
copy from pkg/profiling/task/base/target.go
copy to bpf/profiling/offcpu.h
index 849729b..cc5a73b 100644
--- a/pkg/profiling/task/base/target.go
+++ b/bpf/profiling/offcpu.h
@@ -15,30 +15,38 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package base
+struct key_t {
+    int user_stack_id;
+    int kernel_stack_id;
+};
 
-import (
-       "fmt"
+struct value_t {
+    __u64 counts;
+    __u64 deltas;
+};
 
-       v3 "skywalking.apache.org/repo/goapi/collect/common/v3"
-)
+struct {
+    __uint(type, BPF_MAP_TYPE_STACK_TRACE);
+    __uint(key_size, sizeof(__u32));
+    __uint(value_size, 100 * sizeof(__u64));
+    __uint(max_entries, 10000);
+} stacks SEC(".maps");
 
-type TargetType string
+struct {
+       __uint(type, BPF_MAP_TYPE_LRU_HASH);
+       __uint(key_size, sizeof(__u32));
+       __uint(value_size, sizeof(__u64));
+    __uint(max_entries, 10000);
+} starts SEC(".maps");
 
-const (
-       TargetTypeOnCPU TargetType = "ON_CPU"
-)
+struct {
+       __uint(type, BPF_MAP_TYPE_HASH);
+       __type(key, struct key_t);
+       __type(value, struct value_t);
+       __uint(max_entries, 10000);
+} counts SEC(".maps");
 
-func ParseTargetType(err error, val string) (TargetType, error) {
-       if err != nil {
-               return "", err
-       }
-       if TargetType(val) == TargetTypeOnCPU {
-               return TargetTypeOnCPU, nil
-       }
-       return "", fmt.Errorf("could not found target type: %s", val)
-}
-
-func (t TargetType) InitTask(task *ProfilingTask, command *v3.Command) error {
-       return nil
-}
+struct task_struct {
+       __u32 pid;
+    __u32 tgid;
+}  __attribute__((preserve_access_index));
\ No newline at end of file
diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base
index b938223..dfb031f 100644
--- a/docker/Dockerfile.base
+++ b/docker/Dockerfile.base
@@ -18,8 +18,10 @@ FROM golang:1.17
 
 RUN apt update && \
     apt install -y libbpf-dev lsb-release wget software-properties-common && \
-    mkdir /usr/include/asm && \
-    cp /usr/include/asm-generic/* /usr/include/asm && \
+    mkdir -p /usr/include/asm /usr/include/bits /usr/include/sys && \
+    cp -r /usr/include/$(uname -m)*/asm/* /usr/include/asm && \
+    cp -r /usr/include/$(uname -m)*/bits/* /usr/include/bits && \
+    cp -r /usr/include/asm-generic/* /usr/include/asm && \
     wget https://apt.llvm.org/llvm.sh && \
     chmod +x llvm.sh && \
     ./llvm.sh 13
diff --git a/go.mod b/go.mod
index 242a8d1..f77d120 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
        k8s.io/api v0.23.5
        k8s.io/apimachinery v0.23.5
        k8s.io/client-go v0.23.5
-       skywalking.apache.org/repo/goapi v0.0.0-20220513074115-4af2c2d37d2f
+       skywalking.apache.org/repo/goapi v0.0.0-20220518063910-af3e2df60bce
 )
 
 require (
diff --git a/go.sum b/go.sum
index 576eee9..6e64dd0 100644
--- a/go.sum
+++ b/go.sum
@@ -940,5 +940,5 @@ sigs.k8s.io/structured-merge-diff/v4 v4.2.1 
h1:bKCqE9GvQ5tiVHn5rfn1r+yao3aLQEaLz
 sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod 
h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
 sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
-skywalking.apache.org/repo/goapi v0.0.0-20220513074115-4af2c2d37d2f 
h1:InBru/3MVpcVoGPGRjY+A5LpAi992E37j6dCvvNqF/w=
-skywalking.apache.org/repo/goapi v0.0.0-20220513074115-4af2c2d37d2f/go.mod 
h1:uWwwvhcwe2MD/nJCg0c1EE/eL6KzaBosLHDfMFoEJ30=
+skywalking.apache.org/repo/goapi v0.0.0-20220518063910-af3e2df60bce 
h1:3wCiFWEEREdxe0T/PJM1S6DDqDsKETYVNtfaJ4zWmdQ=
+skywalking.apache.org/repo/goapi v0.0.0-20220518063910-af3e2df60bce/go.mod 
h1:uWwwvhcwe2MD/nJCg0c1EE/eL6KzaBosLHDfMFoEJ30=
diff --git a/pkg/profiling/task/base/runner.go 
b/pkg/profiling/task/base/runner.go
new file mode 100644
index 0000000..a874893
--- /dev/null
+++ b/pkg/profiling/task/base/runner.go
@@ -0,0 +1,65 @@
+// 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 base
+
+import (
+       "math"
+       "sync"
+
+       "github.com/cilium/ebpf"
+
+       "github.com/apache/skywalking-rover/pkg/logger"
+       "github.com/apache/skywalking-rover/pkg/tools/profiling"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/v3"
+)
+
+var log = logger.GetLogger("profiling", "task", "base")
+
+type Runner struct {
+       StackNotFoundCache map[uint32]bool
+       ShutdownOnce       sync.Once
+}
+
+func NewBaseRunner() *Runner {
+       return &Runner{StackNotFoundCache: make(map[uint32]bool)}
+}
+
+func (r *Runner) GenerateProfilingData(profilingInfo *profiling.Info, stackID 
uint32, stackMap *ebpf.Map,
+       stackType v3.EBPFProfilingStackType, symbolArray []uint64) 
*v3.EBPFProfilingStackMetadata {
+       if profilingInfo == nil || stackID <= 0 || stackID == math.MaxUint32 {
+               return nil
+       }
+       if err := stackMap.Lookup(stackID, symbolArray); err != nil {
+               if r.StackNotFoundCache[stackID] {
+                       return nil
+               }
+               r.StackNotFoundCache[stackID] = true
+               log.Warnf("error to lookup %v stack: %d, error: %v", stackType, 
stackID, err)
+               return nil
+       }
+       symbols := profilingInfo.FindSymbols(symbolArray, MissingSymbol)
+       if len(symbols) == 0 {
+               return nil
+       }
+       return &v3.EBPFProfilingStackMetadata{
+               StackType:    stackType,
+               StackId:      int32(stackID),
+               StackSymbols: symbols,
+       }
+}
diff --git a/pkg/profiling/task/base/target.go 
b/pkg/profiling/task/base/target.go
index 849729b..25054f0 100644
--- a/pkg/profiling/task/base/target.go
+++ b/pkg/profiling/task/base/target.go
@@ -26,7 +26,8 @@ import (
 type TargetType string
 
 const (
-       TargetTypeOnCPU TargetType = "ON_CPU"
+       TargetTypeOnCPU  TargetType = "ON_CPU"
+       TargetTypeOffCPU TargetType = "OFF_CPU"
 )
 
 func ParseTargetType(err error, val string) (TargetType, error) {
@@ -35,6 +36,8 @@ func ParseTargetType(err error, val string) (TargetType, 
error) {
        }
        if TargetType(val) == TargetTypeOnCPU {
                return TargetTypeOnCPU, nil
+       } else if TargetType(val) == TargetTypeOffCPU {
+               return TargetTypeOffCPU, nil
        }
        return "", fmt.Errorf("could not found target type: %s", val)
 }
diff --git a/pkg/profiling/task/offcpu/runner.go 
b/pkg/profiling/task/offcpu/runner.go
new file mode 100644
index 0000000..85fea37
--- /dev/null
+++ b/pkg/profiling/task/offcpu/runner.go
@@ -0,0 +1,206 @@
+// 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.
+
+//go:build linux
+
+package offcpu
+
+import (
+       "context"
+       "fmt"
+       "time"
+
+       "github.com/hashicorp/go-multierror"
+
+       "github.com/cilium/ebpf/link"
+
+       "github.com/apache/skywalking-rover/pkg/logger"
+       "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/profiling/task/base"
+       "github.com/apache/skywalking-rover/pkg/tools"
+       "github.com/apache/skywalking-rover/pkg/tools/profiling"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/v3"
+)
+
+// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
+// nolint
+//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags 
$BPF_CFLAGS bpf $REPO_ROOT/bpf/profiling/offcpu.c -- -I$REPO_ROOT/bpf/include 
-D__TARGET_ARCH_x86
+
+var log = logger.GetLogger("profiling", "task", "offcpu")
+
+type ProcessStack struct {
+       UserStackID   uint32
+       KernelStackID uint32
+}
+
+type StackCounter struct {
+       Times  uint64 // total execute count
+       Deltas uint64 // total execute delta duration(nanoseconds)
+}
+
+type Runner struct {
+       base             *base.Runner
+       pid              int32
+       processProfiling *profiling.Info
+       kernelProfiling  *profiling.Info
+
+       // runtime
+       previousStacks  map[ProcessStack]*StackCounter
+       bpf             *bpfObjects
+       kprobe          link.Link
+       stopChan        chan bool
+       flushDataNotify context.CancelFunc
+}
+
+func NewRunner(config *base.TaskConfig) (base.ProfileTaskRunner, error) {
+       return &Runner{
+               base: base.NewBaseRunner(),
+       }, nil
+}
+
+func (r *Runner) Init(task *base.ProfilingTask, process api.ProcessInterface) 
error {
+       r.pid = process.Pid()
+       r.processProfiling = process.ProfilingStat()
+       kernelProfiling, err := tools.KernelFileProfilingStat()
+       if err != nil {
+               log.Warnf("could not analyze kernel profiling stats: %v", err)
+       }
+       r.kernelProfiling = kernelProfiling
+       r.previousStacks = make(map[ProcessStack]*StackCounter)
+       r.stopChan = make(chan bool, 1)
+       return nil
+}
+
+func (r *Runner) Run(ctx context.Context, notify 
base.ProfilingRunningSuccessNotify) error {
+       objs := bpfObjects{}
+       spec, err := loadBpf()
+       if err != nil {
+               return err
+       }
+       // update the monitor pid
+       funcName := "do_finish_task_switch"
+       replacedPid := false
+       for i, ins := range spec.Programs[funcName].Instructions {
+               if ins.Reference == "MONITOR_PID" {
+                       spec.Programs[funcName].Instructions[i].Constant = 
int64(r.pid)
+                       spec.Programs[funcName].Instructions[i].Offset = 0
+                       replacedPid = true
+               }
+       }
+       if !replacedPid {
+               return fmt.Errorf("replace the monitor pid failure")
+       }
+       if err1 := spec.LoadAndAssign(&objs, nil); err1 != nil {
+               return err1
+       }
+       r.bpf = &objs
+
+       kprobe, err := link.Kprobe("finish_task_switch", 
objs.DoFinishTaskSwitch)
+       if err != nil {
+               log.Fatalf("link to finish task swtich failure: %v", err)
+       }
+       r.kprobe = kprobe
+
+       notify()
+       <-r.stopChan
+       return nil
+}
+
+func (r *Runner) Stop() error {
+       var err error
+       r.base.ShutdownOnce.Do(func() {
+               // wait for all profiling data been consumed finished
+               cancel, cancelFunc := context.WithCancel(context.Background())
+               r.flushDataNotify = cancelFunc
+               select {
+               case <-cancel.Done():
+               case <-time.After(10 * time.Second):
+               }
+
+               if r.bpf != nil {
+                       if err1 := r.bpf.Close(); err1 != nil {
+                               err = multierror.Append(err, err1)
+                       }
+                       r.bpf = nil
+               }
+               if r.kprobe != nil {
+                       if err1 := r.kprobe.Close(); err1 != nil {
+                               err = multierror.Append(err, err1)
+                       }
+               }
+               close(r.stopChan)
+       })
+       return err
+}
+
+func (r *Runner) FlushData() ([]*v3.EBPFProfilingData, error) {
+       if r.bpf == nil {
+               return nil, nil
+       }
+       if r.flushDataNotify == nil {
+               return nil, nil
+       }
+       var stack ProcessStack
+       var counter StackCounter
+       iterate := r.bpf.Counts.Iterate()
+       stacks := r.bpf.Stacks
+       result := make([]*v3.EBPFProfilingData, 0)
+       stackSymbols := make([]uint64, 100)
+       for iterate.Next(&stack, &counter) {
+               metadatas := make([]*v3.EBPFProfilingStackMetadata, 0)
+               // kernel stack
+               if d := r.base.GenerateProfilingData(r.kernelProfiling, 
stack.KernelStackID, stacks,
+                       v3.EBPFProfilingStackType_PROCESS_KERNEL_SPACE, 
stackSymbols); d != nil {
+                       metadatas = append(metadatas, d)
+               }
+               // user stack
+               if d := r.base.GenerateProfilingData(r.processProfiling, 
stack.UserStackID, stacks,
+                       v3.EBPFProfilingStackType_PROCESS_USER_SPACE, 
stackSymbols); d != nil {
+                       metadatas = append(metadatas, d)
+               }
+
+               if len(metadatas) == 0 {
+                       continue
+               }
+
+               // update the counters in memory
+               switchCount := int32(counter.Times)
+               duration := int64(counter.Deltas)
+               existCounter := r.previousStacks[stack]
+               if existCounter != nil {
+                       switchCount -= int32(existCounter.Times)
+                       duration -= int64(existCounter.Deltas)
+               }
+               r.previousStacks[stack] = &counter
+
+               result = append(result, &v3.EBPFProfilingData{
+                       Profiling: &v3.EBPFProfilingData_OffCPU{
+                               OffCPU: &v3.EBPFOffCPUProfiling{
+                                       Stacks:      metadatas,
+                                       SwitchCount: switchCount,
+                                       Duration:    duration,
+                               },
+                       },
+               })
+       }
+
+       if r.flushDataNotify != nil {
+               r.flushDataNotify()
+       }
+       return result, nil
+}
diff --git a/pkg/profiling/task/oncpu/runner.go 
b/pkg/profiling/task/oncpu/runner.go
index a929a90..6ea9f47 100644
--- a/pkg/profiling/task/oncpu/runner.go
+++ b/pkg/profiling/task/oncpu/runner.go
@@ -25,10 +25,8 @@ import (
        "encoding/binary"
        "errors"
        "fmt"
-       "math"
        "os"
        "runtime"
-       "sync"
        "time"
 
        "github.com/cilium/ebpf"
@@ -59,19 +57,18 @@ type Event struct {
 }
 
 type Runner struct {
+       base             *base.Runner
        pid              int32
        processProfiling *profiling.Info
        kernelProfiling  *profiling.Info
        dumpPeriod       time.Duration
 
        // runtime
-       perfEventFds       []int
-       countReader        *perf.Reader
-       stackCounter       map[Event]int
-       stackMap           *ebpf.Map
-       stackNotFoundCache map[uint32]bool
-       shutdownOnce       sync.Once
-       flushDataNotify    context.CancelFunc
+       perfEventFds    []int
+       countReader     *perf.Reader
+       stackCounter    map[Event]int
+       stackMap        *ebpf.Map
+       flushDataNotify context.CancelFunc
 }
 
 func NewRunner(config *base.TaskConfig) (base.ProfileTaskRunner, error) {
@@ -86,9 +83,9 @@ func NewRunner(config *base.TaskConfig) 
(base.ProfileTaskRunner, error) {
                return nil, fmt.Errorf("the ON_CPU dump period could not be 
smaller than 1ms")
        }
        return &Runner{
-               dumpPeriod:         dumpPeriod,
-               stackNotFoundCache: make(map[uint32]bool),
-               stackCounter:       make(map[Event]int),
+               base:         base.NewBaseRunner(),
+               dumpPeriod:   dumpPeriod,
+               stackCounter: make(map[Event]int),
        }, nil
 }
 
@@ -201,7 +198,7 @@ func (r *Runner) openPerfEvent(perfFd int) ([]int, error) {
 
 func (r *Runner) Stop() error {
        var result error
-       r.shutdownOnce.Do(func() {
+       r.base.ShutdownOnce.Do(func() {
                for _, fd := range r.perfEventFds {
                        if err := r.closePerfEvent(fd); err != nil {
                                result = multierror.Append(result, err)
@@ -233,13 +230,13 @@ func (r *Runner) FlushData() ([]*v3.EBPFProfilingData, 
error) {
        for event, count := range existsCounters {
                metadatas := make([]*v3.EBPFProfilingStackMetadata, 0)
                // kernel stack
-               if d := r.generateProfilingData(r.kernelProfiling, 
event.KernelStackID,
+               if d := r.base.GenerateProfilingData(r.kernelProfiling, 
event.KernelStackID, r.stackMap,
                        v3.EBPFProfilingStackType_PROCESS_KERNEL_SPACE, 
stackSymbols); d != nil {
                        metadatas = append(metadatas, d)
                }
 
                // user stack
-               if d := r.generateProfilingData(r.processProfiling, 
event.UserStackID,
+               if d := r.base.GenerateProfilingData(r.processProfiling, 
event.UserStackID, r.stackMap,
                        v3.EBPFProfilingStackType_PROCESS_USER_SPACE, 
stackSymbols); d != nil {
                        metadatas = append(metadatas, d)
                }
@@ -266,30 +263,6 @@ func (r *Runner) FlushData() ([]*v3.EBPFProfilingData, 
error) {
        return result, nil
 }
 
-func (r *Runner) generateProfilingData(profilingInfo *profiling.Info, stackID 
uint32,
-       stackType v3.EBPFProfilingStackType, symbolArray []uint64) 
*v3.EBPFProfilingStackMetadata {
-       if profilingInfo == nil || stackID <= 0 || stackID == math.MaxUint32 {
-               return nil
-       }
-       if err := r.stackMap.Lookup(stackID, symbolArray); err != nil {
-               if r.stackNotFoundCache[stackID] {
-                       return nil
-               }
-               r.stackNotFoundCache[stackID] = true
-               log.Warnf("error to lookup %v stack: %d, error: %v", stackType, 
stackID, err)
-               return nil
-       }
-       symbols := profilingInfo.FindSymbols(symbolArray, base.MissingSymbol)
-       if len(symbols) == 0 {
-               return nil
-       }
-       return &v3.EBPFProfilingStackMetadata{
-               StackType:    stackType,
-               StackId:      int32(stackID),
-               StackSymbols: symbols,
-       }
-}
-
 func (r *Runner) flushStackCounter() map[Event]int {
        updateTo := make(map[Event]int)
        updateToP := &updateTo
diff --git a/pkg/profiling/task/registion.go b/pkg/profiling/task/registion.go
index 2e9271b..f88d161 100644
--- a/pkg/profiling/task/registion.go
+++ b/pkg/profiling/task/registion.go
@@ -23,6 +23,7 @@ import (
        "github.com/hashicorp/go-multierror"
 
        "github.com/apache/skywalking-rover/pkg/profiling/task/base"
+       "github.com/apache/skywalking-rover/pkg/profiling/task/offcpu"
        "github.com/apache/skywalking-rover/pkg/profiling/task/oncpu"
 )
 
@@ -30,6 +31,7 @@ var profilingRunners = make(map[base.TargetType]func(config 
*base.TaskConfig) (b
 
 func init() {
        profilingRunners[base.TargetTypeOnCPU] = oncpu.NewRunner
+       profilingRunners[base.TargetTypeOffCPU] = offcpu.NewRunner
 }
 
 func NewProfilingRunner(taskType base.TargetType, taskConfig *base.TaskConfig) 
(base.ProfileTaskRunner, error) {

Reply via email to