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 55f11d3  Support detect VM level process (#5)
55f11d3 is described below

commit 55f11d36000dd85d4747f6a7f26661a350d9d11f
Author: mrproliu <[email protected]>
AuthorDate: Tue Mar 1 15:19:18 2022 +0800

    Support detect VM level process (#5)
---
 .golangci.yml                                      |   7 +
 configs/rover_configs.yaml                         |  27 +-
 go.mod                                             |   7 +
 go.sum                                             |  17 ++
 pkg/boot/module.go                                 |   5 +
 pkg/boot/module_test.go                            |  39 ++-
 pkg/boot/register.go                               |   2 +
 pkg/config/env_override.go                         |   4 +
 pkg/core/api.go                                    |   2 +
 pkg/core/module.go                                 |  18 +-
 pkg/module/manager_test.go                         |   3 +
 pkg/module/module.go                               |   3 +
 pkg/process/api/process.go                         |  62 ++++
 pkg/{boot/register.go => process/config.go}        |  19 +-
 .../register.go => process/finders/base/config.go} |  13 +-
 pkg/process/finders/base/finder.go                 |  58 ++++
 .../finders/base/process.go}                       |  20 +-
 pkg/process/finders/base/template.go               | 123 ++++++++
 pkg/process/finders/context.go                     |  63 ++++
 pkg/process/finders/manager.go                     | 119 ++++++++
 pkg/{boot => process/finders}/register.go          |  21 +-
 pkg/process/finders/storage.go                     | 242 ++++++++++++++++
 pkg/process/finders/vm/config.go                   |  57 ++++
 pkg/process/finders/vm/finder.go                   | 318 +++++++++++++++++++++
 pkg/process/finders/vm/process.go                  |  67 +++++
 pkg/process/finders/vm/template.go                 |  56 ++++
 pkg/{core/module.go => process/mdoule.go}          |  45 ++-
 pkg/tools/ip.go                                    | 153 ++++++++++
 pkg/tools/process.go                               |  95 ++++++
 pkg/tools/profiling/api.go                         |  64 +++++
 .../register.go => tools/profiling/go_library.go}  |  42 ++-
 pkg/tools/profiling/kernel.go                      |  67 +++++
 pkg/tools/profiling/objdump.go                     |  66 +++++
 33 files changed, 1837 insertions(+), 67 deletions(-)

diff --git a/.golangci.yml b/.golangci.yml
index e7982e4..dd69a2d 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -56,6 +56,13 @@ linters-settings:
   whitespace:
     multi-if: false
     multi-func: false
+  gosec:
+    includes:
+      - G401
+      - G306
+      - G101
+    excludes:
+      - G204
 
 linters:
   enable:
diff --git a/configs/rover_configs.yaml b/configs/rover_configs.yaml
index a1d5591..f46fb82 100644
--- a/configs/rover_configs.yaml
+++ b/configs/rover_configs.yaml
@@ -32,4 +32,29 @@ core:
     # How frequently to check the connection(second)
     check_period: ${ROVER_BACKEND_CHECK_PERIOD:5}
     # The auth value when send request
