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 f84d1b6  Implement eBPF Profiling (#7)
f84d1b6 is described below

commit f84d1b69eb3e6b4c6c099c39a86c706e25e2c419
Author: mrproliu <[email protected]>
AuthorDate: Tue Mar 22 10:12:29 2022 +0800

    Implement eBPF Profiling (#7)
---
 .github/workflows/build-and-test.yaml              |  10 +-
 .gitignore                                         |   4 +
 Makefile                                           |  72 +----
 .../finders/base/process.go => bpf/include/api.h   |  36 +--
 pkg/boot/register.go => bpf/profiling/oncpu.c      |  26 +-
 pkg/boot/register.go => bpf/profiling/oncpu.h      |  24 +-
 configs/rover_configs.yaml                         |  16 +-
 docker/Dockerfile.base                             |  27 ++
 go.mod                                             |   4 +-
 go.sum                                             |  15 +-
 pkg/boot/register.go                               |   2 +
 pkg/{boot/register.go => process/api.go}           |  15 +-
 pkg/process/api/process.go                         |   4 +
 pkg/process/finders/base/process.go                |   3 +
 pkg/process/finders/context.go                     |   5 +
 pkg/process/finders/manager.go                     |   4 +
 pkg/process/finders/storage.go                     |  12 +-
 pkg/process/finders/vm/finder.go                   |  36 ++-
 pkg/process/finders/vm/process.go                  |   4 +
 pkg/process/{mdoule.go => module.go}               |   5 +
 pkg/{boot/register.go => profiling/config.go}      |  16 +-
 pkg/profiling/manager.go                           | 156 +++++++++++
 pkg/{process/mdoule.go => profiling/module.go}     |  27 +-
 .../base/process.go => profiling/task/base/api.go} |  28 +-
 .../register.go => profiling/task/base/config.go}  |  16 +-
 .../process.go => profiling/task/base/target.go}   |  32 ++-
 pkg/profiling/task/base/task.go                    | 100 +++++++
 .../process.go => profiling/task/base/trigger.go}  |  40 ++-
 pkg/profiling/task/context.go                      |  80 ++++++
 pkg/profiling/task/manager.go                      | 255 +++++++++++++++++
 pkg/profiling/task/oncpu/runner.go                 | 311 +++++++++++++++++++++
 pkg/profiling/task/registion.go                    |  54 ++++
 pkg/{boot/register.go => tools/host/file.go}       |  19 +-
 pkg/tools/profiling/api.go                         |  50 +++-
 pkg/tools/profiling/go_library.go                  |   2 +-
 pkg/tools/profiling/kernel.go                      |   6 +-
 pkg/tools/profiling/objdump.go                     |   2 +-
 scripts/build/base.mk                              |  55 ++++
 scripts/build/build.mk                             |  38 +++
 scripts/build/check.mk                             |  27 ++
 scripts/build/generate.mk                          |  29 ++
 scripts/build/lint.mk                              |  30 ++
 scripts/build/test.mk                              |  31 ++
 43 files changed, 1529 insertions(+), 199 deletions(-)

diff --git a/.github/workflows/build-and-test.yaml 
b/.github/workflows/build-and-test.yaml
index 2233088..cceb7a8 100644
--- a/.github/workflows/build-and-test.yaml
+++ b/.github/workflows/build-and-test.yaml
@@ -48,10 +48,14 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
       - name: Get dependencies
         run: make deps
-      - name: Lint
-        run: make lint
+      - name: Generate
+        run: make container-generate
       - name: Test
-        run: make test
+        run: make container-test
+      - name: Lint
+        run: make container-lint
+      - name: Make binary
+        run: make linux
       - name: Check CI Consistency
         if: matrix.go-version == '1.17' && matrix.runner == 'ubuntu'
         run: make check
diff --git a/.gitignore b/.gitignore
index 3161f6d..ea40d08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,11 @@
 *.iml
 .DS_Store
 *~
+.cache/
 bin/
 coverage.txt
 
 !/dist/bin/
+
+pkg/**/bpf_*.o
+pkg/**/bpf_*.go
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 4578dda..dc5d6a5 100644
--- a/Makefile
+++ b/Makefile
@@ -15,63 +15,15 @@
 # limitations under the License.
 #
 
-VERSION ?= latest
-HUB ?= apache
-OUT_DIR = bin
-BINARY = skywalking-rover
-
-RELEASE_BIN = skywalking-rover-$(VERSION)-bin
-RELEASE_SRC = skywalking-rover-$(VERSION)-src
-
-SH = sh
-GO = go
-GIT = git
-PROTOC = protoc
-GO_PATH = $$($(GO) env GOPATH)
-GO_BUILD = $(GO) build
-GO_GET = $(GO) get
-GO_TEST = $(GO) test
-GO_LINT = $(GO_PATH)/bin/golangci-lint
-GO_BUILD_FLAGS = -v
-GO_BUILD_LDFLAGS = -X main.version=$(VERSION) -w -s
-GO_TEST_LDFLAGS =
-
-PLATFORMS := linux
-os = $(word 1, $@)
-ARCH = amd64
-
-SHELL = /bin/bash
-
-all: deps verify check
-
-.PHONY: tools
-tools:
-       $(GO_LINT) version || curl -sfL 
https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh 
-s -- -b $(GO_PATH)/bin v1.39.0
-
-deps: tools
-       $(GO_GET) -v -t -d ./...
-
-.PHONY: lint
-lint: tools
-       $(GO_LINT) run -v --timeout 5m ./...
-
-.PHONY: test
-test: clean
-       $(GO_TEST) -ldflags "$(GO_TEST_LDFLAGS)" ./... 
-coverprofile=coverage.txt -covermode=atomic
-
-.PHONY: verify
-verify: clean lint test
-
-.PHONY: clean
-clean: tools
-       -rm -rf coverage.txt
-
-.PHONY: check
-check: clean
-       $(GO) mod tidy > /dev/null
-       @if [ ! -z "`git status -s`" ]; then \
-               echo "Following files are not consistent with CI:"; \
-               git status -s; \
-               git diff; \
-               exit 1; \
-       fi
+include scripts/build/base.mk
+include scripts/build/generate.mk
+include scripts/build/test.mk
+include scripts/build/lint.mk
+include scripts/build/build.mk
+include scripts/build/check.mk
+
+.PHONY: all
+all: clean test lint build
+
+.PHONY: container-all
+container-all: clean container-generate build
\ No newline at end of file
diff --git a/pkg/process/finders/base/process.go b/bpf/include/api.h
similarity index 53%
copy from pkg/process/finders/base/process.go
copy to bpf/include/api.h
index 7358bd7..f058e20 100644
--- a/pkg/process/finders/base/process.go
+++ b/bpf/include/api.h
@@ -15,22 +15,26 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package base
+#ifndef __BPF_API__
+#define __BPF_API__
 
-import (
-       "github.com/shirou/gopsutil/process"
+// include linux relate bpf
+#include <linux/bpf.h>
 
-       "github.com/apache/skywalking-rover/pkg/process/api"
-)
+#define __uint(name, val) int (*name)[val]
+#define __type(name, val) typeof(val) *name
+#define __array(name, val) typeof(val) *name[]
 
-// DetectedProcess from the finder
-type DetectedProcess interface {
-       // Pid of process in host
-       Pid() int32
-       // OriginalProcess is works for query the process data
-       OriginalProcess() *process.Process
-       // Entity of process, is related with backend entity
-       Entity() *api.ProcessEntity
-       // DetectType define the process find type
-       DetectType() api.ProcessDetectType
-}
+// 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;
+
+#endif
\ No newline at end of file
diff --git a/pkg/boot/register.go b/bpf/profiling/oncpu.c
similarity index 65%
copy from pkg/boot/register.go
copy to bpf/profiling/oncpu.c
index 061cb4a..fa4321a 100644
--- a/pkg/boot/register.go
+++ b/bpf/profiling/oncpu.c
@@ -15,16 +15,20 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+#include "api.h"
+#include "oncpu.h"
 
-import (
-       "github.com/apache/skywalking-rover/pkg/core"
-       "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process"
-)
+char __license[] SEC("license") = "Dual MIT/GPL";
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
-       module.Register(process.NewModule())
-}
+SEC("perf_event")
+int do_perf_event(struct pt_regs *ctx) {
+    // create map key
+    struct key_t key = {};
+
+    // get stacks
+    key.kernel_stack_id = bpf_get_stackid(ctx, &stacks, 0);
+    key.user_stack_id = bpf_get_stackid(ctx, &stacks, BPF_F_USER_STACK);
+
+    bpf_perf_event_output(ctx, &counts, BPF_F_CURRENT_CPU, &key, sizeof(key));
+    return 0;
+}
\ No newline at end of file
diff --git a/pkg/boot/register.go b/bpf/profiling/oncpu.h
similarity index 70%
copy from pkg/boot/register.go
copy to bpf/profiling/oncpu.h
index 061cb4a..6fc467d 100644
--- a/pkg/boot/register.go
+++ b/bpf/profiling/oncpu.h
@@ -15,16 +15,18 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+struct key_t {
+    __u32 user_stack_id;
+    __u32 kernel_stack_id;
+};
 
