You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@skywalking.apache.org by wu...@apache.org on 2022/03/01 07:26:10 UTC
[skywalking-rover] branch main updated: Support detect VM level process (#5)
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 <74...@qq.com>
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
+}