-    authentication: ${ROVER_BACKEND_AUTHENTICATION:""}
\ No newline at end of file
+    authentication: ${ROVER_BACKEND_AUTHENTICATION:""}
+
+process_discovery:
+  # The period of report or keep alive process(second)
+  heartbeat_period: ${ROVER_PROCESS_DISCOVERY_HEARTBEAT_PERIOD:20s}
+  # Detect processes in VM mode
+  vm:
+    # Is active the VM mode to detect processes
+    active: ${ROVER_PROCESS_DISCOVERY_VM_ACTIVE:false}
+    # The period to detect the process
+    period: ${ROVER_PROCESS_DISCOVERY_VM_PERIOD:3s}
+    finders:
+      # Use regex string to locate the processes
+      # Duplicate entities cannot be reported. If multiple entity are 
generated, only one process will be report
+      # If the multiple finders could match the same one process, only the 
first finder could be selected and report
+      - match_cmd_regex: ${ROVER_PROCESS_DISCOVERY_VM_FINDER_MATCH_CMD_REGEX:}
+        # The Layer need to relate to the process entity
+        layer: ${ROVER_PROCESS_DISCOVERY_VM_FINDER_LAYER:VM}
+        # The Service Name need to relate to the process entity
+        service_name: ${ROVER_PROCESS_DISCOVERY_VM_FINDER_SERVICE_NAME:}
+        # The Service Instance Name need to relate to the process entity
+        # By default the instance name is the host IP v4 address from "en0" 
net interface
+        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
diff --git a/go.mod b/go.mod
index 90a2a35..a600bb2 100644
--- a/go.mod
+++ b/go.mod
@@ -3,15 +3,19 @@ module github.com/apache/skywalking-rover
 go 1.17
 
 require (
+       github.com/google/uuid v1.3.0
        github.com/hashicorp/go-multierror v1.1.1
+       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
        google.golang.org/grpc v1.44.0
+       skywalking.apache.org/repo/goapi v0.0.0-20220228040118-f1aefbfd2a8c
 )
 
 require (
        github.com/fsnotify/fsnotify v1.5.1 // indirect
+       github.com/go-ole/go-ole v1.2.6 // indirect
        github.com/golang/protobuf v1.5.2 // indirect
        github.com/hashicorp/errwrap v1.0.0 // indirect
        github.com/hashicorp/hcl v1.0.0 // indirect
@@ -24,6 +28,9 @@ require (
        github.com/spf13/jwalterweatherman v1.1.0 // indirect
        github.com/spf13/pflag v1.0.5 // indirect
        github.com/subosito/gotenv v1.2.0 // indirect
+       github.com/tklauser/go-sysconf v0.3.9 // indirect
+       github.com/tklauser/numcpus v0.3.0 // indirect
+       github.com/yusufpapurcu/wmi v1.2.2 // indirect
        golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
        golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect
        golang.org/x/text v0.3.7 // indirect
diff --git a/go.sum b/go.sum
index 4748765..ace9634 100644
--- a/go.sum
+++ b/go.sum
@@ -115,6 +115,8 @@ github.com/go-kit/kit v0.8.0/go.mod 
h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
 github.com/go-kit/kit v0.9.0/go.mod 
h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-logfmt/logfmt v0.3.0/go.mod 
h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod 
h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod 
h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-stack/stack v1.8.0/go.mod 
h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/godbus/dbus/v5 v5.0.4/go.mod 
h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/gogo/protobuf v1.1.1/go.mod 
h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -188,6 +190,8 @@ github.com/google/pprof 
v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe
 github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod 
h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/renameio v0.1.0/go.mod 
h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.1.2/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod 
h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod 
h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod 
h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gax-go/v2 v2.1.0/go.mod 
h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
@@ -312,6 +316,8 @@ github.com/ryanuber/columnize 
v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
 github.com/sagikazarmark/crypt v0.3.0/go.mod 
h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
 github.com/sagikazarmark/crypt v0.4.0/go.mod 
h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod 
h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shirou/gopsutil v3.21.11+incompatible 
h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
+github.com/shirou/gopsutil v3.21.11+incompatible/go.mod 
h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/sirupsen/logrus v1.2.0/go.mod 
h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod 
h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.8.1 
h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
@@ -342,12 +348,18 @@ github.com/stretchr/testify v1.7.0 
h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
 github.com/stretchr/testify v1.7.0/go.mod 
h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0 
h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod 
h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tklauser/go-sysconf v0.3.9 
h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
+github.com/tklauser/go-sysconf v0.3.9/go.mod 
h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
+github.com/tklauser/numcpus v0.3.0 
h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
+github.com/tklauser/numcpus v0.3.0/go.mod 
h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod 
h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
 github.com/yuin/goldmark v1.1.25/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod 
h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yusufpapurcu/wmi v1.2.2 
h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
+github.com/yusufpapurcu/wmi v1.2.2/go.mod 
h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 go.etcd.io/etcd/api/v3 v3.5.1/go.mod 
h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
 go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod 
h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
 go.etcd.io/etcd/client/v2 v2.305.1/go.mod 
h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
@@ -493,6 +505,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod 
h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -536,9 +549,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod 
h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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-20210908233432-aa78b53d3365/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/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=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -796,3 +811,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod 
h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 rsc.io/binaryregexp v0.2.0/go.mod 
h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+skywalking.apache.org/repo/goapi v0.0.0-20220228040118-f1aefbfd2a8c 
h1:gf4vxQAHDGBYPpqBsWN5x6+gBCnfzxnI0yPc8acznO8=
+skywalking.apache.org/repo/goapi v0.0.0-20220228040118-f1aefbfd2a8c/go.mod 
h1:4KrWd+Oi4lkB+PtxZgIlf+3T6EECPru4fOWNMEHjxRk=
diff --git a/pkg/boot/module.go b/pkg/boot/module.go
index db92ad4..a66e4ed 100644
--- a/pkg/boot/module.go
+++ b/pkg/boot/module.go
@@ -91,6 +91,11 @@ func (m *ModuleStarter) Run(ctx context.Context) error {
                m.startedModules = append(m.startedModules, module)
        }
 
+       // notify all modules setup success
+       for _, mod := range m.startedModules {
+               mod.NotifyStartSuccess()
+       }
+
        // register terminal
        signals := make(chan os.Signal, 1)
        signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, 
syscall.SIGTERM)
diff --git a/pkg/boot/module_test.go b/pkg/boot/module_test.go
index cd30e49..a6a89d8 100644
--- a/pkg/boot/module_test.go
+++ b/pkg/boot/module_test.go
@@ -156,6 +156,9 @@ func TestRun(t *testing.T) {
                        startSequence: []string{
                                "test1", "test2",
                        },
+                       startNotifySequence: []string{
+                               "test1", "test2",
+                       },
                        shutdownSequence: []string{
                                "test2", "test1",
                        },
@@ -175,6 +178,9 @@ func TestRun(t *testing.T) {
                        startSequence: []string{
                                "test1", "test2",
                        },
+                       startNotifySequence: []string{
+                               "test1", "test2",
+                       },
                        shutdownSequence: []string{
                                "test2", "test1",
                        },
@@ -194,6 +200,9 @@ func TestRun(t *testing.T) {
                        startSequence: []string{
                                "test2", "test1",
                        },
+                       startNotifySequence: []string{
+                               "test2", "test1",
+                       },
                        shutdownSequence: []string{
                                "test1", "test2",
                        },
@@ -211,12 +220,13 @@ func TestRun(t *testing.T) {
 }
 
 type testRunStruct struct {
-       name             string
-       dependencies     map[string][]string
-       modules          []string
-       startSequence    []string
-       shutdownSequence []string
-       triggerShutdown  func(ctx context.Context, cancel context.CancelFunc, 
starter *ModuleStarter)
+       name                string
+       dependencies        map[string][]string
+       modules             []string
+       startSequence       []string
+       startNotifySequence []string
+       shutdownSequence    []string
+       triggerShutdown     func(ctx context.Context, cancel 
context.CancelFunc, starter *ModuleStarter)
 }
 
 func testRun(run *testRunStruct, t *testing.T) {
@@ -265,20 +275,29 @@ func testRun(run *testRunStruct, t *testing.T) {
                t.Fatalf("the module start sequence not right: \nexcept: 
\n%v\nactual:\n%v", run.startSequence, sequence.startSequence)
        }
 
+       if !reflect.DeepEqual(sequence.startNotifySequence, 
run.startNotifySequence) {
+               t.Fatalf("the module start sequence not right: \nexcept: 
\n%v\nactual:\n%v", run.startSequence, sequence.startSequence)
+       }
+
        if !reflect.DeepEqual(sequence.shutdownSequence, run.shutdownSequence) {
                t.Fatalf("the module shutdown sequence not right: \nexcept: 
\n%v\nactual:\n%v", run.shutdownSequence, sequence.shutdownSequence)
        }
 }
 
 type sequenceMonitor struct {
-       startSequence    []string
-       shutdownSequence []string
+       startSequence       []string
+       startNotifySequence []string
+       shutdownSequence    []string
 }
 
 func (s *sequenceMonitor) AddStartup(name string) {
        s.startSequence = append(s.startSequence, name)
 }
 
+func (s *sequenceMonitor) AddNotifyStart(name string) {
+       s.startNotifySequence = append(s.startNotifySequence, name)
+}
+
 func (s *sequenceMonitor) AddShutdown(name string) {
        s.shutdownSequence = append(s.shutdownSequence, name)
 }
@@ -306,6 +325,10 @@ func (t *testModule) Start(ctx context.Context, mgr 
*module.Manager) error {
        return nil
 }
 
+func (t *testModule) NotifyStartSuccess() {
+       t.sequence.AddNotifyStart(t.name)
+}
+
 func (t *testModule) Shutdown(ctx context.Context, mgr *module.Manager) error {
        t.sequence.AddShutdown(t.name)
        return nil
diff --git a/pkg/boot/register.go b/pkg/boot/register.go
index d77d5c2..061cb4a 100644
--- a/pkg/boot/register.go
+++ b/pkg/boot/register.go
@@ -20,9 +20,11 @@ package boot
 import (
        "github.com/apache/skywalking-rover/pkg/core"
        "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/apache/skywalking-rover/pkg/process"
 )
 
 func init() {
        // register all active module
        module.Register(core.NewModule())
+       module.Register(process.NewModule())
 }
diff --git a/pkg/config/env_override.go b/pkg/config/env_override.go
index e42cdd7..be8a925 100644
--- a/pkg/config/env_override.go
+++ b/pkg/config/env_override.go
@@ -46,6 +46,10 @@ func overrideConfig(v *viper.Viper, key string, envRegex 
*regexp.Regexp) {
                v.Set(key, overrideString(val, envRegex))
        case []interface{}:
                v.Set(key, overrideSlice(val, envRegex))
+       case int:
+               v.Set(key, val)
+       case bool:
+               v.Set(key, val)
        }
 }
 
diff --git a/pkg/core/api.go b/pkg/core/api.go
index 7559ccc..a05a33f 100644
--- a/pkg/core/api.go
+++ b/pkg/core/api.go
@@ -21,6 +21,8 @@ import "github.com/apache/skywalking-rover/pkg/core/backend"
 
 // Operator when the other module operate with core module
 type Operator interface {
+       // InstanceId of Rover
+       InstanceID() string
        // BackendOperator for operate with backend client
        BackendOperator() backend.Operator
 }
diff --git a/pkg/core/module.go b/pkg/core/module.go
index d044bfa..7670b1a 100644
--- a/pkg/core/module.go
+++ b/pkg/core/module.go
@@ -20,10 +20,12 @@ package core
 import (
        "context"
 
-       "github.com/apache/skywalking-rover/pkg/core/backend"
-       "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/google/uuid"
 
        "github.com/hashicorp/go-multierror"
+
+       "github.com/apache/skywalking-rover/pkg/core/backend"
+       "github.com/apache/skywalking-rover/pkg/module"
 )
 
 const ModuleName = "core"
@@ -31,6 +33,7 @@ const ModuleName = "core"
 type Module struct {
        config *Config
 
+       instanceID    string
        backendClient *backend.Client
 }
 
@@ -51,6 +54,8 @@ func (m *Module) Config() module.ConfigInterface {
 }
 
 func (m *Module) Start(ctx context.Context, mgr *module.Manager) error {
+       // generate instance id
+       m.instanceID = uuid.New().String()
        // backend client
        if m.config.BackendConfig != nil {
                m.backendClient = backend.NewClient(m.config.BackendConfig)
@@ -61,6 +66,9 @@ func (m *Module) Start(ctx context.Context, mgr 
*module.Manager) error {
        return nil
 }
 
+func (m *Module) NotifyStartSuccess() {
+}
+
 func (m *Module) Shutdown(ctx context.Context, mgr *module.Manager) error {
        var result *multierror.Error
        if m.backendClient != nil {
@@ -69,9 +77,13 @@ func (m *Module) Shutdown(ctx context.Context, mgr 
*module.Manager) error {
        return result.ErrorOrNil()
 }
 
-func (m *Module) ClientGrpcOperator() backend.Operator {
+func (m *Module) BackendOperator() backend.Operator {
        if m.backendClient == nil {
                return nil
        }
        return m.backendClient
 }
+
+func (m *Module) InstanceID() string {
+       return m.instanceID
+}
diff --git a/pkg/module/manager_test.go b/pkg/module/manager_test.go
index f713944..af7ac7c 100644
--- a/pkg/module/manager_test.go
+++ b/pkg/module/manager_test.go
@@ -71,6 +71,9 @@ func (t *testModule) Start(ctx context.Context, mgr *Manager) 
error {
        return nil
 }
 
+func (t *testModule) NotifyStartSuccess() {
+}
+
 func (t *testModule) Shutdown(ctx context.Context, mgr *Manager) error {
        return nil
 }
diff --git a/pkg/module/module.go b/pkg/module/module.go
index e6f43d6..a97c9ae 100644
--- a/pkg/module/module.go
+++ b/pkg/module/module.go
@@ -37,6 +37,9 @@ type Module interface {
        // The module needs to return the start result after startup is 
completed
        Start(ctx context.Context, mgr *Manager) error
 
+       // NotifyStartSuccess when all module have been start success
+       NotifyStartSuccess()
+
        // Shutdown module, the sequence of shutdown is the reverse of the 
module Start
        // The shutdown would trigger in the following cases
        // 1. If other modules fail to start
diff --git a/pkg/process/api/process.go b/pkg/process/api/process.go
new file mode 100644
index 0000000..39d5d84
--- /dev/null
+++ b/pkg/process/api/process.go
@@ -0,0 +1,62 @@
+// 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 api
+
+type ProcessDetectType int8
+
+const (
+       _ ProcessDetectType = iota
+       VM
+       KUBERNETES
+)
+
+func (d ProcessDetectType) Name() string {
+       if d == VM {
+               return "VM"
+       } else if d == KUBERNETES {
+               return "Kubernetes"
+       }
+       return "not matched"
+}
+
+type ProcessInterface interface {
+       // ID of process, it's provide by backend
+       ID() string
+       // Pid of process
+       Pid() int32
+       // DetectType of process, it decide how to find this process
+       DetectType() ProcessDetectType
+       // Entity of process in backend
+       Entity() *ProcessEntity
+}
+
+// ProcessEntity is related to backend entity concept
+type ProcessEntity struct {
+       Layer        string
+       ServiceName  string
+       InstanceName string
+       ProcessName  string
+}
+
+func (e *ProcessEntity) SameWith(other *ProcessEntity) bool {
+       if e == nil || other == nil {
+               return false
+       }
+       return e.Layer == other.Layer && e.ServiceName == other.ServiceName && 
e.InstanceName == other.InstanceName &&
+               e.ProcessName == other.ProcessName
+}
diff --git a/pkg/boot/register.go b/pkg/process/config.go
similarity index 74%
copy from pkg/boot/register.go
copy to pkg/process/config.go
index d77d5c2..36194ca 100644
--- a/pkg/boot/register.go
+++ b/pkg/process/config.go
@@ -15,14 +15,23 @@
 // 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/finders/vm"
 )
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
+type Config struct {
+       module.Config
+
+       // heartbeat the process list period
+       HeartbeatPeriod string `mapstructure:"heartbeat_period"`
+
+       // VM process finder
+       VM *vm.Config
+}
+
+func (c *Config) IsActive() bool {
+       return true
 }
diff --git a/pkg/boot/register.go b/pkg/process/finders/base/config.go
similarity index 80%
copy from pkg/boot/register.go
copy to pkg/process/finders/base/config.go
index d77d5c2..cd37580 100644
--- a/pkg/boot/register.go
+++ b/pkg/process/finders/base/config.go
@@ -15,14 +15,9 @@
 // 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"
-)
-
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
+type FinderBaseConfig interface {
+       // ActiveFinder to detect process
+       ActiveFinder() bool
 }
diff --git a/pkg/process/finders/base/finder.go 
b/pkg/process/finders/base/finder.go
new file mode 100644
index 0000000..29733f6
--- /dev/null
+++ b/pkg/process/finders/base/finder.go
@@ -0,0 +1,58 @@
+// 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 (
+       "context"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/process/v3"
+
+       "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/apache/skywalking-rover/pkg/process/api"
+)
+
+type BuildEBPFProcessContext struct {
+       HostIP string
+}
+
+// ProcessFinder is defined how to detect the process and communicate with 
backend
+type ProcessFinder interface {
+       // Init the finder before Start
+       Init(ctx context.Context, conf FinderBaseConfig, manager 
ProcessManager) error
+       // Start to detect process
+       Start()
+       // Stop the process detect
+       Stop() error
+       // DetectType of Process is detecting
+       DetectType() api.ProcessDetectType
+
+       // ValidateProcessIsSame between two same finder process
+       ValidateProcessIsSame(p1, p2 DetectedProcess) bool
+
+       // BuildEBPFProcess is transform the process entity as backend protocol 
data
+       BuildEBPFProcess(ctx *BuildEBPFProcessContext, process DetectedProcess) 
*v3.EBPFProcessProperties
+       // ParseProcessId is means how to read the process id receive from 
backend
+       ParseProcessID(process DetectedProcess, downstream 
*v3.EBPFProcessDownstream) string
+}
+
+// ProcessManager is an API work for help ProcessFinder synchronized process 
with backend
+type ProcessManager interface {
+       GetModuleManager() *module.Manager
+       // SyncAllProcessInFinder is mean synchronized all processes data from 
current ProcessFinder
+       SyncAllProcessInFinder(processes []DetectedProcess)
+}
diff --git a/pkg/boot/register.go b/pkg/process/finders/base/process.go
similarity index 64%
copy from pkg/boot/register.go
copy to pkg/process/finders/base/process.go
index d77d5c2..7358bd7 100644
--- a/pkg/boot/register.go
+++ b/pkg/process/finders/base/process.go
@@ -15,14 +15,22 @@
 // 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/shirou/gopsutil/process"
+
+       "github.com/apache/skywalking-rover/pkg/process/api"
 )
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
+// 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
 }
diff --git a/pkg/process/finders/base/template.go 
b/pkg/process/finders/base/template.go
new file mode 100644
index 0000000..3b30c9b
--- /dev/null
+++ b/pkg/process/finders/base/template.go
@@ -0,0 +1,123 @@
+// 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 (
+       "bytes"
+       "fmt"
+       "text/template"
+
+       "github.com/apache/skywalking-rover/pkg/core"
+       "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/apache/skywalking-rover/pkg/tools"
+)
+
+type TemplateBuilder struct {
+       Name     string
+       Template string
+
+       template *template.Template
+}
+
+func NewTemplateBuilder(name, content string) (*TemplateBuilder, error) {
+       tmpl, err := template.New(name).Parse(content)
+       if err != nil {
+               return nil, fmt.Errorf("failed to parse template for %s, 
content: %s. reason: %v", name, content, err)
+       }
+       return &TemplateBuilder{Name: name, Template: content, template: tmpl}, 
nil
+}
+
+func (t *TemplateBuilder) Execute(data interface{}) (string, error) {
+       var r bytes.Buffer
+       if err := t.template.Execute(&r, data); err != nil {
+               return "", err
+       }
+       return r.String(), nil
+}
+
+// NewTemplateRover is generated the Rover context for render
+func NewTemplateRover(manager *module.Manager) *TemplateRover {
+       operator := manager.FindModule(core.ModuleName).(core.Operator)
+       return &TemplateRover{operator.InstanceID()}
+}
+
+// NewTemplateProcess is generated the process context for render
+func NewTemplateProcess(manager *module.Manager, process DetectedProcess) 
*TemplateProcess {
+       return &TemplateProcess{process}
+}
+
+type TemplateRover struct {
+       instanceID string
+}
+
+// InstanceID of rover
+func (t *TemplateRover) InstanceID() string {
+       return t.instanceID
+}
+
+// HostIPV4 ip v4 address of local machine from appoint net interface name
+func (t *TemplateRover) HostIPV4(name string) (string, error) {
+       v4 := tools.HostIPAddressV4(name)
+       if v4 == "" {
+               return "", fmt.Errorf("could not found the ip v4 address from 
%s", name)
+       }
+       return v4, nil
+}
+
+// HostIPV6 ip v6 address of local machine from appoint net interface name
+func (t *TemplateRover) HostIPV6(name string) (string, error) {
+       v6 := tools.HostIPAddressV6(name)
+       if v6 == "" {
+               return "", fmt.Errorf("could not found the ip v6 address from 
%s", name)
+       }
+       return v6, nil
+}
+
+// HostName name of local machine
+func (t *TemplateRover) HostName() string {
+       return tools.Hostname()
+}
+
+type TemplateProcess struct {
+       DetectedProcess
+}
+
+// ExeFilePath Execute file path
+func (p *TemplateProcess) ExeFilePath() (string, error) {
+       return p.OriginalProcess().Exe()
+}
+
+// ExeName Execute file name
+func (p *TemplateProcess) ExeName() (string, error) {
+       return p.OriginalProcess().Name()
+}
+
+// CommandLine command line of process
+func (p *TemplateProcess) CommandLine() (string, error) {
+       return p.OriginalProcess().Cmdline()
+}
+
+// Pid of process
+func (p *TemplateProcess) Pid() int32 {
+       return p.OriginalProcess().Pid
+}
+
+// WorkDir means which directory to run the execute file
+func (p *TemplateProcess) WorkDir() (string, error) {
+       return p.OriginalProcess().Cwd()
+}
diff --git a/pkg/process/finders/context.go b/pkg/process/finders/context.go
new file mode 100644
index 0000000..8a5219b
--- /dev/null
+++ b/pkg/process/finders/context.go
@@ -0,0 +1,63 @@
+// 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 finders
+
+import (
+       "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/process/finders/base"
+)
+
+type ProcessUploadStatus int8
+
+const (
+       _ ProcessUploadStatus = iota
+       // NotReport is detected the process, but not report to the backend
+       NotReport
+       // ReportSuccess mean backend has informed, so it could have id
+       ReportSuccess
+       // Ignore by the backend
+       Ignore
+)
+
+type ProcessContext struct {
+       // process ID from backed
+       id string
+
+       // sync with backend status
+       syncStatus ProcessUploadStatus
+
+       // detectProcess from finder
+       detectProcess base.DetectedProcess
+       detectType    api.ProcessDetectType
+}
+
+func (p *ProcessContext) ID() string {
+       return p.id
+}
+
+func (p *ProcessContext) Pid() int32 {
+       return p.detectProcess.Pid()
+}
+
+func (p *ProcessContext) DetectType() api.ProcessDetectType {
+       return p.detectType
+}
+
+func (p *ProcessContext) Entity() *api.ProcessEntity {
+       return p.detectProcess.Entity()
+}
diff --git a/pkg/process/finders/manager.go b/pkg/process/finders/manager.go
new file mode 100644
index 0000000..9c2a597
--- /dev/null
+++ b/pkg/process/finders/manager.go
@@ -0,0 +1,119 @@
+// 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 finders
+
+import (
+       "context"
+       "fmt"
+       "time"
+
+       "github.com/apache/skywalking-rover/pkg/logger"
+
+       "github.com/hashicorp/go-multierror"
+
+       "github.com/apache/skywalking-rover/pkg/module"
+       "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/process/finders/base"
+)
+
+var log = logger.GetLogger("process", "finder")
+
+// ProcessManager means Manage all Process
+type ProcessManager struct {
+       moduleManager *module.Manager
+       // finders
+       finders map[base.FinderBaseConfig]base.ProcessFinder
+       // process storage
+       storage *ProcessStorage
+}
+
+type ProcessManagerWithFinder struct {
+       *ProcessManager
+       finderType api.ProcessDetectType
+}
+
+func NewProcessManager(ctx context.Context, moduleManager *module.Manager,
+       reportInterval time.Duration, configs ...base.FinderBaseConfig) 
(*ProcessManager, error) {
+       // locate all finders
+       confinedFinders := make(map[base.FinderBaseConfig]base.ProcessFinder)
+       fsList := make([]base.ProcessFinder, 0)
+       for _, conf := range configs {
+               if conf == nil || !conf.ActiveFinder() {
+                       continue
+               }
+               finder := getFinder(conf)
+               confinedFinders[conf] = finder
+               fsList = append(fsList, finder)
+       }
+       if len(confinedFinders) == 0 {
+               return nil, fmt.Errorf("no process finder found")
+       }
+
+       // start new storage
+       storage, err := NewProcessStorage(ctx, moduleManager, reportInterval, 
fsList)
+       if err != nil {
+               return nil, err
+       }
+
+       // init all finders
+       manager := &ProcessManager{
+               finders:       confinedFinders,
+               moduleManager: moduleManager,
+               storage:       storage,
+       }
+       for conf, finder := range confinedFinders {
+               processManager := &ProcessManagerWithFinder{ProcessManager: 
manager, finderType: finder.DetectType()}
+               if err := finder.Init(ctx, conf, processManager); err != nil {
+                       return nil, fmt.Errorf("starting %s finder failure: 
%v", finder.DetectType().Name(), err)
+               }
+       }
+
+       return manager, nil
+}
+
+func (m *ProcessManager) Start() {
+       // start all finders
+       for _, finder := range m.finders {
+               finder.Start()
+       }
+       // start storage report with interval
+       m.storage.StartReport()
+}
+
+func (m *ProcessManager) Shutdown() error {
+       var result error
+       // stop reporter
+       if err := m.storage.StopReport(); err != nil {
+               result = multierror.Append(result, err)
+       }
+       // stop finders
+       for _, finder := range m.finders {
+               if err := finder.Stop(); err != nil {
+                       result = multierror.Append(result, err)
+               }
+       }
+       return result
+}
+
+func (p *ProcessManagerWithFinder) GetModuleManager() *module.Manager {
+       return p.moduleManager
+}
+
+func (p *ProcessManagerWithFinder) SyncAllProcessInFinder(processes 
[]base.DetectedProcess) {
+       p.storage.SyncAllProcessInFinder(p.finderType, processes)
+}
diff --git a/pkg/boot/register.go b/pkg/process/finders/register.go
similarity index 64%
copy from pkg/boot/register.go
copy to pkg/process/finders/register.go
index d77d5c2..4f63bb8 100644
--- a/pkg/boot/register.go
+++ b/pkg/process/finders/register.go
@@ -15,14 +15,25 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package boot
+package finders
 
 import (
-       "github.com/apache/skywalking-rover/pkg/core"
-       "github.com/apache/skywalking-rover/pkg/module"
+       "reflect"
+
+       "github.com/apache/skywalking-rover/pkg/process/finders/base"
+       "github.com/apache/skywalking-rover/pkg/process/finders/vm"
 )
 
+var finders = make(map[reflect.Type]base.ProcessFinder)
+
 func init() {
-       // register all active module
-       module.Register(core.NewModule())
+       registerFinder(reflect.TypeOf(&vm.Config{}), &vm.ProcessFinder{})
+}
+
+func registerFinder(t reflect.Type, finder base.ProcessFinder) {
+       finders[t] = finder
+}
+
+func getFinder(conf base.FinderBaseConfig) base.ProcessFinder {
+       return finders[reflect.TypeOf(conf)]
 }
diff --git a/pkg/process/finders/storage.go b/pkg/process/finders/storage.go
new file mode 100644
index 0000000..da9c5c8
--- /dev/null
+++ b/pkg/process/finders/storage.go
@@ -0,0 +1,242 @@
+// 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 finders
+
+import (
+       "context"
+       "sync"
+       "time"
+
+       "github.com/hashicorp/go-multierror"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/process/v3"
+
+       "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/base"
+       "github.com/apache/skywalking-rover/pkg/tools"
+)
+
+type ProcessStorage struct {
+       processes map[int32]*ProcessContext
+       mutex     sync.Mutex
+
+       // working with backend
+       reportInterval time.Duration
+       roverID        string
+       processClient  v3.EBPFProcessServiceClient
+       finders        map[api.ProcessDetectType]base.ProcessFinder
+
+       // report context
+       ctx    context.Context
+       cancel context.CancelFunc
+}
+
+func NewProcessStorage(ctx context.Context, moduleManager *module.Manager,
+       reportInterval time.Duration, finderList []base.ProcessFinder) 
(*ProcessStorage, error) {
+       data := make(map[int32]*ProcessContext)
+       // working with core module
+       coreOperator := 
moduleManager.FindModule(core.ModuleName).(core.Operator)
+       roverID := coreOperator.InstanceID()
+       backendConn := coreOperator.BackendOperator().GetConnection()
+       processClient := v3.NewEBPFProcessServiceClient(backendConn)
+       ctx, cancel := context.WithCancel(ctx)
+       fs := make(map[api.ProcessDetectType]base.ProcessFinder)
+       for _, f := range finderList {
+               fs[f.DetectType()] = f
+       }
+       return &ProcessStorage{
+               processes:      data,
+               reportInterval: reportInterval,
+               roverID:        roverID,
+               processClient:  processClient,
+               finders:        fs,
+               ctx:            ctx,
+               cancel:         cancel,
+       }, nil
+}
+
+func (s *ProcessStorage) StartReport() {
+       go func() {
+               timeTicker := time.NewTicker(s.reportInterval)
+               for {
+                       select {
+                       case <-timeTicker.C:
+                               if err := s.reportAllProcesses(); err != nil {
+                                       log.Errorf("report all processes error: 
%v", err)
+                               }
+                       case <-s.ctx.Done():
+                               timeTicker.Stop()
+                               return
+                       }
+               }
+       }()
+}
+
+func (s *ProcessStorage) StopReport() error {
+       s.cancel()
+       return nil
+}
+
+func (s *ProcessStorage) reportAllProcesses() error {
+       s.mutex.Lock()
+       defer s.mutex.Unlock()
+       if len(s.processes) == 0 {
+               return nil
+       }
+
+       // build process list(wait report or keep alive)
+       waitReportProcesses := make([]*ProcessContext, 0)
+       keepAliveProcesses := make([]*ProcessContext, 0)
+       for _, p := range s.processes {
+               if p.syncStatus == NotReport {
+                       waitReportProcesses = append(waitReportProcesses, p)
+               } else if p.syncStatus == ReportSuccess {
+                       keepAliveProcesses = append(keepAliveProcesses, p)
+               }
+       }
+
+       // process with backend
+       var result error
+       if err := s.processesReport(waitReportProcesses); err != nil {
+               result = multierror.Append(result, err)
+       }
+       if err := s.processesKeepAlive(keepAliveProcesses); err != nil {
+               result = multierror.Append(result, err)
+       }
+
+       return result
+}
+
+func (s *ProcessStorage) processesKeepAlive(waitKeepAliveProcess 
[]*ProcessContext) error {
+       if len(waitKeepAliveProcess) == 0 {
+               return nil
+       }
+
+       processIDList := make([]*v3.EBPFProcessPingPkg, 0)
+       for _, ps := range waitKeepAliveProcess {
+               if ps.id != "" {
+                       processIDList = append(processIDList, 
&v3.EBPFProcessPingPkg{ProcessId: ps.id})
+               }
+       }
+
+       _, err := s.processClient.KeepAlive(s.ctx, 
&v3.EBPFProcessPingPkgList{Processes: processIDList})
+       return err
+}
+
+func (s *ProcessStorage) processesReport(waitReportProcesses 
[]*ProcessContext) error {
+       if len(waitReportProcesses) == 0 {
+               return nil
+       }
+
+       properties := make([]*v3.EBPFProcessProperties, 0)
+       buildContext := &base.BuildEBPFProcessContext{}
+       buildContext.HostIP = tools.DefaultHostIPAddress()
+       for _, ps := range waitReportProcesses {
+               properties = append(properties, 
s.finders[ps.DetectType()].BuildEBPFProcess(buildContext, ps.detectProcess))
+       }
+       processes, err := s.processClient.ReportProcesses(s.ctx, 
&v3.EBPFProcessReportList{Processes: properties})
+       if err != nil {
+               return err
+       }
+
+       for _, waitProcess := range waitReportProcesses {
+               found := false
+               for _, reportedProcess := range processes.GetProcesses() {
+                       id := 
s.finders[waitProcess.DetectType()].ParseProcessID(waitProcess.detectProcess, 
reportedProcess)
+                       if id == "" {
+                               continue
+                       }
+
+                       s.updateProcessToUploadSuccess(waitProcess, id)
+                       found = true
+                       break
+               }
+
+               if !found {
+                       s.updateProcessToUploadIgnored(waitProcess)
+               }
+       }
+       return nil
+}
+
+func (s *ProcessStorage) SyncAllProcessInFinder(finder api.ProcessDetectType, 
processes []base.DetectedProcess) {
+       s.mutex.Lock()
+       defer s.mutex.Unlock()
+
+       pidToProcess := make(map[int32]base.DetectedProcess)
+       for _, ps := range processes {
+               pidToProcess[ps.Pid()] = ps
+       }
+
+       // for each all process in the manager
+       for pid, managedProcess := range s.processes {
+               needToSyncProcess := pidToProcess[pid]
+               // remove it from the list of need to sync
+               pidToProcess[pid] = nil
+
+               // The process to be synchronized is not found in all process 
list
+               // And this process is same with finder type
+               // So we need to remove this process
+               if needToSyncProcess == nil {
+                       if managedProcess.DetectType() == finder {
+                               s.processes[pid] = nil
+                       }
+                       continue
+               }
+
+               // they are the not same detect type
+               // TODO we just implement the VM mode process for now, so 
continue
+               if needToSyncProcess.DetectType() != 
managedProcess.DetectType() {
+                       continue
+               }
+
+               // check the entity is the same
+               if 
s.finders[needToSyncProcess.DetectType()].ValidateProcessIsSame(needToSyncProcess,
 managedProcess.detectProcess) {
+                       continue
+               }
+
+               // different entity, so we need to remove oldest and add the 
new process
+               s.processes[pid] = s.constructNewProcessContext(finder, 
needToSyncProcess)
+       }
+
+       // other processes are need to be added
+       for pid, ps := range pidToProcess {
+               if ps != nil {
+                       s.processes[pid] = s.constructNewProcessContext(finder, 
ps)
+               }
+       }
+}
+
+func (s *ProcessStorage) constructNewProcessContext(finder 
api.ProcessDetectType, process base.DetectedProcess) *ProcessContext {
+       return &ProcessContext{
+               syncStatus:    NotReport,
+               detectProcess: process,
+               detectType:    finder,
+       }
+}
+
+func (s *ProcessStorage) updateProcessToUploadSuccess(pc *ProcessContext, id 
string) {
+       pc.id = id
+       pc.syncStatus = ReportSuccess
+}
+
+func (s *ProcessStorage) updateProcessToUploadIgnored(pc *ProcessContext) {
+       pc.syncStatus = Ignore
+}
diff --git a/pkg/process/finders/vm/config.go b/pkg/process/finders/vm/config.go
new file mode 100644
index 0000000..d1174b1
--- /dev/null
+++ b/pkg/process/finders/vm/config.go
@@ -0,0 +1,57 @@
+// 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 vm
+
+import (
+       "regexp"
+
+       "github.com/apache/skywalking-rover/pkg/process/finders/base"
+)
+
+type Config struct {
+       base.FinderBaseConfig
+
+       Active bool `mapstructure:"active"`
+
+       // Check Period
+       Period string `mapstructure:"period"`
+
+       // Process finder list
+       Finders []*ProcessFinderConfig `mapstructure:"finders"`
+}
+
+func (c *Config) ActiveFinder() bool {
+       return c.Active
+}
+
+type ProcessFinderConfig struct {
+       // Use command line to match the processes
+       MatchCommandRegex string `mapstructure:"match_cmd_regex"`
+
+       // entity
+       Layer        string `mapstructure:"layer"`         // process layer
+       ServiceName  string `mapstructure:"service_name"`  // process entity 
service name
+       InstanceName string `mapstructure:"instance_name"` // process entity 
service instance name
+       ProcessName  string `mapstructure:"process_name"`  // process entity 
process name
+
+       // pre-build for build the process
+       commandlineRegex    *regexp.Regexp
+       serviceNameBuilder  *base.TemplateBuilder
+       instanceNameBuilder *base.TemplateBuilder
+       processNameBuilder  *base.TemplateBuilder
+}
diff --git a/pkg/process/finders/vm/finder.go b/pkg/process/finders/vm/finder.go
new file mode 100644
index 0000000..1f275a8
--- /dev/null
+++ b/pkg/process/finders/vm/finder.go
@@ -0,0 +1,318 @@
+// 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 vm
+
+import (
+       "context"
+       "fmt"
+       "regexp"
+       "time"
+
+       v3 "skywalking.apache.org/repo/goapi/collect/ebpf/profiling/process/v3"
+
+       "github.com/shirou/gopsutil/process"
+
+       "github.com/apache/skywalking-rover/pkg/logger"
+       "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/process/finders/base"
+       "github.com/apache/skywalking-rover/pkg/tools"
+)
+
+var log = logger.GetLogger("process", "finder", "vm")
+
+type ProcessFinder struct {
+       conf *Config
+
+       manager   base.ProcessManager
+       ctx       context.Context
+       cancelCtx context.CancelFunc
+
+       period time.Duration
+}
+
+func (p *ProcessFinder) Init(ctx context.Context, conf base.FinderBaseConfig, 
manager base.ProcessManager) error {
+       if err := validateConfig(conf.(*Config)); err != nil {
+               return err
+       }
+
+       p.conf = conf.(*Config)
+       p.manager = manager
+       p.ctx, p.cancelCtx = context.WithCancel(ctx)
+
+       period, err := time.ParseDuration(p.conf.Period)
+       if err != nil {
+               return err
+       }
+       p.period = period
+       return nil
+}
+
+func (p *ProcessFinder) Start() {
+       go p.startWatch()
+}
+
+func (p *ProcessFinder) Stop() error {
+       p.cancelCtx()
+       return nil
+}
+
+func (p *ProcessFinder) DetectType() api.ProcessDetectType {
+       return api.VM
+}
+
+func (p *ProcessFinder) ValidateProcessIsSame(p1, p2 base.DetectedProcess) 
bool {
+       vm1 := p1.(*Process)
+       vm2 := p2.(*Process)
+       return p1.Pid() == p2.Pid() && vm1.cmd == vm2.cmd && 
p1.Entity().SameWith(p2.Entity())
+}
+
+func (p *ProcessFinder) BuildEBPFProcess(ctx *base.BuildEBPFProcessContext, ps 
base.DetectedProcess) *v3.EBPFProcessProperties {
+       hostProcess := &v3.EBPFHostProcessMetadata{}
+       hostProcess.HostIP = ctx.HostIP
+       hostProcess.Pid = ps.Pid()
+       hostProcess.Cmd = ps.(*Process).cmd
+       hostProcess.Entity = &v3.EBPFProcessEntityMetadata{
+               Layer:        ps.Entity().Layer,
+               ServiceName:  ps.Entity().ServiceName,
+               InstanceName: ps.Entity().InstanceName,
+               ProcessName:  ps.Entity().ProcessName,
+       }
+       properties := &v3.EBPFProcessProperties{Metadata: 
&v3.EBPFProcessProperties_HostProcess{
+               HostProcess: hostProcess,
+       }}
+       return properties
+}
+
+func (p *ProcessFinder) ParseProcessID(ps base.DetectedProcess, downstream 
*v3.EBPFProcessDownstream) string {
+       if downstream.GetHostProcess() == nil {
+               return ""
+       }
+       if ps.Pid() == downstream.GetHostProcess().GetPid() {
+               return downstream.ProcessId
+       }
+       return ""
+}
+
+func (p *ProcessFinder) startWatch() {
+       // find one time
+       if err := p.findAndReportProcesses(); err != nil {
+               log.Warnf("list all process failure, %v", err)
+       }
+       // schedule
+       ticker := time.NewTicker(p.period)
+       for {
+               select {
+               case <-ticker.C:
+                       if err := p.findAndReportProcesses(); err != nil {
+                               log.Warnf("list all process failure, %v", err)
+                       }
+               case <-p.ctx.Done():
+                       return
+               }
+       }
+}
+
+func (p *ProcessFinder) findAndReportProcesses() error {
+       // find all process
+       processes, err := p.findMatchedProcesses()
+       if err != nil {
+               return err
+       }
+
+       // validate the process could be profiling
+       processes = p.validateTheProcessesCouldProfiling(processes)
+
+       // report to the manager
+       psList := make([]base.DetectedProcess, 0)
+       for _, ps := range processes {
+               psList = append(psList, ps)
+       }
+       p.manager.SyncAllProcessInFinder(psList)
+       return nil
+}
+
+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)
+                       continue
+               }
+
+               // check support profiling
+               if pf, err := tools.ExecutableFileProfilingStat(exe); err != 
nil {
+                       log.Warnf("the process could not be profiling, so 
ignored. pid: %d, reason: %v", ps.pid, err)
+                       continue
+               } else {
+                       ps.profiling = pf
+               }
+
+               result = append(result, ps)
+       }
+       return result
+}
+
+func (p *ProcessFinder) findMatchedProcesses() ([]*Process, error) {
+       // all system processes
+       processes, err := process.ProcessesWithContext(p.ctx)
+       if err != nil {
+               return nil, err
+       }
+       // find all matches processes
+       findedProcesses := make([]*Process, 0)
+       for _, pro := range processes {
+               // TODO should we need verify the process must be in the root 
namespace? such as exclude the container processes
+               // That's mean the same process would could only be detect by 
one finder?
+
+               // find the matched process finder
+               finderConfig, cmdline, err := p.findMatchesFinder(pro)
+               if err != nil {
+                       log.Warnf("failed to match process %d, reason: %v", 
pro.Pid, err)
+                       continue
+               }
+               if finderConfig == nil {
+                       continue
+               }
+
+               // build the linux process and add to the list
+               ps := NewProcess(pro, cmdline, finderConfig)
+               ps.entity.Layer = finderConfig.Layer
+               ps.entity.ServiceName, err = p.buildEntity(err, ps, 
finderConfig.serviceNameBuilder)
+               ps.entity.InstanceName, err = p.buildEntity(err, ps, 
finderConfig.instanceNameBuilder)
+               ps.entity.ProcessName, err = p.buildEntity(err, ps, 
finderConfig.processNameBuilder)
+               if err != nil {
+                       log.Warnf("failed to build the process data for pid: 
%d, reason: %v", pro.Pid, err)
+                       continue
+               } else {
+                       findedProcesses = append(findedProcesses, ps)
+               }
+       }
+       if len(findedProcesses) == 0 {
+               return nil, nil
+       }
+       // remove duplicated(identity) process
+       identity2Processes := make(map[string][]*Process)
+       for _, ps := range findedProcesses {
+               id := ps.BuildIdentity()
+               if identity2Processes[id] == nil {
+                       identity2Processes[id] = make([]*Process, 0)
+               }
+               identity2Processes[id] = append(identity2Processes[id], ps)
+       }
+       result := make([]*Process, 0)
+       for _, psList := range identity2Processes {
+               reportProcess := psList[0]
+               if len(psList) > 1 {
+                       pidList := make([]int32, 0)
+                       for _, ps := range psList {
+                               pidList = append(pidList, ps.pid)
+                       }
+                       log.WithField("command_line", reportProcess.cmd).
+                               WithField("service_name", 
reportProcess.entity.ServiceName).
+                               WithField("instance_name", 
reportProcess.entity.InstanceName).
+                               WithField("process_name", 
reportProcess.entity.ProcessName).
+                               WithField("pid_list", pidList).
+                               Warnf("find multiple similar process in VM, " +
+                                       "only report the first of these 
processes. " +
+                                       "please update the name of process to 
identity them more clear.")
+               }
+               result = append(result, reportProcess)
+       }
+       return result, nil
+}
+
+func (p *ProcessFinder) buildEntity(err error, ps *Process, entity 
*base.TemplateBuilder) (string, error) {
+       if err != nil {
+               return "", err
+       }
+       return renderTemplate(entity, ps, p)
+}
+
+func (p *ProcessFinder) findMatchesFinder(ps *process.Process) 
(*ProcessFinderConfig, string, error) {
+       // verify the process exists, if not exists just return
+       if exists, err := process.PidExists(ps.Pid); err != nil {
+               return nil, "", err
+       } else if !exists {
+               return nil, "", nil
+       }
+
+       cmdline, err := ps.Cmdline()
+       if err != nil {
+               return nil, "", fmt.Errorf("query command line failure: %v", 
err)
+       }
+       var matched *ProcessFinderConfig
+       for _, finder := range p.conf.Finders {
+               if finder.commandlineRegex.MatchString(cmdline) {
+                       if matched == nil {
+                               matched = finder
+                       } else {
+                               log.Warnf("found multiple finder for the 
process %d, command line: %s, choose the first one mached to build process",
+                                       ps.Pid, cmdline)
+                               return matched, cmdline, nil
+                       }
+               }
+       }
+       return matched, cmdline, nil
+}
+
+func validateConfig(conf *Config) error {
+       if len(conf.Finders) == 0 {
+               return fmt.Errorf("must have one VM process finder")
+       }
+
+       // validate config
+       for _, f := range conf.Finders {
+               var err error
+               err = stringMustNotNull(err, "layer", f.Layer)
+               f.commandlineRegex, err = regexMustNotNull(err, 
"match_cmd_regex", f.MatchCommandRegex)
+               f.serviceNameBuilder, err = templateMustNotNull(err, 
"service_name", f.ServiceName)
+               f.instanceNameBuilder, err = templateMustNotNull(err, 
"instance_name", f.InstanceName)
+               f.processNameBuilder, err = templateMustNotNull(err, 
"process_name", f.ProcessName)
+
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func stringMustNotNull(err error, confKey, confValue string) error {
+       if err != nil {
+               return err
+       }
+       if confValue == "" {
+               return fmt.Errorf("the %s of VM process must be set", confKey)
+       }
+       return nil
+}
+
+func templateMustNotNull(err error, confKey, confValue string) 
(*base.TemplateBuilder, error) {
+       if err1 := stringMustNotNull(err, confKey, confValue); err1 != nil {
+               return nil, err1
+       }
+       return base.NewTemplateBuilder(confKey, confValue)
+}
+
+func regexMustNotNull(err error, confKey, confValue string) (*regexp.Regexp, 
error) {
+       if err1 := stringMustNotNull(err, confKey, confValue); err1 != nil {
+               return nil, err1
+       }
+       return regexp.Compile(confValue)
+}
diff --git a/pkg/process/finders/vm/process.go 
b/pkg/process/finders/vm/process.go
new file mode 100644
index 0000000..0973f1a
--- /dev/null
+++ b/pkg/process/finders/vm/process.go
@@ -0,0 +1,67 @@
+// 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 vm
+
+import (
+       "fmt"
+
+       "github.com/shirou/gopsutil/process"
+
+       "github.com/apache/skywalking-rover/pkg/process/api"
+       "github.com/apache/skywalking-rover/pkg/tools/profiling"
+)
+
+type Process struct {
+       // original reference
+       original     *process.Process
+       finderConfig *ProcessFinderConfig
+
+       // process data
+       pid       int32
+       cmd       string
+       profiling *profiling.Info
+
+       // entity for backend
+       entity *api.ProcessEntity
+}
+
+func NewProcess(p *process.Process, cmdline string, config 
*ProcessFinderConfig) *Process {
+       return &Process{original: p, pid: p.Pid, cmd: cmdline, finderConfig: 
config, entity: &api.ProcessEntity{}}
+}
+
+func (p *Process) Pid() int32 {
+       return p.pid
+}
+
+func (p *Process) Entity() *api.ProcessEntity {
+       return p.entity
+}
+
+func (p *Process) DetectType() api.ProcessDetectType {
+       return api.VM
+}
+
+func (p *Process) OriginalProcess() *process.Process {
+       return p.original
+}
+
+// BuildIdentity without pid
+func (p *Process) BuildIdentity() string {
+       return fmt.Sprintf("%s_%s_%s_%s", p.cmd, p.entity.ServiceName,
+               p.entity.InstanceName, p.entity.ProcessName)
+}
diff --git a/pkg/process/finders/vm/template.go 
b/pkg/process/finders/vm/template.go
new file mode 100644
index 0000000..7268421
--- /dev/null
+++ b/pkg/process/finders/vm/template.go
@@ -0,0 +1,56 @@
+// 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 vm
+
+import (
+       "fmt"
+
+       "github.com/apache/skywalking-rover/pkg/process/finders/base"
+)
+
+func renderTemplate(builder *base.TemplateBuilder, process *Process, finder 
*ProcessFinder) (string, error) {
+       moduleManager := finder.manager.GetModuleManager()
+       return builder.Execute(&TemplateContext{
+               Rover:   base.NewTemplateRover(moduleManager),
+               Process: base.NewTemplateProcess(moduleManager, process),
+               Finder:  &TemplateFinder{finder: finder, process: process},
+       })
+}
+
+type TemplateContext struct {
+       Rover   *base.TemplateRover
+       Process *base.TemplateProcess
+       Finder  *TemplateFinder
+}
+
+type TemplateFinder struct {
+       finder  *ProcessFinder
+       process *Process
+}
+
+func (t *TemplateFinder) Layer() string {
+       return t.process.finderConfig.Layer
+}
+
+func (t *TemplateFinder) RegexMatchGroup(inx int) (string, error) {
+       submatch := 
t.process.finderConfig.commandlineRegex.FindStringSubmatch(t.process.cmd)
+       if len(submatch) == 0 || inx+1 >= len(submatch) {
+               return "", fmt.Errorf("could not find match")
+       }
+       return submatch[inx], nil
+}
diff --git a/pkg/core/module.go b/pkg/process/mdoule.go
similarity index 68%
copy from pkg/core/module.go
copy to pkg/process/mdoule.go
index d044bfa..e7463c7 100644
--- a/pkg/core/module.go
+++ b/pkg/process/mdoule.go
@@ -15,23 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package core
+package process
 
 import (
        "context"
+       "time"
 
-       "github.com/apache/skywalking-rover/pkg/core/backend"
+       "github.com/apache/skywalking-rover/pkg/core"
        "github.com/apache/skywalking-rover/pkg/module"
-
-       "github.com/hashicorp/go-multierror"
+       "github.com/apache/skywalking-rover/pkg/process/finders"
 )
 
-const ModuleName = "core"
+const ModuleName = "process_discovery"
 
 type Module struct {
        config *Config
 
-       backendClient *backend.Client
+       manager *finders.ProcessManager
 }
 
 func NewModule() *Module {
@@ -43,7 +43,7 @@ func (m *Module) Name() string {
 }
 
 func (m *Module) RequiredModules() []string {
-       return nil
+       return []string{core.ModuleName}
 }
 
 func (m *Module) Config() module.ConfigInterface {
@@ -51,27 +51,24 @@ func (m *Module) Config() module.ConfigInterface {
 }
 
 func (m *Module) Start(ctx context.Context, mgr *module.Manager) error {
-       // backend client
-       if m.config.BackendConfig != nil {
-               m.backendClient = backend.NewClient(m.config.BackendConfig)
-               if err := m.backendClient.Start(ctx); err != nil {
-                       return err
-               }
+       period, err := time.ParseDuration(m.config.HeartbeatPeriod)
+       if err != nil {
+               return err
        }
+       processManager, err := finders.NewProcessManager(ctx, mgr, period, 
m.config.VM)
+       if err != nil {
+               return err
+       }
+       m.manager = processManager
+
        return nil
 }
 
-func (m *Module) Shutdown(ctx context.Context, mgr *module.Manager) error {
-       var result *multierror.Error
-       if m.backendClient != nil {
-               result = multierror.Append(result, m.backendClient.Stop())
-       }
-       return result.ErrorOrNil()
+func (m *Module) NotifyStartSuccess() {
+       // notify all finder to report processes
+       m.manager.Start()
 }
 
-func (m *Module) ClientGrpcOperator() backend.Operator {
-       if m.backendClient == nil {
-               return nil
-       }
-       return m.backendClient
+func (m *Module) Shutdown(ctx context.Context, mgr *module.Manager) error {
+       return m.manager.Shutdown()
 }
diff --git a/pkg/tools/ip.go b/pkg/tools/ip.go
new file mode 100644
index 0000000..b8e5718
--- /dev/null
+++ b/pkg/tools/ip.go
@@ -0,0 +1,153 @@
+// 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 tools
+
+import (
+       "fmt"
+       "net"
+       "os"
+)
+
+var (
+       host *hostInfo
+)
+
+func init() {
+       host = queryHostInfo()
+}
+
+// DefaultHostIPAddress IP address of machine
+func DefaultHostIPAddress() string {
+       return host.defaultIPAddr
+}
+
+// HostIPAddressV4 found the IPV4 address from appoint net interface name
+func HostIPAddressV4(name string) string {
+       address := host.ipAddresses[name]
+       if address == nil {
+               return ""
+       }
+       return address.ipV4
+}
+
+// HostIPAddressV6 found the IPV6 address from appoint net interface name
+func HostIPAddressV6(name string) string {
+       address := host.ipAddresses[name]
+       if address == nil {
+               return ""
+       }
+       return address.ipV6
+}
+
+// Hostname of machine
+func Hostname() string {
+       return host.name
+}
+
+type hostInfo struct {
+       // hostname
+       name string
+       // ip address
+       ipAddresses   map[string]*hostIPAddress
+       defaultIPAddr string
+}
+
+type hostIPAddress struct {
+       ipV4 string
+       ipV6 string
+}
+
+func queryHostInfo() *hostInfo {
+       addresses, def, err := localIPAddress0()
+       if err != nil {
+               panic(err)
+       }
+       name, err := hostname0()
+       if err != nil {
+               panic(err)
+       }
+       return &hostInfo{name: name, ipAddresses: addresses, defaultIPAddr: def}
+}
+
+func hostname0() (string, error) {
+       return os.Hostname()
+}
+
+func localIPAddress0() (ipAddresses map[string]*hostIPAddress, defAddr string, 
err error) {
+       ifaces, err := net.Interfaces()
+       if err != nil {
+               return nil, "", err
+       }
+       // handle err
+       ipAddresses = make(map[string]*hostIPAddress)
+       var defV4, defV6 string
+       for _, i := range ifaces {
+               if i.Flags&net.FlagLoopback != 0 {
+                       continue
+               }
+               addrs, err := i.Addrs()
+               if err != nil {
+                       return nil, "", err
+               }
+               ipv4, ipv6 := analyzeIPAddresses(addrs)
+
+               if ipv4 != "" || ipv6 != "" {
+                       if defV4 == "" {
+                               defV4 = ipv4
+                       }
+                       if defV6 == "" {
+                               defV6 = ipv6
+                       }
+                       ipAddresses[i.Name] = &hostIPAddress{ipV4: ipv4, ipV6: 
ipv6}
+               }
+       }
+
+       if len(ipAddresses) == 0 {
+               return nil, "", fmt.Errorf("not found")
+       }
+
+       if defV4 != "" {
+               defAddr = defV4
+       } else {
+               defAddr = defV6
+       }
+
+       return ipAddresses, defAddr, nil
+}
+
+func analyzeIPAddresses(addrs []net.Addr) (ipv4, ipv6 string) {
+       for _, addr := range addrs {
+               var ip net.IP
+               switch v := addr.(type) {
+               case *net.IPNet:
+                       ip = v.IP
+               case *net.IPAddr:
+                       ip = v.IP
+               }
+               if ip.IsLoopback() {
+                       continue
+               }
+               if ip.To4() != nil {
+                       ipv4 = ip.To4().String()
+               }
+               if ip.To16() != nil {
+                       ipv6 = ip.To16().String()
+               }
+       }
+       return ipv4, ipv6
+}
diff --git a/pkg/tools/process.go b/pkg/tools/process.go
new file mode 100644
index 0000000..46910ff
--- /dev/null
+++ b/pkg/tools/process.go
@@ -0,0 +1,95 @@
+// 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 tools
+
+import (
+       "fmt"
+       "os"
+       "sort"
+       "strings"
+
+       "github.com/apache/skywalking-rover/pkg/tools/profiling"
+)
+
+var (
+       // NotSupportProfilingExe mean which program are not support for 
profiling
+       // Not Support JIT and Script language for now
+       NotSupportProfilingExe = []string{
+               "java", "python", "node", "bash", "ruby", "ssh",
+       }
+
+       // executable file profiling finders
+       profilingStatFinderList = []profiling.StatFinder{
+               profiling.NewGoLibrary(), profiling.NewObjDump(),
+       }
+
+       // kernel profiling finder
+       kernelFinder = profiling.NewKernelFinder()
+)
+
+// KernelFileProfilingStat is works for read the kernel and get is support for 
kernel symbol analyze
+func KernelFileProfilingStat() (*profiling.Info, error) {
+       if !kernelFinder.IsSupport(profiling.KernelSymbolFilePath) {
+               return nil, fmt.Errorf("not support kernel space profiling")
+       }
+       return kernelFinder.Analyze(profiling.KernelSymbolFilePath)
+}
+
+// ExecutableFileProfilingStat is validating the exe file could be profiling 
and get info
+func ExecutableFileProfilingStat(exePath string) (*profiling.Info, error) {
+       stat, err := os.Stat(exePath)
+       if err != nil {
+               return nil, fmt.Errorf("check file error: %v", err)
+       }
+       for _, notSupport := range NotSupportProfilingExe {
+               if strings.HasPrefix(stat.Name(), notSupport) {
+                       return nil, fmt.Errorf("not support %s language 
profiling", notSupport)
+               }
+       }
+
+       var lastError error
+       for _, finder := range profilingStatFinderList {
+               if finder.IsSupport(exePath) {
+                       if r, err1 := analyzeByFinder(exePath, finder); err1 == 
nil {
+                               return r, nil
+                       }
+                       lastError = err
+               }
+       }
+
+       if lastError == nil {
+               lastError = fmt.Errorf("could not found library to analyze the 
file")
+       }
+
+       return nil, lastError
+}
+
+func analyzeByFinder(exePath string, finder profiling.StatFinder) 
(*profiling.Info, error) {
+       // do analyze
+       info, err := finder.Analyze(exePath)
+       if err != nil {
+               return nil, err
+       }
+
+       // order the symbols by address
+       sort.SliceStable(info.Symbols, func(i, j int) bool {
+               return info.Symbols[i].Location < info.Symbols[j].Location
+       })
+
+       return info, nil
+}
diff --git a/pkg/tools/profiling/api.go b/pkg/tools/profiling/api.go
new file mode 100644
index 0000000..512fe6d
--- /dev/null
+++ b/pkg/tools/profiling/api.go
@@ -0,0 +1,64 @@
+// 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
+
+var KernelSymbolFilePath = "/proc/kallsyms"
+
+// Info of profiling process
+type Info struct {
+       Symbols []*Symbol
+}
+
+// Symbol of executable file
+type Symbol struct {
+       Name     string
+       Location uint64
+}
+
+type StatFinder interface {
+       // IsSupport to stat the executable file for profiling
+       IsSupport(filePath string) bool
+       // Analyze the executable file
+       Analyze(filePath string) (*Info, error)
+}
+
+// FindSymbolName by address
+func (i *Info) FindSymbolName(address uint64) string {
+       symbols := i.Symbols
+
+       start := 0
+       end := len(symbols)
+       for start < end {
+               mid := start + (end-start)/2
+               result := int64(address) - int64(symbols[mid].Location)
+
+               if result < 0 {
+                       end = mid
+               } else if result > 0 {
+                       start = mid + 1
+               } else {
+                       return symbols[mid].Name
+               }
+       }
+
+       if start >= 1 && symbols[start-1].Location < address && address < 
symbols[start].Location {
+               return symbols[start-1].Name
+       }
+
+       return ""
+}
diff --git a/pkg/boot/register.go b/pkg/tools/profiling/go_library.go
similarity index 50%
copy from pkg/boot/register.go
copy to pkg/tools/profiling/go_library.go
index d77d5c2..c5cd3d2 100644
--- a/pkg/boot/register.go
+++ b/pkg/tools/profiling/go_library.go
@@ -15,14 +15,44 @@
 // 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"
+       "debug/elf"
+       "fmt"
 )
 
-func init() {
-       // register all active module
-       module.Register(core.NewModule())
+// GoLibrary is using build-in elf reader to read
+type GoLibrary struct {
+}
+
+func NewGoLibrary() *GoLibrary {
+       return &GoLibrary{}
+}
+
+func (l *GoLibrary) IsSupport(filePath string) bool {
+       return true
+}
+
+func (l *GoLibrary) Analyze(filePath string) (*Info, error) {
+       // read els file
+       file, err := elf.Open(filePath)
+       if err != nil {
+               return nil, fmt.Errorf("read ELF file error: %v", err)
+       }
+       defer file.Close()
+
+       // exist symbol data
+       symbols, err := file.Symbols()
+       if err != nil || len(symbols) == 0 {
+               return nil, fmt.Errorf("read symbol data failure or no symbole 
data: %v", err)
+       }
+
+       // adapt symbol struct
+       data := make([]*Symbol, len(symbols))
+       for i, sym := range symbols {
+               data[i] = &Symbol{Name: sym.Name, Location: sym.Value}
+       }
+
+       return &Info{Symbols: data}, nil
 }
diff --git a/pkg/tools/profiling/kernel.go b/pkg/tools/profiling/kernel.go
new file mode 100644
index 0000000..3285463
--- /dev/null
+++ b/pkg/tools/profiling/kernel.go
@@ -0,0 +1,67 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package profiling
+
+import (
+       "bufio"
+       "fmt"
+       "os"
+       "strconv"
+       "strings"
+)
+
+type KernelFinder struct {
+       kernelFileExists bool
+}
+
+func NewKernelFinder() *KernelFinder {
+       stat, _ := os.Stat(KernelSymbolFilePath)
+       return &KernelFinder{kernelFileExists: stat != nil}
+}
+
+func (k *KernelFinder) IsSupport(filepath string) bool {
+       if filepath != KernelSymbolFilePath {
+               return false
+       }
+       stat, _ := os.Stat(filepath)
+       return stat != nil
+}
+
+func (k *KernelFinder) Analyze(filepath string) (*Info, error) {
+       kernelPath, err := os.Open(filepath)
+       if err != nil {
+               return nil, err
+       }
+
+       scanner := bufio.NewScanner(kernelPath)
+       symbols := make([]*Symbol, 0)
+       for scanner.Scan() {
+               info := strings.Split(scanner.Text(), " ")
+               atoi, err := strconv.ParseUint(info[0], 16, 64)
+
+               if err != nil {
+                       return nil, fmt.Errorf("error read addr: %s, %v", 
info[0], err)
+               }
+               symbols = append(symbols, &Symbol{
+                       Name:     info[2],
+                       Location: atoi,
+               })
+       }
+
+       return &Info{Symbols: symbols}, nil
+}
diff --git a/pkg/tools/profiling/objdump.go b/pkg/tools/profiling/objdump.go
new file mode 100644
index 0000000..4783760
--- /dev/null
+++ b/pkg/tools/profiling/objdump.go
@@ -0,0 +1,66 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package profiling
+
+import (
+       "bufio"
+       "bytes"
+       "os/exec"
+       "regexp"
+       "strconv"
+)
+
+var objDumpOutputFormat = 
"^([0-9a-f]+)\\s+\\w?\\s+\\w?\\s+\\S+\\s*[0-9a-f]*\\s+(\\S+)$"
+
+// ObjDump is using `objdump` command to read
+type ObjDump struct {
+       commandPath string
+       outputRegex *regexp.Regexp
+}
+
+func NewObjDump() *ObjDump {
+       path, _ := exec.LookPath("objdump")
+       compile := regexp.MustCompile(objDumpOutputFormat)
+       return &ObjDump{commandPath: path, outputRegex: compile}
+}
+
+func (o *ObjDump) IsSupport(filepath string) bool {
+       return o.commandPath != ""
+}
+
+func (o *ObjDump) Analyze(filepath string) (*Info, error) {
+       resBytes, err := exec.Command(o.commandPath, "--syms", 
filepath).Output() // #nosec G204
+       if err != nil {
+               return nil, err
+       }
+
+       symbols := make([]*Symbol, 0)
+       scanner := bufio.NewScanner(bytes.NewReader(resBytes))
+       for scanner.Scan() {
+               submatch := o.outputRegex.FindStringSubmatch(scanner.Text())
+               if len(submatch) == 0 {
+                       continue
+               }
+               atoi, err := strconv.ParseUint(submatch[1], 16, 64)
+               if err != nil {
+                       continue
+               }
+               symbols = append(symbols, &Symbol{Name: submatch[2], Location: 
atoi})
+       }
+       return &Info{Symbols: symbols}, nil
+}

Reply via email to