-import (
-       "github.com/apache/skywalking-rover/pkg/core"
-       "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process"
-)
+struct {
+       __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+} counts SEC(".maps");
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
-       module.Register(process.NewModule())
-}
+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");
\ No newline at end of file
diff --git a/configs/rover_configs.yaml b/configs/rover_configs.yaml
index c462249..33f9336 100644
--- a/configs/rover_configs.yaml
+++ b/configs/rover_configs.yaml
@@ -57,4 +57,18 @@ process_discovery:
         instance_name: 
${ROVER_PROCESS_DISCOVERY_VM_FINDER_INSTANCE_NAME:{{.Rover.HostIPV4 "en0"}}}
         # The Process Name need to relate to the process entity
         # By default, the process name is the executable name of the process
-        process_name: 
${ROVER_PROCESS_DISCOVERY_VM_FINDER_PROCESS_NAME:{{.Process.ExeName}}}
\ No newline at end of file
+        process_name: 
${ROVER_PROCESS_DISCOVERY_VM_FINDER_PROCESS_NAME:{{.Process.ExeName}}}
+
+profiling:
+  # Is active the process profiling
+  active: ${ROVER_PROFILING_ACTIVE:true}
+  # Check the profiling task interval
+  check_interval: ${ROVER_PROFILING_CHECK_INTERVAL:10s}
+  # Combine existing profiling data and report to the backend interval
+  flush_interval: ${ROVER_PROFILING_FLUSH_INTERVAL:5s}
+  # Customize profiling task config
+  task:
+    # The config when executing ON_CPU profiling task
+    on_cpu:
+      # the profiling stack dump period
+      dump_period: ${ROVER_PROFILING_TASK_ON_CPU_DUMP_PERIOD:9ms}
\ No newline at end of file
diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base
new file mode 100644
index 0000000..b938223
--- /dev/null
+++ b/docker/Dockerfile.base
@@ -0,0 +1,27 @@
+# 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 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 && \
+    wget https://apt.llvm.org/llvm.sh && \
+    chmod +x llvm.sh && \
+    ./llvm.sh 13
+
+ENV PATH="${PATH}:/usr/lib/llvm-13/bin"
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 60264ef..498fb05 100644
--- a/go.mod
+++ b/go.mod
@@ -3,12 +3,15 @@ module github.com/apache/skywalking-rover
 go 1.17
 
 require (
+       github.com/cilium/ebpf v0.8.1
        github.com/google/uuid v1.3.0
        github.com/hashicorp/go-multierror v1.1.1
+       github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639
        github.com/shirou/gopsutil v3.21.11+incompatible
        github.com/sirupsen/logrus v1.8.1
        github.com/spf13/cobra v1.3.0
        github.com/spf13/viper v1.10.1
+       golang.org/x/sys v0.0.0-20211210111614-af8b64212486
        google.golang.org/grpc v1.44.0
        skywalking.apache.org/repo/goapi v0.0.0-20220302122002-ea09cd279b0d
 )
@@ -32,7 +35,6 @@ require (
        github.com/tklauser/numcpus v0.3.0 // indirect
        github.com/yusufpapurcu/wmi v1.2.2 // indirect
        golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
-       golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
        golang.org/x/text v0.3.7 // indirect
        google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // 
indirect
        google.golang.org/protobuf v1.27.1 // indirect
diff --git a/go.sum b/go.sum
index c5a14db..77a0f94 100644
--- a/go.sum
+++ b/go.sum
@@ -72,6 +72,8 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod 
h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
 github.com/chzyer/logex v1.1.10/go.mod 
h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod 
h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod 
h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
+github.com/cilium/ebpf v0.8.1/go.mod 
h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod 
h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod 
h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
 github.com/client9/misspell v0.3.4/go.mod 
h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -88,6 +90,7 @@ github.com/cncf/xds/go 
v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWH
 github.com/coreos/go-semver v0.3.0/go.mod 
h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod 
h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod 
h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/creack/pty v1.1.9/go.mod 
h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -105,6 +108,8 @@ github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod 
h1:2t7qjJNvHPx8IjnBOzl9E
 github.com/fatih/color v1.7.0/go.mod 
h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod 
h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fatih/color v1.13.0/go.mod 
h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/frankban/quicktest v1.14.0 
h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
+github.com/frankban/quicktest v1.14.0/go.mod 
h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
 github.com/fsnotify/fsnotify v1.5.1 
h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
 github.com/fsnotify/fsnotify v1.5.1/go.mod 
h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
 github.com/ghodss/yaml v1.0.0/go.mod 
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -234,6 +239,7 @@ github.com/hashicorp/serf v0.9.5/go.mod 
h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN
 github.com/hashicorp/serf v0.9.6/go.mod 
h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
 github.com/iancoleman/strcase v0.2.0/go.mod 
h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod 
h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
+github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 
h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod 
h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/inconshreveable/mousetrap v1.0.0 
h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod 
h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -250,11 +256,13 @@ github.com/konsorten/go-windows-terminal-sequences 
v1.0.1/go.mod h1:T0+1ngSBFLxv
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod 
h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod 
h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
 github.com/kr/pretty v0.2.0/go.mod 
h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod 
h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod 
h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod 
h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lyft/protoc-gen-star v0.5.3/go.mod 
h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
 github.com/magiconair/properties v1.8.5 
h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
 github.com/magiconair/properties v1.8.5/go.mod 
h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -311,6 +319,8 @@ github.com/prometheus/procfs v0.0.2/go.mod 
h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
 github.com/prometheus/procfs v0.0.8/go.mod 
h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod 
h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.3.0/go.mod 
h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.1 
h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod 
h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod 
h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sagikazarmark/crypt v0.3.0/go.mod 
h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
@@ -553,6 +563,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod 
h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/pkg/boot/register.go b/pkg/boot/register.go
index 061cb4a..10e161d 100644
--- a/pkg/boot/register.go
+++ b/pkg/boot/register.go
@@ -21,10 +21,12 @@ import (
        "github.com/apache/skywalking-rover/pkg/core"
        "github.com/apache/skywalking-rover/pkg/module"
        "github.com/apache/skywalking-rover/pkg/process"
+       "github.com/apache/skywalking-rover/pkg/profiling"
 )
 
 func init() {
        // register all active module
        module.Register(core.NewModule())
        module.Register(process.NewModule())
+       module.Register(profiling.NewModule())
 }
diff --git a/pkg/boot/register.go b/pkg/process/api.go
similarity index 74%
copy from pkg/boot/register.go
copy to pkg/process/api.go
index 061cb4a..2345d25 100644
--- a/pkg/boot/register.go
+++ b/pkg/process/api.go
@@ -15,16 +15,11 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+package process
 
-import (
-       "github.com/apache/skywalking-rover/pkg/core"
-       "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process"
-)
+import "github.com/apache/skywalking-rover/pkg/process/api"
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
-       module.Register(process.NewModule())
+type Operator interface {
+       // FindProcessById the processID is received from the backend, if not 
found then return nil
+       FindProcessByID(processID string) api.ProcessInterface
 }
diff --git a/pkg/process/api/process.go b/pkg/process/api/process.go
index 39d5d84..75cc0b5 100644
--- a/pkg/process/api/process.go
+++ b/pkg/process/api/process.go
@@ -17,6 +17,8 @@
 
 package api
 
+import "github.com/apache/skywalking-rover/pkg/tools/profiling"
+
 type ProcessDetectType int8
 
 const (
@@ -43,6 +45,8 @@ type ProcessInterface interface {
        DetectType() ProcessDetectType
        // Entity of process in backend
        Entity() *ProcessEntity
+       // ProfilingStat of process
+       ProfilingStat() *profiling.Info
 }
 
 // ProcessEntity is related to backend entity concept
diff --git a/pkg/process/finders/base/process.go 
b/pkg/process/finders/base/process.go
index 7358bd7..49246fe 100644
--- a/pkg/process/finders/base/process.go
+++ b/pkg/process/finders/base/process.go
@@ -21,6 +21,7 @@ import (
        "github.com/shirou/gopsutil/process"
 
        "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/tools/profiling"
 )
 
 // DetectedProcess from the finder
@@ -33,4 +34,6 @@ type DetectedProcess interface {
        Entity() *api.ProcessEntity
        // DetectType define the process find type
        DetectType() api.ProcessDetectType
+       // ProfilingStat of process
+       ProfilingStat() *profiling.Info
 }
diff --git a/pkg/process/finders/context.go b/pkg/process/finders/context.go
index 8a5219b..715b9d2 100644
--- a/pkg/process/finders/context.go
+++ b/pkg/process/finders/context.go
@@ -20,6 +20,7 @@ package finders
 import (
        "github.com/apache/skywalking-rover/pkg/process/api"
        "github.com/apache/skywalking-rover/pkg/process/finders/base"
+       "github.com/apache/skywalking-rover/pkg/tools/profiling"
 )
 
 type ProcessUploadStatus int8
@@ -61,3 +62,7 @@ func (p *ProcessContext) DetectType() api.ProcessDetectType {
 func (p *ProcessContext) Entity() *api.ProcessEntity {
        return p.detectProcess.Entity()
 }
+
+func (p *ProcessContext) ProfilingStat() *profiling.Info {
+       return p.detectProcess.ProfilingStat()
+}
diff --git a/pkg/process/finders/manager.go b/pkg/process/finders/manager.go
index 9c2a597..1787cf5 100644
--- a/pkg/process/finders/manager.go
+++ b/pkg/process/finders/manager.go
@@ -117,3 +117,7 @@ func (p *ProcessManagerWithFinder) GetModuleManager() 
*module.Manager {
 func (p *ProcessManagerWithFinder) SyncAllProcessInFinder(processes 
[]base.DetectedProcess) {
        p.storage.SyncAllProcessInFinder(p.finderType, processes)
 }
+
+func (m *ProcessManager) FindProcessByID(processID string) 
api.ProcessInterface {
+       return m.storage.FindProcessByID(processID)
+}
diff --git a/pkg/process/finders/storage.go b/pkg/process/finders/storage.go
index 754de08..0d2430b 100644
--- a/pkg/process/finders/storage.go
+++ b/pkg/process/finders/storage.go
@@ -201,7 +201,7 @@ func (s *ProcessStorage) SyncAllProcessInFinder(finder 
api.ProcessDetectType, pr
                // So we need to remove this process
                if needToSyncProcess == nil {
                        if managedProcess.DetectType() == finder {
-                               s.processes[pid] = nil
+                               delete(s.processes, pid)
                        }
                        continue
                }
@@ -240,8 +240,18 @@ func (s *ProcessStorage) constructNewProcessContext(finder 
api.ProcessDetectType
 func (s *ProcessStorage) updateProcessToUploadSuccess(pc *ProcessContext, id 
string) {
        pc.id = id
        pc.syncStatus = ReportSuccess
+       log.Infof("uploaded process name: %s, id: %s", 
pc.detectProcess.Entity().ProcessName, id)
 }
 
 func (s *ProcessStorage) updateProcessToUploadIgnored(pc *ProcessContext) {
        pc.syncStatus = Ignore
 }
+
+func (s *ProcessStorage) FindProcessByID(processID string) 
api.ProcessInterface {
+       for _, ps := range s.processes {
+               if ps.id == processID {
+                       return ps
+               }
+       }
+       return nil
+}
diff --git a/pkg/process/finders/vm/finder.go b/pkg/process/finders/vm/finder.go
index 1f275a8..b94ccbf 100644
--- a/pkg/process/finders/vm/finder.go
+++ b/pkg/process/finders/vm/finder.go
@@ -20,6 +20,7 @@ package vm
 import (
        "context"
        "fmt"
+       "os"
        "regexp"
        "time"
 
@@ -31,6 +32,7 @@ import (
        "github.com/apache/skywalking-rover/pkg/process/api"
        "github.com/apache/skywalking-rover/pkg/process/finders/base"
        "github.com/apache/skywalking-rover/pkg/tools"
+       "github.com/apache/skywalking-rover/pkg/tools/host"
 )
 
 var log = logger.GetLogger("process", "finder", "vm")
@@ -149,9 +151,9 @@ func (p *ProcessFinder) findAndReportProcesses() error {
 func (p *ProcessFinder) validateTheProcessesCouldProfiling(processes 
[]*Process) []*Process {
        result := make([]*Process, 0)
        for _, ps := range processes {
-               exe, err := ps.original.Exe()
-               if err != nil {
-                       log.Warnf("could not read process exe file path, pid: 
%d, reason: %v", ps.pid, err)
+               exe := p.tryToFindFileExecutePath(ps.original)
+               if exe == "" {
+                       log.Warnf("could not read process exe file path, pid: 
%d", ps.pid)
                        continue
                }
 
@@ -316,3 +318,31 @@ func regexMustNotNull(err error, confKey, confValue 
string) (*regexp.Regexp, err
        }
        return regexp.Compile(confValue)
 }
+
+func (p *ProcessFinder) tryToFindFileExecutePath(ps *process.Process) string {
+       exe, err := ps.Exe()
+       if pathExists(exe, err) {
+               return exe
+       }
+       cwd, err := ps.Cwd()
+       if pathExists(cwd, err) && pathExists(cwd+"/"+exe, err) {
+               return cwd + "/" + 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
+               }
+       }
+       return ""
+}
+
+func pathExists(exe string, err error) bool {
+       if err != nil {
+               return false
+       }
+       _, e := os.Stat(exe)
+       return e == nil
+}
diff --git a/pkg/process/finders/vm/process.go 
b/pkg/process/finders/vm/process.go
index d6566ce..e5d373f 100644
--- a/pkg/process/finders/vm/process.go
+++ b/pkg/process/finders/vm/process.go
@@ -60,6 +60,10 @@ func (p *Process) OriginalProcess() *process.Process {
        return p.original
 }
 
+func (p *Process) ProfilingStat() *profiling.Info {
+       return p.profiling
+}
+
 // BuildIdentity without pid
 func (p *Process) BuildIdentity() string {
        return fmt.Sprintf("%s_%s_%s_%s", p.entity.Layer, p.entity.ServiceName,
diff --git a/pkg/process/mdoule.go b/pkg/process/module.go
similarity index 91%
copy from pkg/process/mdoule.go
copy to pkg/process/module.go
index e7463c7..d57c3a3 100644
--- a/pkg/process/mdoule.go
+++ b/pkg/process/module.go
@@ -23,6 +23,7 @@ import (
 
        "github.com/apache/skywalking-rover/pkg/core"
        "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/apache/skywalking-rover/pkg/process/api"
        "github.com/apache/skywalking-rover/pkg/process/finders"
 )
 
@@ -72,3 +73,7 @@ func (m *Module) NotifyStartSuccess() {
 func (m *Module) Shutdown(ctx context.Context, mgr *module.Manager) error {
        return m.manager.Shutdown()
 }
+
+func (m *Module) FindProcessByID(processID string) api.ProcessInterface {
+       return m.manager.FindProcessByID(processID)
+}
diff --git a/pkg/boot/register.go b/pkg/profiling/config.go
similarity index 69%
copy from pkg/boot/register.go
copy to pkg/profiling/config.go
index 061cb4a..6bd9aa4 100644
--- a/pkg/boot/register.go
+++ b/pkg/profiling/config.go
@@ -15,16 +15,18 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+package profiling
 
 import (
-       "github.com/apache/skywalking-rover/pkg/core"
        "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process"
+       "github.com/apache/skywalking-rover/pkg/profiling/task/base"
 )
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
-       module.Register(process.NewModule())
+type Config struct {
+       module.Config `mapstructure:",squash"`
+
+       CheckInterval string `mapstructure:"check_interval"` // Check the 
profiling task interval
+       FlushInterval string `mapstructure:"flush_interval"` // Flush profiling 
data interval
+
+       TaskConfig *base.TaskConfig `mapstructure:"task"` // Profiling task 
config
 }
diff --git a/pkg/profiling/manager.go b/pkg/profiling/manager.go
new file mode 100644
index 0000000..59f36f8
--- /dev/null
+++ b/pkg/profiling/manager.go
@@ -0,0 +1,156 @@
+// 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 (
+       "context"
+       "fmt"
+       "time"
+
+       "github.com/apache/skywalking-rover/pkg/core"
+       "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/apache/skywalking-rover/pkg/process"
+       "github.com/apache/skywalking-rover/pkg/profiling/task"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/v3"
+)
+
+// Manager the profiling task, receive them from the backend side
+type Manager struct {
+       profilingClient v3.EBPFProfilingServiceClient
+       interval        time.Duration
+       taskManager     *task.Manager
+
+       ctx    context.Context
+       cancel context.CancelFunc
+
+       instanceID     string
+       lastUpdateTime int64
+}
+
+func NewManager(ctx context.Context, manager *module.Manager, conf *Config) 
(*Manager, error) {
+       coreOperator := manager.FindModule(core.ModuleName).(core.Operator)
+       connection := coreOperator.BackendOperator().GetConnection()
+       profilingClient := v3.NewEBPFProfilingServiceClient(connection)
+       instanceID := coreOperator.InstanceID()
+       duration, err := time.ParseDuration(conf.CheckInterval)
+       if err != nil {
+               return nil, fmt.Errorf("parse profling check interval failure: 
%v", err)
+       }
+
+       flushDuration, err := time.ParseDuration(conf.FlushInterval)
+       if err != nil {
+               return nil, fmt.Errorf("parse profiling data flush interval 
failure: %v", err)
+       }
+
+       taskManager, err := task.NewManager(ctx, 
manager.FindModule(process.ModuleName).(process.Operator),
+               profilingClient, flushDuration, conf.TaskConfig)
+       if err != nil {
+               return nil, err
+       }
+       ctx, cancel := context.WithCancel(ctx)
+       return &Manager{
+               profilingClient: profilingClient,
+               taskManager:     taskManager,
+               interval:        duration,
+               ctx:             ctx,
+               cancel:          cancel,
+               instanceID:      instanceID,
+               lastUpdateTime:  -1,
+       }, nil
+}
+
+func (m *Manager) Start() {
+       m.taskManager.Start()
+       go func() {
+               timeTicker := time.NewTicker(m.interval)
+               for {
+                       select {
+                       case <-timeTicker.C:
+                               if err := m.startingWatchTask(); err != nil {
+                                       log.Errorf("fetch profiling task 
failure: %v", err)
+                               }
+                       case <-m.ctx.Done():
+                               timeTicker.Stop()
+                               return
+                       }
+               }
+       }()
+}
+
+func (m *Manager) startingWatchTask() error {
+       // query task
+       tasks, err := m.profilingClient.QueryTasks(m.ctx, 
&v3.EBPFProfilingTaskQuery{
+               RoverInstanceId:  m.instanceID,
+               LatestUpdateTime: m.lastUpdateTime,
+       })
+       if err != nil {
+               return err
+       }
+       if len(tasks.Commands) == 0 {
+               return nil
+       }
+
+       // analyze profiling tasks
+       taskContexts := make([]*task.Context, 0)
+       lastUpdateTime := m.lastUpdateTime
+       for _, cmd := range tasks.Commands {
+               taskContext, err := m.taskManager.BuildContext(cmd)
+               if err != nil {
+                       log.Warnf("could not execute task, ignored. %v", err)
+                       continue
+               }
+
+               if taskContext.UpdateTime() > lastUpdateTime {
+                       lastUpdateTime = taskContext.UpdateTime()
+               }
+
+               if !taskContext.CheckTaskRunnable() {
+                       continue
+               }
+
+               taskContexts = append(taskContexts, taskContext)
+       }
+
+       // update last task time
+       m.lastUpdateTime = lastUpdateTime
+
+       if len(taskContexts) == 0 {
+               return nil
+       }
+
+       taskIDList := make([]string, len(taskContexts))
+       for inx, c := range taskContexts {
+               taskIDList[inx] = c.TaskID()
+       }
+       log.Infof("received %d profiling task: %v", len(taskContexts), 
taskIDList)
+
+       // start tasks
+       for _, t := range taskContexts {
+               m.taskManager.StartTask(t)
+       }
+       return nil
+}
+
+func (m *Manager) Shutdown() error {
+       if err := m.taskManager.Shutdown(); err != nil {
+               log.Warnf("task manager shutdown failure: %v", err)
+       }
+       m.cancel()
+       return nil
+}
diff --git a/pkg/process/mdoule.go b/pkg/profiling/module.go
similarity index 76%
rename from pkg/process/mdoule.go
rename to pkg/profiling/module.go
index e7463c7..88b149c 100644
--- a/pkg/process/mdoule.go
+++ b/pkg/profiling/module.go
@@ -15,23 +15,27 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package process
+package profiling
 
 import (
        "context"
-       "time"
+
+       "github.com/cilium/ebpf/rlimit"
 
        "github.com/apache/skywalking-rover/pkg/core"
+       "github.com/apache/skywalking-rover/pkg/logger"
        "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process/finders"
+       "github.com/apache/skywalking-rover/pkg/process"
 )
 
-const ModuleName = "process_discovery"
+const ModuleName = "profiling"
+
+var log = logger.GetLogger("profiling")
 
 type Module struct {
        config *Config
 
-       manager *finders.ProcessManager
+       manager *Manager
 }
 
 func NewModule() *Module {
@@ -43,7 +47,7 @@ func (m *Module) Name() string {
 }
 
 func (m *Module) RequiredModules() []string {
-       return []string{core.ModuleName}
+       return []string{core.ModuleName, process.ModuleName}
 }
 
 func (m *Module) Config() module.ConfigInterface {
@@ -51,21 +55,20 @@ func (m *Module) Config() module.ConfigInterface {
 }
 
 func (m *Module) Start(ctx context.Context, mgr *module.Manager) error {
-       period, err := time.ParseDuration(m.config.HeartbeatPeriod)
-       if err != nil {
+       // Allow the current process to lock memory for eBPF resources.
+       if err := rlimit.RemoveMemlock(); err != nil {
                return err
        }
-       processManager, err := finders.NewProcessManager(ctx, mgr, period, 
m.config.VM)
+
+       manager, err := NewManager(ctx, mgr, m.config)
        if err != nil {
                return err
        }
-       m.manager = processManager
-
+       m.manager = manager
        return nil
 }
 
 func (m *Module) NotifyStartSuccess() {
-       // notify all finder to report processes
        m.manager.Start()
 }
 
diff --git a/pkg/process/finders/base/process.go 
b/pkg/profiling/task/base/api.go
similarity index 53%
copy from pkg/process/finders/base/process.go
copy to pkg/profiling/task/base/api.go
index 7358bd7..b82c7fc 100644
--- a/pkg/process/finders/base/process.go
+++ b/pkg/profiling/task/base/api.go
@@ -18,19 +18,25 @@
 package base
 
 import (
-       "github.com/shirou/gopsutil/process"
+       "context"
 
        "github.com/apache/skywalking-rover/pkg/process/api"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/v3"
 )
 
-// DetectedProcess from the finder
-type DetectedProcess interface {
-       // Pid of process in host
-       Pid() int32
-       // OriginalProcess is works for query the process data
-       OriginalProcess() *process.Process
-       // Entity of process, is related with backend entity
-       Entity() *api.ProcessEntity
-       // DetectType define the process find type
-       DetectType() api.ProcessDetectType
+const MissingSymbol = "[MISSING]"
+
+type ProfilingRunningSuccessNotify func()
+
+// ProfileTaskRunner is use to running different type of profiling task, such 
as on-cpu profiling task
+type ProfileTaskRunner interface {
+       // Init runner with profiling task and process
+       Init(task *ProfilingTask, process api.ProcessInterface) error
+       // Run profiling, if throw error or method finish means the profiling 
task finished
+       Run(ctx context.Context, notify ProfilingRunningSuccessNotify) error
+       // Stop the runner initiative, is typically used to specify the 
profiling duration
+       Stop() error
+       // FlushData means dump the exists profiling data and flush them to the 
backend protocol format
+       FlushData() ([]*v3.EBPFProfilingData, error)
 }
diff --git a/pkg/boot/register.go b/pkg/profiling/task/base/config.go
similarity index 74%
copy from pkg/boot/register.go
copy to pkg/profiling/task/base/config.go
index 061cb4a..1c03158 100644
--- a/pkg/boot/register.go
+++ b/pkg/profiling/task/base/config.go
@@ -15,16 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+package base
 
-import (
-       "github.com/apache/skywalking-rover/pkg/core"
-       "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process"
-)
+type TaskConfig struct {
+       OnCPU *OnCPUConfig `mapstructure:"on_cpu"` // ON_CPU type of profiling 
task config
+}
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
-       module.Register(process.NewModule())
+type OnCPUConfig struct {
+       Period string `mapstructure:"dump_period"` // The duration of dump stack
 }
diff --git a/pkg/process/finders/base/process.go 
b/pkg/profiling/task/base/target.go
similarity index 64%
copy from pkg/process/finders/base/process.go
copy to pkg/profiling/task/base/target.go
index 7358bd7..849729b 100644
--- a/pkg/process/finders/base/process.go
+++ b/pkg/profiling/task/base/target.go
@@ -18,19 +18,27 @@
 package base
 
 import (
-       "github.com/shirou/gopsutil/process"
+       "fmt"
 
-       "github.com/apache/skywalking-rover/pkg/process/api"
+       v3 "skywalking.apache.org/repo/goapi/collect/common/v3"
 )
 
-// DetectedProcess from the finder
-type DetectedProcess interface {
-       // Pid of process in host
-       Pid() int32
-       // OriginalProcess is works for query the process data
-       OriginalProcess() *process.Process
-       // Entity of process, is related with backend entity
-       Entity() *api.ProcessEntity
-       // DetectType define the process find type
-       DetectType() api.ProcessDetectType
+type TargetType string
+
+const (
+       TargetTypeOnCPU TargetType = "ON_CPU"
+)
+
+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
 }
diff --git a/pkg/profiling/task/base/task.go b/pkg/profiling/task/base/task.go
new file mode 100644
index 0000000..cef7e9a
--- /dev/null
+++ b/pkg/profiling/task/base/task.go
@@ -0,0 +1,100 @@
+// 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 (
+       "fmt"
+       "strconv"
+       "time"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/common/v3"
+)
+
+type ProfilingTask struct {
+       // TaskID of profiling task
+       TaskID string
+       // ProcessID of need to monitoring process
+       ProcessID string
+       // UpdateTime of profiling task
+       UpdateTime int64
+       // StartTime of profiling task, when need to start to profiling
+       StartTime int64
+       // TriggerType of task
+       TriggerType TriggerType
+       // TargetType of task
+       TargetType TargetType
+       // MaxRunningDuration of task
+       MaxRunningDuration time.Duration
+}
+
+func ProfilingTaskFromCommand(command *v3.Command) (*ProfilingTask, error) {
+       if command.GetCommand() != "EBPFProfilingTaskQuery" {
+               return nil, fmt.Errorf("not support command: %s", 
command.GetCommand())
+       }
+
+       var err error
+       taskID, err := getCommandStringValue(err, command, "TaskId")
+       processID, err := getCommandStringValue(err, command, "ProcessId")
+       taskUpdateTime, err := getCommandIntValue(err, command, 
"TaskUpdateTime")
+       triggerTypeStr, err := getCommandStringValue(err, command, 
"TriggerType")
+       triggerType, err := ParseTriggerType(err, triggerTypeStr)
+       targetTypeStr, err := getCommandStringValue(err, command, "TargetType")
+       targetType, err := ParseTargetType(err, targetTypeStr)
+       taskStartTime, err := getCommandIntValue(err, command, "TaskStartTime")
+       if err != nil {
+               return nil, err
+       }
+
+       task := &ProfilingTask{
+               TaskID:      taskID,
+               ProcessID:   processID,
+               UpdateTime:  taskUpdateTime,
+               StartTime:   taskStartTime,
+               TargetType:  targetType,
+               TriggerType: triggerType,
+       }
+
+       if err := task.TriggerType.InitTask(task, command); err != nil {
+               return nil, err
+       }
+       if err := task.TargetType.InitTask(task, command); err != nil {
+               return nil, err
+       }
+
+       return task, nil
+}
+
+func getCommandStringValue(err error, command *v3.Command, key string) 
(string, error) {
+       if err != nil {
+               return "", err
+       }
+       for _, arg := range command.GetArgs() {
+               if arg.GetKey() == key {
+                       return arg.GetValue(), nil
+               }
+       }
+       return "", fmt.Errorf("could not found key: %v", key)
+}
+
+func getCommandIntValue(err error, command *v3.Command, key string) (int64, 
error) {
+       val, err := getCommandStringValue(err, command, key)
+       if err != nil {
+               return 0, err
+       }
+       return strconv.ParseInt(val, 10, 64)
+}
diff --git a/pkg/process/finders/base/process.go 
b/pkg/profiling/task/base/trigger.go
similarity index 54%
copy from pkg/process/finders/base/process.go
copy to pkg/profiling/task/base/trigger.go
index 7358bd7..978e3c0 100644
--- a/pkg/process/finders/base/process.go
+++ b/pkg/profiling/task/base/trigger.go
@@ -18,19 +18,35 @@
 package base
 
 import (
-       "github.com/shirou/gopsutil/process"
+       "fmt"
+       "time"
 
-       "github.com/apache/skywalking-rover/pkg/process/api"
+       v3 "skywalking.apache.org/repo/goapi/collect/common/v3"
 )
 
-// DetectedProcess from the finder
-type DetectedProcess interface {
-       // Pid of process in host
-       Pid() int32
-       // OriginalProcess is works for query the process data
-       OriginalProcess() *process.Process
-       // Entity of process, is related with backend entity
-       Entity() *api.ProcessEntity
-       // DetectType define the process find type
-       DetectType() api.ProcessDetectType
+type TriggerType string
+
+const (
+       TriggerTypeFixedTime TriggerType = "FIXED_TIME"
+)
+
+func ParseTriggerType(err error, val string) (TriggerType, error) {
+       if err != nil {
+               return "", err
+       }
+       if TriggerType(val) == TriggerTypeFixedTime {
+               return TriggerTypeFixedTime, nil
+       }
+       return "", fmt.Errorf("could not found trigger type: %s", val)
+}
+
+func (t TriggerType) InitTask(task *ProfilingTask, command *v3.Command) error {
+       if t == TriggerTypeFixedTime {
+               val, err := getCommandIntValue(nil, command, 
"FixedTriggerDuration")
+               if err != nil {
+                       return err
+               }
+               task.MaxRunningDuration = time.Duration(val) * time.Second
+       }
+       return nil
 }
diff --git a/pkg/profiling/task/context.go b/pkg/profiling/task/context.go
new file mode 100644
index 0000000..de64805
--- /dev/null
+++ b/pkg/profiling/task/context.go
@@ -0,0 +1,80 @@
+// 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 task
+
+import (
+       "context"
+       "fmt"
+       "sync"
+       "time"
+
+       "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/profiling/task/base"
+)
+
+type RunningStatus uint8
+
+const (
+       _ RunningStatus = iota
+       NotRunning
+       Running
+       Stopped
+)
+
+// Context of profiling task
+type Context struct {
+       task             *base.ProfilingTask
+       process          api.ProcessInterface
+       runner           base.ProfileTaskRunner
+       status           RunningStatus
+       startRunningTime time.Time
+       runningWg        *sync.WaitGroup
+       ctx              context.Context
+       cancel           context.CancelFunc
+}
+
+// UpdateTime of the profiling task
+func (c *Context) UpdateTime() int64 {
+       return c.task.UpdateTime
+}
+
+func (c *Context) TaskID() string {
+       return c.task.TaskID
+}
+
+// BuildTaskIdentity for filter with same identity task
+func (c *Context) BuildTaskIdentity() string {
+       // use process id, target type, trigger type
+       return fmt.Sprintf("%s_%s_%s", c.task.ProcessID, c.task.TargetType, 
c.task.TriggerType)
+}
+
+// CheckTaskRunnable means checks the task could be running
+func (c *Context) CheckTaskRunnable() bool {
+       // if running with FIXED_TIME type task, check the executing time range
+       if c.task.TriggerType == base.TriggerTypeFixedTime {
+               startTime := c.task.StartTime
+               endTime := 
time.UnixMilli(startTime).Add(c.task.MaxRunningDuration).UnixMilli()
+               now := time.Now().UnixMilli()
+
+               if now > endTime {
+                       log.Infof("out of task executing time range. task id: 
%s", c.task.TaskID)
+                       return false
+               }
+       }
+       return true
+}
diff --git a/pkg/profiling/task/manager.go b/pkg/profiling/task/manager.go
new file mode 100644
index 0000000..8057870
--- /dev/null
+++ b/pkg/profiling/task/manager.go
@@ -0,0 +1,255 @@
+// 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 task
+
+import (
+       "context"
+       "fmt"
+       "sync"
+       "time"
+
+       "github.com/apache/skywalking-rover/pkg/logger"
+       "github.com/apache/skywalking-rover/pkg/process"
+       "github.com/apache/skywalking-rover/pkg/profiling/task/base"
+
+       common_v3 "skywalking.apache.org/repo/goapi/collect/common/v3"
+       profiling_v3 
"skywalking.apache.org/repo/goapi/collect/ebpf/profiling/v3"
+)
+
+var log = logger.GetLogger("profiling", "task")
+
+type Manager struct {
+       processOperator process.Operator
+       profilingClient profiling_v3.EBPFProfilingServiceClient
+       flushInterval   time.Duration
+       ctx             context.Context
+       cancel          context.CancelFunc
+       taskConfig      *base.TaskConfig
+
+       tasks map[string]*Context
+}
+
+func NewManager(ctx context.Context, processOperator process.Operator,
+       profilingClient profiling_v3.EBPFProfilingServiceClient, flushInterval 
time.Duration, taskConfig *base.TaskConfig) (*Manager, error) {
+       if err := CheckProfilingTaskConfig(taskConfig); err != nil {
+               return nil, err
+       }
+
+       ctx, cancel := context.WithCancel(ctx)
+       manager := &Manager{
+               processOperator: processOperator,
+               profilingClient: profilingClient,
+               taskConfig:      taskConfig,
+               tasks:           make(map[string]*Context),
+               flushInterval:   flushInterval,
+               ctx:             ctx,
+               cancel:          cancel,
+       }
+       return manager, nil
+}
+
+func (m *Manager) Start() {
+       go m.startFlushProfilingData()
+}
+
+func (m *Manager) BuildContext(command *common_v3.Command) (*Context, error) {
+       // analyze command
+       t, err := base.ProfilingTaskFromCommand(command)
+       if err != nil || t == nil {
+               return nil, fmt.Errorf("parsing profiling task failure, 
command: %v, reason: %v", command.GetArgs(), err)
+       }
+
+       // find process
+       taskProcess := m.processOperator.FindProcessByID(t.ProcessID)
+       if taskProcess == nil {
+               return nil, fmt.Errorf("could not found %s process %s", 
t.TaskID, t.ProcessID)
+       }
+
+       // init runner
+       var r base.ProfileTaskRunner
+       if runner, err := NewProfilingRunner(t.TargetType, m.taskConfig); err 
!= nil {
+               return nil, err
+       } else if err := runner.Init(t, taskProcess); err != nil {
+               return nil, fmt.Errorf("could not init %s runner for task: %s: 
%v", t.TriggerType, t.TaskID, err)
+       } else {
+               r = runner
+       }
+
+       ctx, cancel := context.WithCancel(m.ctx)
+       return &Context{task: t, process: taskProcess, runner: r, status: 
NotRunning, ctx: ctx, cancel: cancel}, nil
+}
+
+func (m *Manager) StartTask(c *Context) {
+       // shutdown task if exists
+       taskIdentity := c.BuildTaskIdentity()
+       if m.tasks[taskIdentity] != nil {
+               if err := m.shutdownAndRemoveTask(m.tasks[taskIdentity]); err 
!= nil {
+                       log.Warnf("shutdown existing profiling task failure, so 
cannot to start new profiling task: %v. reason: %v", c.task.TaskID, err)
+                       return
+               }
+       }
+
+       currentMilli := time.Now().UnixNano() / int64(time.Millisecond)
+       m.tasks[taskIdentity] = c
+
+       // already reach time
+       if currentMilli >= c.task.StartTime {
+               m.runTask(c)
+               return
+       }
+
+       // schedule to execute
+       afterRun := time.Since(time.UnixMilli(c.task.StartTime))
+       go func() {
+               select {
+               case <-time.After(afterRun):
+                       m.runTask(c)
+               case <-c.ctx.Done():
+                       return
+               }
+       }()
+}
+
+func (m *Manager) runTask(c *Context) {
+       var wg sync.WaitGroup
+       wg.Add(1)
+       c.runningWg = &wg
+       go func() {
+               defer func() {
+                       wg.Done()
+                       c.status = Stopped
+               }()
+               c.status = Running
+               c.startRunningTime = time.Now()
+
+               notify := func() {
+                       m.afterProfilingStartSuccess(c)
+               }
+               // start running
+               if err := c.runner.Run(m.ctx, notify); err != nil {
+                       log.Warnf("executing profiling task failure, taskId: 
%s, reason: %v", c.task.TaskID, err)
+               }
+       }()
+}
+
+func (m *Manager) afterProfilingStartSuccess(c *Context) {
+       log.Infof("starting the profiling task. taskId: %s, pid: %d", 
c.task.TaskID, c.process.Pid())
+       go func() {
+               select {
+               // shutdown task when arrived task running task
+               case <-time.After(c.task.MaxRunningDuration):
+                       log.Infof("arrived task running time, shutting down 
task: %s", c.task.TaskID)
+                       if err := m.shutdownTask(c); err != nil {
+                               log.Warnf("shutting down task failure: %s, 
reason: %v", c.task.TaskID, err)
+                       }
+               // shutdown when context finished
+               case <-c.ctx.Done():
+                       if err := m.shutdownTask(c); err != nil {
+                               log.Warnf("shutting down task failure: %s, 
reason: %v", c.task.TaskID, err)
+                       }
+               }
+       }()
+}
+
+func (m *Manager) shutdownTask(c *Context) error {
+       // return if not running
+       if c.runningWg == nil {
+               return nil
+       }
+       err := c.runner.Stop()
+       c.runningWg.Wait()
+       c.cancel()
+       return err
+}
+
+func (m *Manager) shutdownAndRemoveTask(c *Context) error {
+       err := m.shutdownTask(c)
+       delete(m.tasks, c.BuildTaskIdentity())
+       return err
+}
+
+func (m *Manager) Shutdown() error {
+       m.cancel()
+       return nil
+}
+
+func (m *Manager) checkStoppedTaskAndRemoved() {
+       for identity, t := range m.tasks {
+               if t.status == Stopped {
+                       delete(m.tasks, identity)
+               }
+       }
+}
+
+func (m *Manager) startFlushProfilingData() {
+       timeTicker := time.NewTicker(m.flushInterval)
+       for {
+               select {
+               case <-timeTicker.C:
+                       if err := m.flushProfilingData(); err != nil {
+                               log.Warnf("flush profiling data failure: %v", 
err)
+                       }
+                       // cleanup the stopped after flush profiling data to 
make sure all the profiling data been sent
+                       m.checkStoppedTaskAndRemoved()
+               case <-m.ctx.Done():
+                       timeTicker.Stop()
+                       return
+               }
+       }
+}
+
+func (m *Manager) flushProfilingData() error {
+       if len(m.tasks) == 0 {
+               return nil
+       }
+
+       stream, err := m.profilingClient.CollectProfilingData(m.ctx)
+       if err != nil {
+               return err
+       }
+       currentMilli := time.Now().UnixMilli()
+       for _, t := range m.tasks {
+               data, err1 := t.runner.FlushData()
+               if err1 != nil {
+                       log.Warnf("reading profiling task data failure. taskId: 
%s, error: %v", t.task.TaskID, err1)
+                       continue
+               }
+
+               if len(data) == 0 {
+                       continue
+               }
+
+               // only the first data have task metadata
+               data[0].Task = &profiling_v3.EBPFProfilingTaskMetadata{
+                       TaskId:             t.task.TaskID,
+                       ProcessId:          t.task.ProcessID,
+                       ProfilingStartTime: t.startRunningTime.UnixMilli(),
+                       CurrentTime:        currentMilli,
+               }
+
+               for _, d := range data {
+                       // send each data, stop flush data if the stream have 
found error
+                       if err1 := stream.Send(d); err1 != nil {
+                               return err1
+                       }
+               }
+       }
+
+       _, err = stream.CloseAndRecv()
+       return err
+}
diff --git a/pkg/profiling/task/oncpu/runner.go 
b/pkg/profiling/task/oncpu/runner.go
new file mode 100644
index 0000000..ba7b893
--- /dev/null
+++ b/pkg/profiling/task/oncpu/runner.go
@@ -0,0 +1,311 @@
+// 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 oncpu
+
+import (
+       "bytes"
+       "context"
+       "encoding/binary"
+       "errors"
+       "fmt"
+       "math"
+       "os"
+       "runtime"
+       "sync"
+       "time"
+
+       "github.com/cilium/ebpf"
+       "github.com/cilium/ebpf/perf"
+
+       "github.com/hashicorp/go-multierror"
+
+       "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"
+
+       "golang.org/x/sys/unix"
+
+       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/oncpu.c -- -I$REPO_ROOT/bpf/include
+
+var log = logger.GetLogger("profiling", "task", "oncpu")
+
+type Event struct {
+       UserStackID   uint32
+       KernelStackID uint32
+}
+
+type Runner struct {
+       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    chan bool
+}
+
+func NewRunner(config *base.TaskConfig) (base.ProfileTaskRunner, error) {
+       if config.OnCPU.Period == "" {
+               return nil, fmt.Errorf("please provide the ON_CPU dump period")
+       }
+       dumpPeriod, err := time.ParseDuration(config.OnCPU.Period)
+       if err != nil {
+               return nil, fmt.Errorf("the ON_CPU dump period format not 
right, current value: %s", config.OnCPU.Period)
+       }
+       if dumpPeriod < time.Millisecond {
+               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),
+       }, nil
+}
+
+func (r *Runner) Init(task *base.ProfilingTask, process api.ProcessInterface) 
error {
+       r.pid = process.Pid()
+       // process profiling stat
+       if r.processProfiling = process.ProfilingStat(); r.processProfiling == 
nil {
+               return fmt.Errorf("this process could not be profiling")
+       }
+       // kernel profiling stat
+       kernelProfiling, err := tools.KernelFileProfilingStat()
+       if err != nil {
+               log.Warnf("could not analyze kernel profiling stats: %v", err)
+       }
+       r.kernelProfiling = kernelProfiling
+       r.stackCounter = make(map[Event]int)
+       return nil
+}
+
+func (r *Runner) Run(ctx context.Context, notify 
base.ProfilingRunningSuccessNotify) error {
+       // load bpf
+       objs := bpfObjects{}
+       if err := loadBpfObjects(&objs, nil); err != nil {
+               return err
+       }
+       defer objs.Close()
+       r.stackMap = objs.Stacks
+
+       // init profiling data reader
+       rd, err := perf.NewReader(objs.Counts, os.Getpagesize())
+       if err != nil {
+               return fmt.Errorf("creating perf event reader: %s", err)
+       }
+       r.countReader = rd
+
+       // opened perf events
+       perfEvents, err := r.openPerfEvent(objs.DoPerfEvent.FD())
+       r.perfEventFds = perfEvents
+       if err != nil {
+               return err
+       }
+
+       // notify start success
+       notify()
+       runtime.SetFinalizer(r, (*Runner).Stop)
+
+       // read content
+       var event Event
+       for {
+               record, err := rd.Read()
+               if err != nil {
+                       if errors.Is(err, perf.ErrClosed) {
+                               return nil
+                       }
+                       log.Warnf("reading from perf event reader: %s", err)
+                       continue
+               }
+
+               if record.LostSamples != 0 {
+                       log.Warnf("perf event ring buffer full, dropped %d 
samples", record.LostSamples)
+                       continue
+               }
+
+               // parse perf event data
+               if err := binary.Read(bytes.NewBuffer(record.RawSample), 
binary.LittleEndian, &event); err != nil {
+                       log.Errorf("parsing perf event error: %s", err)
+                       continue
+               }
+
+               r.stackCounter[event]++
+       }
+}
+
+func (r *Runner) openPerfEvent(perfFd int) ([]int, error) {
+       eventAttr := &unix.PerfEventAttr{
+               Type:        unix.PERF_TYPE_SOFTWARE,
+               Config:      unix.PERF_COUNT_SW_CPU_CLOCK,
+               Sample_type: unix.PERF_SAMPLE_RAW,
+               Sample:      uint64(r.dumpPeriod.Nanoseconds()),
+               Wakeup:      1,
+       }
+
+       fds := make([]int, 0)
+       for cpuNum := 0; cpuNum < runtime.NumCPU(); cpuNum++ {
+               fd, err := unix.PerfEventOpen(
+                       eventAttr,
+                       int(r.pid),
+                       cpuNum,
+                       -1,
+                       0,
+               )
+               if err != nil {
+                       return fds, err
+               }
+
+               // attach ebpf to perf event
+               if err := unix.IoctlSetInt(fd, unix.PERF_EVENT_IOC_SET_BPF, 
perfFd); err != nil {
+                       return fds, err
+               }
+
+               // enable perf event
+               if err := unix.IoctlSetInt(fd, unix.PERF_EVENT_IOC_ENABLE, 0); 
err != nil {
+                       return fds, err
+               }
+               fds = append(fds, fd)
+       }
+
+       return fds, nil
+}
+
+func (r *Runner) Stop() error {
+       var result error
+       r.shutdownOnce.Do(func() {
+               for _, fd := range r.perfEventFds {
+                       if err := r.closePerfEvent(fd); err != nil {
+                               result = multierror.Append(result, err)
+                       }
+               }
+
+               // wait for all profiling data been consume finished
+               var flushDataNotify = make(chan bool)
+               r.flushDataNotify = flushDataNotify
+               select {
+               case <-flushDataNotify:
+               case <-time.After(5 * time.Second):
+               }
+
+               if r.countReader != nil {
+                       if err := r.countReader.Close(); err != nil {
+                               result = multierror.Append(result, err)
+                       }
+               }
+       })
+       return result
+}
+
+func (r *Runner) FlushData() ([]*v3.EBPFProfilingData, error) {
+       existsCounters := r.flushStackCounter()
+
+       result := make([]*v3.EBPFProfilingData, 0)
+       stackSymbols := make([]uint64, 100)
+       for event, count := range existsCounters {
+               metadatas := make([]*v3.EBPFProfilingStackMetadata, 0)
+               // kernel stack
+               if d := r.generateProfilingData(r.kernelProfiling, 
event.KernelStackID,
+                       v3.EBPFProfilingStackType_PROCESS_KERNEL_SPACE, 
stackSymbols); d != nil {
+                       metadatas = append(metadatas, d)
+               }
+
+               // user stack
+               if d := r.generateProfilingData(r.processProfiling, 
event.UserStackID,
+                       v3.EBPFProfilingStackType_PROCESS_USER_SPACE, 
stackSymbols); d != nil {
+                       metadatas = append(metadatas, d)
+               }
+
+               // close the flush data notify if exists
+               if r.flushDataNotify != nil {
+                       r.flushDataNotify <- true
+               }
+
+               if len(metadatas) == 0 {
+                       continue
+               }
+
+               result = append(result, &v3.EBPFProfilingData{
+                       Profiling: &v3.EBPFProfilingData_OnCPU{
+                               OnCPU: &v3.EBPFOnCPUProfiling{
+                                       Stacks:    metadatas,
+                                       DumpCount: int32(count),
+                               },
+                       },
+               })
+       }
+
+       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
+
+       older := &r.stackCounter
+       *older, *updateToP = *updateToP, *older
+       return updateTo
+}
+
+func (r *Runner) closePerfEvent(fd int) error {
+       if fd <= 0 {
+               return nil
+       }
+       var result error
+       if err := unix.IoctlSetInt(fd, unix.PERF_EVENT_IOC_DISABLE, 0); err != 
nil {
+               result = multierror.Append(result, fmt.Errorf("closing perf 
event reader: %s", err))
+       }
+       return result
+}
diff --git a/pkg/profiling/task/registion.go b/pkg/profiling/task/registion.go
new file mode 100644
index 0000000..2e9271b
--- /dev/null
+++ b/pkg/profiling/task/registion.go
@@ -0,0 +1,54 @@
+// 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 task
+
+import (
+       "fmt"
+
+       "github.com/hashicorp/go-multierror"
+
+       "github.com/apache/skywalking-rover/pkg/profiling/task/base"
+       "github.com/apache/skywalking-rover/pkg/profiling/task/oncpu"
+)
+
+var profilingRunners = make(map[base.TargetType]func(config *base.TaskConfig) 
(base.ProfileTaskRunner, error))
+
+func init() {
+       profilingRunners[base.TargetTypeOnCPU] = oncpu.NewRunner
+}
+
+func NewProfilingRunner(taskType base.TargetType, taskConfig *base.TaskConfig) 
(base.ProfileTaskRunner, error) {
+       if profilingRunners[taskType] == nil {
+               return nil, fmt.Errorf("could not found %s runner", taskType)
+       }
+       return profilingRunners[taskType](taskConfig)
+}
+
+func CheckProfilingTaskConfig(taskConfig *base.TaskConfig) error {
+       if taskConfig == nil {
+               return fmt.Errorf("please provide the profiling task config")
+       }
+
+       var err error
+       for _, runner := range profilingRunners {
+               if _, e := runner(taskConfig); e != nil {
+                       err = multierror.Append(err, e)
+               }
+       }
+       return err
+}
diff --git a/pkg/boot/register.go b/pkg/tools/host/file.go
similarity index 69%
copy from pkg/boot/register.go
copy to pkg/tools/host/file.go
index 061cb4a..4d26ef1 100644
--- a/pkg/boot/register.go
+++ b/pkg/tools/host/file.go
@@ -15,16 +15,19 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+package host
 
 import (
-       "github.com/apache/skywalking-rover/pkg/core"
-       "github.com/apache/skywalking-rover/pkg/module"
-       "github.com/apache/skywalking-rover/pkg/process"
+       "os"
+       "strings"
 )
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
-       module.Register(process.NewModule())
+var hostMappingPath = os.Getenv("ROVER_HOST_MAPPING")
+
+// GetFileInHost means add the host root mapping prefix, it's dependent when 
the rover is deploy in a container
+func GetFileInHost(absPath string) string {
+       if hostMappingPath != "" && strings.HasPrefix(absPath, hostMappingPath) 
{
+               return absPath
+       }
+       return hostMappingPath + absPath
 }
diff --git a/pkg/tools/profiling/api.go b/pkg/tools/profiling/api.go
index 512fe6d..6256e08 100644
--- a/pkg/tools/profiling/api.go
+++ b/pkg/tools/profiling/api.go
@@ -17,11 +17,14 @@
 
 package profiling
 
+import "github.com/ianlancetaylor/demangle"
+
 var KernelSymbolFilePath = "/proc/kallsyms"
 
 // Info of profiling process
 type Info struct {
-       Symbols []*Symbol
+       Symbols           []*Symbol
+       cacheAddrToSymbol map[uint64]string
 }
 
 // Symbol of executable file
@@ -37,12 +40,38 @@ type StatFinder interface {
        Analyze(filePath string) (*Info, error)
 }
 
+func newInfo(symbols []*Symbol) *Info {
+       return &Info{Symbols: symbols, cacheAddrToSymbol: 
make(map[uint64]string)}
+}
+
+// FindSymbols from address list, if could not found symbol name then append 
default symbol to array
+func (i *Info) FindSymbols(addresses []uint64, defaultSymbol string) []string {
+       if len(addresses) == 0 {
+               return nil
+       }
+       result := make([]string, 0)
+       for _, addr := range addresses {
+               if addr <= 0 {
+                       continue
+               }
+               s := i.FindSymbolName(addr)
+               if s == "" {
+                       s = defaultSymbol
+               }
+               result = append(result, s)
+       }
+       return result
+}
+
 // FindSymbolName by address
 func (i *Info) FindSymbolName(address uint64) string {
+       if d := i.cacheAddrToSymbol[address]; d != "" {
+               return d
+       }
        symbols := i.Symbols
 
        start := 0
-       end := len(symbols)
+       end := len(symbols) - 1
        for start < end {
                mid := start + (end-start)/2
                result := int64(address) - int64(symbols[mid].Location)
@@ -52,13 +81,26 @@ func (i *Info) FindSymbolName(address uint64) string {
                } else if result > 0 {
                        start = mid + 1
                } else {
-                       return symbols[mid].Name
+                       s := processSymbolName(symbols[mid].Name)
+                       i.cacheAddrToSymbol[address] = s
+                       return s
                }
        }
 
        if start >= 1 && symbols[start-1].Location < address && address < 
symbols[start].Location {
-               return symbols[start-1].Name
+               s := processSymbolName(symbols[start-1].Name)
+               i.cacheAddrToSymbol[address] = s
+               return s
        }
 
        return ""
 }
+
+func processSymbolName(name string) string {
+       // fix process demangle symbol name, such as c++ language symbol
+       skip := 0
+       if name[0] == '.' || name[0] == '$' {
+               skip++
+       }
+       return demangle.Filter(name[skip:])
+}
diff --git a/pkg/tools/profiling/go_library.go 
b/pkg/tools/profiling/go_library.go
index c5cd3d2..17dba26 100644
--- a/pkg/tools/profiling/go_library.go
+++ b/pkg/tools/profiling/go_library.go
@@ -54,5 +54,5 @@ func (l *GoLibrary) Analyze(filePath string) (*Info, error) {
                data[i] = &Symbol{Name: sym.Name, Location: sym.Value}
        }
 
-       return &Info{Symbols: data}, nil
+       return newInfo(data), nil
 }
diff --git a/pkg/tools/profiling/kernel.go b/pkg/tools/profiling/kernel.go
index 3285463..ebf7d4e 100644
--- a/pkg/tools/profiling/kernel.go
+++ b/pkg/tools/profiling/kernel.go
@@ -23,6 +23,8 @@ import (
        "os"
        "strconv"
        "strings"
+
+       "github.com/apache/skywalking-rover/pkg/tools/host"
 )
 
 type KernelFinder struct {
@@ -30,7 +32,7 @@ type KernelFinder struct {
 }
 
 func NewKernelFinder() *KernelFinder {
-       stat, _ := os.Stat(KernelSymbolFilePath)
+       stat, _ := os.Stat(host.GetFileInHost(KernelSymbolFilePath))
        return &KernelFinder{kernelFileExists: stat != nil}
 }
 
@@ -63,5 +65,5 @@ func (k *KernelFinder) Analyze(filepath string) (*Info, 
error) {
                })
        }
 
-       return &Info{Symbols: symbols}, nil
+       return newInfo(symbols), nil
 }
diff --git a/pkg/tools/profiling/objdump.go b/pkg/tools/profiling/objdump.go
index 4783760..a04bfd7 100644
--- a/pkg/tools/profiling/objdump.go
+++ b/pkg/tools/profiling/objdump.go
@@ -62,5 +62,5 @@ func (o *ObjDump) Analyze(filepath string) (*Info, error) {
                }
                symbols = append(symbols, &Symbol{Name: submatch[2], Location: 
atoi})
        }
-       return &Info{Symbols: symbols}, nil
+       return newInfo(symbols), nil
 }
diff --git a/scripts/build/base.mk b/scripts/build/base.mk
new file mode 100644
index 0000000..ac87445
--- /dev/null
+++ b/scripts/build/base.mk
@@ -0,0 +1,55 @@
+# 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.
+#
+
+HUB ?= apache
+VERSION ?= latest
+
+SHELL = /bin/bash
+
+REPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))/
+
+OSNAME := $(if $(findstring Darwin,$(shell uname)),darwin,linux)
+
+SH = sh
+GO = go
+GIT = git
+GO_PATH = $$($(GO) env GOPATH)
+GO_BUILD = $(GO) build
+GO_GET = $(GO) get
+
+CONTAINER_COMMAND_IMAGE ?= $(HUB)/skywalking-rover-base
+CONTAINER_COMMAND_TAG ?= v$(VERSION)
+CONTAINER_COMMAND_CLANG ?= clang
+CONTAINER_COMMAND_STRIP ?= llvm-strip
+CONTAINER_COMMAND_CFLAGS := -O2 -g -Wall -Werror $(CFLAGS)
+CONTAINER_COMMAND_ENGINE ?= docker
+
+.PHONY: clean
+clean:
+       -rm -rf coverage.txt
+
+build-base-container:
+       ${CONTAINER_COMMAND_ENGINE} build -t 
${CONTAINER_COMMAND_IMAGE}:${CONTAINER_COMMAND_TAG} . -f docker/Dockerfile.base
+
+container-command: build-base-container
+       ${CONTAINER_COMMAND_ENGINE} run --rm \
+               -v "${REPODIR}":/skywalking-rover -w /skywalking-rover --env 
MAKEFLAGS \
+               --env CFLAGS="-fdebug-prefix-map=/skywalking-rover=." \
+               --env HOME="/skywalking-rover" \
+               "${CONTAINER_COMMAND_IMAGE}:${CONTAINER_COMMAND_TAG}" \
+               make ${COMMAND}
\ No newline at end of file
diff --git a/scripts/build/build.mk b/scripts/build/build.mk
new file mode 100644
index 0000000..edad491
--- /dev/null
+++ b/scripts/build/build.mk
@@ -0,0 +1,38 @@
+# 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.
+#
+
+BINARY = skywalking-rover
+
+OUT_DIR = bin
+GO_BUILD_FLAGS = -v
+GO_BUILD_LDFLAGS = -X main.version=$(VERSION)
+
+PLATFORMS := linux
+ARCH = amd64
+os = $(word 1, $@)
+
+deps:
+       $(GO_GET) -v -t -d ./...
+
+.PHONY: $(PLATFORMS)
+$(PLATFORMS): deps
+       mkdir -p $(OUT_DIR)
+       GOOS=$(os) GOARCH=$(ARCH) $(GO_BUILD) $(GO_BUILD_FLAGS) -ldflags 
"$(GO_BUILD_LDFLAGS)" -o $(OUT_DIR)/$(BINARY)-$(VERSION)-$(os)-$(ARCH) ./cmd
+
+.PHONY: build
+build: linux
\ No newline at end of file
diff --git a/scripts/build/check.mk b/scripts/build/check.mk
new file mode 100644
index 0000000..54a0b32
--- /dev/null
+++ b/scripts/build/check.mk
@@ -0,0 +1,27 @@
+# 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.
+#
+
+.PHONY: check
+check: clean
+       $(GO) mod tidy > /dev/null
+       @if [ ! -z "`git status -s`" ]; then \
+               echo "Following files are not consistent with CI:"; \
+               git status -s; \
+               git diff; \
+               exit 1; \
+       fi
\ No newline at end of file
diff --git a/scripts/build/generate.mk b/scripts/build/generate.mk
new file mode 100644
index 0000000..1a46c4a
--- /dev/null
+++ b/scripts/build/generate.mk
@@ -0,0 +1,29 @@
+# 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.
+#
+
+.PHONY: generate
+generate: export BPF_CLANG := $(CONTAINER_COMMAND_CLANG)
+generate: export BPF_CFLAGS := $(CONTAINER_COMMAND_CFLAGS)
+generate: export REPO_ROOT := $(REPODIR)
+generate:
+       cd ./ && go generate ./...
+
+# Usually works for generate ebpf ELF file on Mac OS or windows
+.PHONY: container-generate
+container-generate: COMMAND=generate
+container-generate: container-command
\ No newline at end of file
diff --git a/scripts/build/lint.mk b/scripts/build/lint.mk
new file mode 100644
index 0000000..12f2324
--- /dev/null
+++ b/scripts/build/lint.mk
@@ -0,0 +1,30 @@
+# 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_LINT = $(GO_PATH)/bin/golangci-lint
+
+linter:
+       $(GO_LINT) version || curl -sfL 
https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh 
-s -- -b $(GO_PATH)/bin v1.39.0
+
+.PHONY: lint
+lint: linter generate
+       $(GO_LINT) run -v --timeout 5m ./...
+
+.PHONY: container-lint
+container-lint: COMMAND=lint
+container-lint: container-command
\ No newline at end of file
diff --git a/scripts/build/test.mk b/scripts/build/test.mk
new file mode 100644
index 0000000..a4bcbad
--- /dev/null
+++ b/scripts/build/test.mk
@@ -0,0 +1,31 @@
+# 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_TEST = $(GO) test
+GO_TEST_LDFLAGS =
+
+.PHONY: test
+test: clean
+       $(GO_TEST) -ldflags "$(GO_TEST_LDFLAGS)" ./... 
-coverprofile=coverage.txt -covermode=atomic
+
+.PHONY: test
+test: clean generate
+
+.PHONY: container-test
+container-test: COMMAND=test
+container-test: container-command
\ No newline at end of file

Reply via email to