This is an automated email from the ASF dual-hosted git repository.
liuhan 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 8f8f7ef Support nodejs SSL (#48)
8f8f7ef is described below
commit 8f8f7ef6df7a8c82e9686e2f27b513d3f23fa58d
Author: mrproliu <[email protected]>
AuthorDate: Wed Aug 24 17:12:39 2022 +0800
Support nodejs SSL (#48)
---
.github/workflows/rover.yaml | 2 +
CHANGES.md | 2 +-
bpf/profiling/network/netmonitor.c | 3 +-
bpf/profiling/network/node_tls.c | 50 +++++
bpf/profiling/network/node_tls.h | 79 ++++++++
bpf/profiling/network/openssl.c | 6 +
docs/en/setup/configuration/profiling.md | 2 +-
pkg/process/finders/kubernetes/finder.go | 11 +-
pkg/profiling/task/network/linker.go | 7 +
pkg/profiling/task/network/ssl.go | 213 ++++++++++++++++++---
pkg/tools/elf/class.go | 50 +++++
pkg/tools/elf/dwarf.go | 45 +++++
pkg/tools/elf/dwarf_init.go | 96 +++++++++-
pkg/tools/offsets/nodejs.go | 77 ++++++++
pkg/tools/version/version.go | 80 ++++++++
scripts/debug/nodejs/tls_gen_offsets.go | 37 ++++
scripts/debug/nodejs/tls_gen_offsets.sh | 44 +++++
test/e2e/cases/process/istio/kind.yaml | 4 +
test/e2e/cases/profiling/network/nodejs/Dockerfile | 27 +++
.../profiling/network/nodejs/docker-compose.yml | 64 +++++++
.../network/nodejs/e2e.yaml} | 41 ++--
test/e2e/cases/profiling/network/nodejs/service.js | 52 +++++
22 files changed, 954 insertions(+), 38 deletions(-)
diff --git a/.github/workflows/rover.yaml b/.github/workflows/rover.yaml
index 07eda2a..72f1a43 100644
--- a/.github/workflows/rover.yaml
+++ b/.github/workflows/rover.yaml
@@ -161,6 +161,8 @@ jobs:
env: ISTIO_VERSION=1.13.1
- name: C++ Profiling
config: test/e2e/cases/profiling/network/c_plus_plus/e2e.yaml
+ - name: Nodejs Profiling
+ config: test/e2e/cases/profiling/network/nodejs/e2e.yaml
steps:
- uses: actions/checkout@v2
with:
diff --git a/CHANGES.md b/CHANGES.md
index 5778fff..f752ecd 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,7 +7,7 @@ Release Notes.
#### Features
* Support `NETWORK` Profiling.
* Let the logger as a configurable module.
-* Support analyze the data of OpenSSL, BoringSSL library, GoTLS in `NETWORK`
Profiling.
+* Support analyze the data of OpenSSL, BoringSSL library, GoTLS, NodeTLS in
`NETWORK` Profiling.
* Enhancing the kubernetes process finder.
#### Bug Fixes
diff --git a/bpf/profiling/network/netmonitor.c
b/bpf/profiling/network/netmonitor.c
index 233a51b..e262566 100644
--- a/bpf/profiling/network/netmonitor.c
+++ b/bpf/profiling/network/netmonitor.c
@@ -1130,4 +1130,5 @@ int tcp_drop(struct pt_regs *ctx) {
}
#include "openssl.c"
-#include "go_tls.c"
\ No newline at end of file
+#include "go_tls.c"
+#include "node_tls.c"
\ No newline at end of file
diff --git a/bpf/profiling/network/node_tls.c b/bpf/profiling/network/node_tls.c
new file mode 100644
index 0000000..6f293d2
--- /dev/null
+++ b/bpf/profiling/network/node_tls.c
@@ -0,0 +1,50 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "node_tls.h"
+
+SEC("uprobe/node_tls_wrap")
+int node_tls_wrap(struct pt_regs* ctx) {
+ void* tls_wrap = (void*)PT_REGS_PARM1(ctx);
+ __u64 id = bpf_get_current_pid_tgid();
+ bpf_map_update_elem(&node_tls_wrap_construct_args_map, &id, &tls_wrap, 0);
+ return 0;
+}
+
+SEC("uretprobe/node_tls_wrap")
+int node_tls_wrap_ret(struct pt_regs* ctx) {
+ __u64 id = bpf_get_current_pid_tgid();
+ bpf_map_delete_elem(&node_tls_wrap_construct_args_map, &id);
+ return 0;
+}
+
+SEC("uretprobe/ssl_new")
+int node_tls_ret_ssl(struct pt_regs* ctx) {
+ // get ssl data
+ void* ssl = (void*)PT_REGS_RC(ctx);
+ if (ssl == NULL) {
+ return 0;
+ }
+ // get node tls_wrap
+ __u64 id = bpf_get_current_pid_tgid();
+ struct ssl_wrap **wrap =
bpf_map_lookup_elem(&node_tls_wrap_construct_args_map, &id);
+ if (wrap == NULL) {
+ return 0;
+ }
+ bpf_map_update_elem(&node_tls_ssl_wrap_mapping, &ssl, wrap, 0);
+ return 0;
+}
\ No newline at end of file
diff --git a/bpf/profiling/network/node_tls.h b/bpf/profiling/network/node_tls.h
new file mode 100644
index 0000000..b51472d
--- /dev/null
+++ b/bpf/profiling/network/node_tls.h
@@ -0,0 +1,79 @@
+// 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.
+
+#pragma once
+
+struct node_tls_symaddr_t {
+ __u32 tlsWrapStreamListenerOffset;
+ __u32 streamListenerStreamOffset;
+ __u32 streamBaseStreamResourceOffset;
+ __u32 libuvStreamWrapStreamBaseOffset;
+ __u32 libuvStreamWrapStreamOffset;
+ __u32 uvStreamSIOWatcherOffset;
+ __u32 uvIOSFDOffset;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 10000);
+ __type(key, __u64);
+ __type(value, void*);
+} node_tls_wrap_construct_args_map SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 10000);
+ __type(key, void*);
+ __type(value, void*);
+} node_tls_ssl_wrap_mapping SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 10000);
+ __type(key, __u32);
+ __type(value, struct node_tls_symaddr_t);
+} node_tls_symaddr_map SEC(".maps");
+static __inline int get_node_tls_wrap_fd(__u32 tgid, void *tlsWrap) {
+ struct node_tls_symaddr_t *addr =
bpf_map_lookup_elem(&node_tls_symaddr_map, &tgid);
+ if (addr == NULL) {
+ return -1;
+ }
+
+ void *stream = NULL;
+ bpf_probe_read(&stream, sizeof(stream), tlsWrap +
addr->tlsWrapStreamListenerOffset + addr->streamListenerStreamOffset);
+ if (stream == NULL) {
+ return -1;
+ }
+
+ void *uv_stream = NULL;
+ bpf_probe_read(&uv_stream, sizeof(uv_stream), stream -
addr->streamBaseStreamResourceOffset - addr->libuvStreamWrapStreamBaseOffset +
+
addr->libuvStreamWrapStreamOffset);
+ if (uv_stream == NULL) {
+ return -1;
+ }
+
+ __u64 fd;
+ bpf_probe_read(&fd, sizeof(fd), uv_stream + addr->uvStreamSIOWatcherOffset
+ addr->uvIOSFDOffset);
+ return fd;
+}
+static __inline int get_node_tls_fd(__u32 tgid, void *ssl) {
+ void **tlsWrap = bpf_map_lookup_elem(&node_tls_ssl_wrap_mapping, &ssl);
+ if (tlsWrap == NULL) {
+ return -1;
+ }
+ return get_node_tls_wrap_fd(tgid, *tlsWrap);
+}
\ No newline at end of file
diff --git a/bpf/profiling/network/openssl.c b/bpf/profiling/network/openssl.c
index 124c34d..8723232 100644
--- a/bpf/profiling/network/openssl.c
+++ b/bpf/profiling/network/openssl.c
@@ -16,6 +16,7 @@
// under the License.
#include "openssl.h"
+#include "node_tls.h"
static __inline void process_openssl_data(struct pt_regs* ctx, __u64 id, __u32
data_direction, struct sock_data_args_t* args, __u32 func_name) {
int bytes_count = PT_REGS_RC(ctx);
@@ -46,6 +47,11 @@ static int get_fd(uint32_t tgid, bool read, void* ssl) {
return fd;
}
+ fd = get_node_tls_fd(tgid, ssl);
+ if (fd > 0) {
+ return fd;
+ }
+
return 0;
}
diff --git a/docs/en/setup/configuration/profiling.md
b/docs/en/setup/configuration/profiling.md
index 94d9674..8513b31 100644
--- a/docs/en/setup/configuration/profiling.md
+++ b/docs/en/setup/configuration/profiling.md
@@ -30,7 +30,7 @@ Off CPU Profiling task is attach the `finish_task_switch` in
`krobe` to profilin
### Network
Network Profiling task is intercept IO-related syscall and `urprobe` in
process to identify the network traffic and generate the metrics.
-Also, the following protocol are supported for analyzing using OpenSSL
library, BoringSSL library, GoTLS or plaintext:
+Also, the following protocol are supported for analyzing using OpenSSL
library, BoringSSL library, GoTLS, NodeTLS or plaintext:
1. HTTP
2. MySQL
diff --git a/pkg/process/finders/kubernetes/finder.go
b/pkg/process/finders/kubernetes/finder.go
index bf9feb1..c77b877 100644
--- a/pkg/process/finders/kubernetes/finder.go
+++ b/pkg/process/finders/kubernetes/finder.go
@@ -22,6 +22,7 @@ import (
"context"
"fmt"
"os"
+ "regexp"
"strconv"
"strings"
"time"
@@ -48,6 +49,8 @@ import (
var log = logger.GetLogger("process", "finder", "kubernetes")
+var kubepodsRegex = regexp.MustCompile(`cri-containerd-(?P<Group>\w+)\.scope`)
+
type ProcessFinder struct {
conf *Config
@@ -267,7 +270,13 @@ func (f *ProcessFinder) getProcessCGroup(pid int32)
([]string, error) {
}
lastPath := strings.LastIndex(infos[2], "/")
if lastPath > 1 && lastPath != len(infos[2])-1 {
- cache[infos[2][lastPath+1:]] = true
+ path := infos[2][lastPath+1:]
+ // adapt Kubepod
+ // ex:
cri-containerd-7dae778c37bd1204677518f1032bbecf01f5c41878ea7bd370021263417cc626.scope
+ if kubepod := kubepodsRegex.FindStringSubmatch(path);
len(kubepod) >= 1 {
+ path = kubepod[1]
+ }
+ cache[path] = true
}
}
if len(cache) == 0 {
diff --git a/pkg/profiling/task/network/linker.go
b/pkg/profiling/task/network/linker.go
index 36995d2..c8cdf1e 100644
--- a/pkg/profiling/task/network/linker.go
+++ b/pkg/profiling/task/network/linker.go
@@ -196,6 +196,13 @@ func (u *UProbeExeFile) AddLink(symbol string, enter, exit
*ebpf.Program) {
u.AddLinkWithType(symbol, false, exit)
}
+func (u *UProbeExeFile) AddLinkWithSymbols(symbol []string, enter, exit
*ebpf.Program) {
+ for _, s := range symbol {
+ u.AddLinkWithType(s, true, enter)
+ u.AddLinkWithType(s, false, exit)
+ }
+}
+
func (u *UProbeExeFile) AddGoLink(symbol string, enter, exit *ebpf.Program,
elfFile *elf.File) {
u.AddGoLinkWithType(symbol, true, enter, elfFile)
u.AddGoLinkWithType(symbol, false, exit, elfFile)
diff --git a/pkg/profiling/task/network/ssl.go
b/pkg/profiling/task/network/ssl.go
index 0f5944e..9adb7c8 100644
--- a/pkg/profiling/task/network/ssl.go
+++ b/pkg/profiling/task/network/ssl.go
@@ -31,6 +31,7 @@ import (
"github.com/apache/skywalking-rover/pkg/tools/host"
"github.com/apache/skywalking-rover/pkg/tools/path"
"github.com/apache/skywalking-rover/pkg/tools/profiling"
+ "github.com/apache/skywalking-rover/pkg/tools/version"
)
var (
@@ -42,6 +43,7 @@ var (
goTLSPollFDSymbol = "internal/poll.FD"
goTLSConnSymbol = "crypto/tls.Conn"
goTLSRuntimeG = "runtime.g"
+ nodeVersionRegex =
regexp.MustCompile(`^node\.js/v(?P<Major>\d+)\.(?P<Minor>\d+)\.(?P<Patch>\d+)$`)
)
type OpenSSLFdSymAddrConfigInBPF struct {
@@ -71,6 +73,11 @@ func addSSLProcess(pid int, bpf *bpfObjects, linker *Linker)
error {
return err1
}
+ // Nodejs
+ if err1 := processNodeProcess(pid, bpf, linker, modules); err1 != nil {
+ return err1
+ }
+
return nil
}
@@ -105,7 +112,11 @@ func processOpenSSLProcess(pid int, bpf *bpfObjects,
linker *Linker, modules []*
}
// attach the linker
- libSSLLinker := linker.OpenUProbeExeFile(libsslPath)
+ return processOpenSSLModule(bpf, processModules[libsslName], linker)
+}
+
+func processOpenSSLModule(bpf *bpfObjects, libSSLModule *profiling.Module,
linker *Linker) error {
+ libSSLLinker := linker.OpenUProbeExeFile(libSSLModule.Path)
libSSLLinker.AddLink("SSL_write", bpf.OpensslWrite, bpf.OpensslWriteRet)
libSSLLinker.AddLink("SSL_read", bpf.OpensslRead, bpf.OpensslReadRet)
return linker.HasError()
@@ -194,13 +205,13 @@ func processGoProcess(pid int, bpf *bpfObjects, linker
*Linker, modules []*profi
}
defer elfFile.Close()
- _, minor, err := getGoVersion(elfFile, buildVersionSymbol)
+ v, err := getGoVersion(elfFile, buildVersionSymbol)
if err != nil {
return err
}
// generate symbol offsets
- symbolConfig, elfFile, err := generateGOTLSSymbolOffsets(modules, pid,
elfFile, minor)
+ symbolConfig, elfFile, err := generateGOTLSSymbolOffsets(modules, pid,
elfFile, v)
if err != nil {
return err
}
@@ -222,40 +233,177 @@ func processGoProcess(pid int, bpf *bpfObjects, linker
*Linker, modules []*profi
return linker.HasError()
}
-func getGoVersion(elfFile *elf.File, versionSymbol *profiling.Symbol) (major,
minor int, err error) {
+func processNodeProcess(pid int, bpf *bpfObjects, linker *Linker, modules
[]*profiling.Module) error {
+ moduleName1, moduleName2, libsslName := "/nodejs", "/node", "libssl.so"
+ processModules, err := findProcessModules(modules, moduleName1,
moduleName2, libsslName)
+ if err != nil {
+ return err
+ }
+ nodeModule := processModules[moduleName1]
+ libsslModule := processModules[libsslName]
+ needsReAttachSSL := false
+ if nodeModule == nil {
+ nodeModule = processModules[moduleName2]
+ }
+ if nodeModule == nil {
+ log.Debugf("current process is not nodejs program, so won't add
the nodejs protos. pid: %d", pid)
+ return nil
+ }
+ if libsslModule == nil {
+ if searchSymbol([]*profiling.Module{nodeModule}, func(a, b
string) bool {
+ return a == b
+ }, "SSL_read") == nil ||
searchSymbol([]*profiling.Module{nodeModule}, func(a, b string) bool {
+ return a == b
+ }, "SSL_write") == nil {
+ log.Warnf("could not found the SSL_read/SSL_write under
the nodejs program, so ignore. pid: %d", pid)
+ return nil
+ }
+ libsslModule = nodeModule
+ needsReAttachSSL = true
+ }
+ v, err := getNodeVersion(nodeModule.Path)
+ if err != nil {
+ return fmt.Errorf("read nodejs version failure, pid: %d, error:
%v", pid, err)
+ }
+ log.Debugf("read the nodejs version, pid: %d, version: %s", pid, v)
+ config, err := findNodeTLSAddrConfig(v)
+ if err != nil {
+ return err
+ }
+ // setting the locations
+ if err := bpf.NodeTlsSymaddrMap.Put(uint32(pid), config); err != nil {
+ return fmt.Errorf("setting the node TLS location failure, pid:
%d, error: %v", pid, err)
+ }
+ // register node tls
+ if err := registerNodeTLSProbes(v, bpf, linker, nodeModule,
libsslModule); err != nil {
+ return fmt.Errorf("register node TLS probes failure, pid: %d,
error: %v", pid, err)
+ }
+ // attach the OpenSSL Probe if needs
+ if needsReAttachSSL {
+ return processOpenSSLModule(bpf, libsslModule, linker)
+ }
+ return nil
+}
+
+var nodeTLSAddrWithVersions = []struct {
+ v *version.Version
+ conf *NodeTLSAddrInBPF
+}{
+ {version.Build(10, 19, 0), &NodeTLSAddrInBPF{0x0130, 0x08, 0x00, 0x50,
0x90, 0x88, 0x30}},
+ {version.Build(12, 3, 1), &NodeTLSAddrInBPF{0x0130, 0x08, 0x00, 0x50,
0x90, 0x88, 0x30}},
+ {version.Build(12, 16, 2), &NodeTLSAddrInBPF{0x0138, 0x08, 0x00, 0x58,
0x98, 0x88, 0x30}},
+ {version.Build(13, 0, 0), &NodeTLSAddrInBPF{0x0130, 0x08, 0x00, 0x50,
0x90, 0x88, 0x30}},
+ {version.Build(13, 2, 0), &NodeTLSAddrInBPF{0x0130, 0x08, 0x00, 0x58,
0x98, 0x88, 0x30}},
+ {version.Build(13, 10, 1), &NodeTLSAddrInBPF{0x0140, 0x08, 0x00, 0x60,
0xa0, 0x88, 0x30}},
+ {version.Build(14, 5, 0), &NodeTLSAddrInBPF{0x138, 0x08, 0x00, 0x58,
0x98, 0x88, 0x30}},
+ {version.Build(15, 0, 0), &NodeTLSAddrInBPF{0x78, 0x08, 0x00, 0x58,
0x98, 0x88, 0x30}},
+}
+
+var nodeTLSProbeWithVersions = []struct {
+ v *version.Version
+ f func(uprobe *UProbeExeFile, bpf *bpfObjects, nodeModule
*profiling.Module)
+}{
+ {version.Build(10, 19, 0), func(uprobe *UProbeExeFile, bpf *bpfObjects,
nodeModule *profiling.Module) {
+
uprobe.AddLinkWithSymbols(searchSymbolNames([]*profiling.Module{nodeModule},
strings.HasPrefix, "_ZN4node7TLSWrapC2E"),
+ bpf.NodeTlsWrap, bpf.NodeTlsWrapRet)
+
uprobe.AddLinkWithSymbols(searchSymbolNames([]*profiling.Module{nodeModule},
strings.HasPrefix, "_ZN4node7TLSWrap7ClearInE"),
+ bpf.NodeTlsWrap, bpf.NodeTlsWrapRet)
+
uprobe.AddLinkWithSymbols(searchSymbolNames([]*profiling.Module{nodeModule},
strings.HasPrefix, "_ZN4node7TLSWrap8ClearOutE"),
+ bpf.NodeTlsWrap, bpf.NodeTlsWrapRet)
+ }},
+ {version.Build(15, 0, 0), func(uprobe *UProbeExeFile, bpf *bpfObjects,
nodeModule *profiling.Module) {
+
uprobe.AddLinkWithSymbols(searchSymbolNames([]*profiling.Module{nodeModule},
strings.HasPrefix, "_ZN4node6crypto7TLSWrapC2E"),
+ bpf.NodeTlsWrap, bpf.NodeTlsWrapRet)
+
uprobe.AddLinkWithSymbols(searchSymbolNames([]*profiling.Module{nodeModule},
strings.HasPrefix, "_ZN4node6crypto7TLSWrap7ClearInE"),
+ bpf.NodeTlsWrap, bpf.NodeTlsWrapRet)
+
uprobe.AddLinkWithSymbols(searchSymbolNames([]*profiling.Module{nodeModule},
strings.HasPrefix, "_ZN4node6crypto7TLSWrap8ClearOutE"),
+ bpf.NodeTlsWrap, bpf.NodeTlsWrapRet)
+ }},
+}
+
+type NodeTLSAddrInBPF struct {
+ TLSWrapStreamListenerOffset uint32
+ StreamListenerStreamOffset uint32
+ StreamBaseStreamResourceOffset uint32
+ LibuvStreamWrapStreamBaseOffset uint32
+ LibuvStreamWrapStreamOffset uint32
+ UVStreamSIOWatcherOffset uint32
+ UVIOSFDOffset uint32
+}
+
+func findNodeTLSAddrConfig(v *version.Version) (*NodeTLSAddrInBPF, error) {
+ var lastest *NodeTLSAddrInBPF
+ for _, c := range nodeTLSAddrWithVersions {
+ if v.GreaterOrEquals(c.v) {
+ lastest = c.conf
+ }
+ }
+ if lastest != nil {
+ return lastest, nil
+ }
+ return nil, fmt.Errorf("could not support version: %s", v)
+}
+
+func registerNodeTLSProbes(v *version.Version, bpf *bpfObjects, linker
*Linker, nodeModule, libSSLModule *profiling.Module) error {
+ var probeFunc func(uprobe *UProbeExeFile, bpf *bpfObjects, nodeModule
*profiling.Module)
+ for _, c := range nodeTLSProbeWithVersions {
+ if v.GreaterOrEquals(c.v) {
+ probeFunc = c.f
+ }
+ }
+ if probeFunc == nil {
+ return fmt.Errorf("the version is not support: %v", v)
+ }
+ file := linker.OpenUProbeExeFile(nodeModule.Path)
+ probeFunc(file, bpf, nodeModule)
+
+ // find the SSL_new, and register
+ file = linker.OpenUProbeExeFile(libSSLModule.Path)
+ file.AddLinkWithType("SSL_new", false, bpf.NodeTlsRetSsl)
+ return linker.HasError()
+}
+
+func getNodeVersion(p string) (*version.Version, error) {
+ result, err := exec.Command("strings", p).Output()
+ if err != nil {
+ return nil, err
+ }
+ for _, d := range strings.Split(string(result), "\n") {
+ versionInfo :=
nodeVersionRegex.FindStringSubmatch(strings.TrimSpace(d))
+ if len(versionInfo) != 4 {
+ continue
+ }
+ return version.Read(versionInfo[1], versionInfo[2],
versionInfo[3])
+ }
+
+ return nil, fmt.Errorf("nodejs version is not found")
+}
+
+func getGoVersion(elfFile *elf.File, versionSymbol *profiling.Symbol)
(*version.Version, error) {
buffer, err := elfFile.ReadSymbolData(".data", versionSymbol.Location,
versionSymbol.Size)
if err != nil {
- return 0, 0, fmt.Errorf("reading go version struct info
failure: %v", err)
+ return nil, fmt.Errorf("reading go version struct info failure:
%v", err)
}
var t = GoStringInC{}
buf := bytes.NewReader(buffer)
err = binary.Read(buf, binary.LittleEndian, &t)
if err != nil {
- return 0, 0, fmt.Errorf("read the go structure failure: %v",
err)
+ return nil, fmt.Errorf("read the go structure failure: %v", err)
}
buffer, err = elfFile.ReadSymbolData(".data", t.Ptr, t.Size)
if err != nil {
- return 0, 0, fmt.Errorf("read the go version failure: %v", err)
+ return nil, fmt.Errorf("read the go version failure: %v", err)
}
// parse versions
submatch := goVersionRegex.FindStringSubmatch(string(buffer))
if len(submatch) != 3 {
- return 0, 0, fmt.Errorf("the go version is failure to identify,
version: %s", string(buffer))
- }
- major, err = strconv.Atoi(submatch[1])
- if err != nil {
- return 0, 0, fmt.Errorf("the marjor version is a number,
version: %s", string(buffer))
- }
- minor, err = strconv.Atoi(submatch[2])
- if err != nil {
- return 0, 0, fmt.Errorf("the minor version is a number,
version: %s", string(buffer))
+ return nil, fmt.Errorf("the go version is failure to identify,
version: %s", string(buffer))
}
-
- return major, minor, nil
+ return version.Read(submatch[1], submatch[2], "")
}
-func generateGOTLSSymbolOffsets(modules []*profiling.Module, _ int, elfFile
*elf.File, minorVersion int) (*GoTLSSymbolAddresses, *elf.File, error) {
+func generateGOTLSSymbolOffsets(modules []*profiling.Module, _ int, elfFile
*elf.File, v *version.Version) (*GoTLSSymbolAddresses, *elf.File, error) {
reader, err := elfFile.NewDwarfReader(
goTLSReadSymbol, goTLSWriteSymbol, goTLSGIDStatusSymbol,
goTLSPollFDSymbol, goTLSConnSymbol, goTLSRuntimeG)
@@ -290,7 +438,7 @@ func generateGOTLSSymbolOffsets(modules
[]*profiling.Module, _ int, elfFile *elf
}
var retValArg0, retValArg1 = "~r1", "~r2"
- if minorVersion >= 18 {
+ if v.Minor >= 18 {
retValArg0, retValArg1 = "~r0", "~r1"
}
@@ -423,15 +571,36 @@ func buildSSLSymAddrConfig(libcryptoPath string)
(*OpenSSLFdSymAddrConfigInBPF,
type stringVerify func(a, b string) bool
+func searchSymbolNames(modules []*profiling.Module, verify stringVerify,
values ...string) []string {
+ list := searchSymbolList(modules, verify, values...)
+ if len(list) > 0 {
+ result := make([]string, 0)
+ for _, i := range list {
+ result = append(result, i.Name)
+ }
+ return result
+ }
+ return nil
+}
+
func searchSymbol(modules []*profiling.Module, verify stringVerify, values
...string) *profiling.Symbol {
+ list := searchSymbolList(modules, verify, values...)
+ if len(list) > 0 {
+ return list[0]
+ }
+ return nil
+}
+
+func searchSymbolList(modules []*profiling.Module, verify stringVerify, values
...string) []*profiling.Symbol {
+ var result []*profiling.Symbol
for _, mod := range modules {
for _, s := range mod.Symbols {
for _, validator := range values {
if verify(s.Name, validator) {
- return s
+ result = append(result, s)
}
}
}
}
- return nil
+ return result
}
diff --git a/pkg/tools/elf/class.go b/pkg/tools/elf/class.go
new file mode 100644
index 0000000..529fd5a
--- /dev/null
+++ b/pkg/tools/elf/class.go
@@ -0,0 +1,50 @@
+// 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 elf
+
+import "debug/dwarf"
+
+type ClassInfo struct {
+ name string
+ inheritances []*ClassInheritance
+ members []*dwarf.StructField
+}
+
+func (c *ClassInfo) GetInheritanceOffset(name string) int64 {
+ for _, i := range c.inheritances {
+ if i.name == name {
+ return int64(i.offset)
+ }
+ }
+ return -1
+}
+
+func (c *ClassInfo) GetMemberOffset(name string) int64 {
+ for _, m := range c.members {
+ if m.Name == name {
+ return m.ByteOffset
+ }
+ }
+ return -1
+}
+
+type ClassInheritance struct {
+ name string
+ cType *dwarf.StructType
+ offset uint64
+}
diff --git a/pkg/tools/elf/dwarf.go b/pkg/tools/elf/dwarf.go
index a899842..16b2904 100644
--- a/pkg/tools/elf/dwarf.go
+++ b/pkg/tools/elf/dwarf.go
@@ -32,6 +32,7 @@ type DwarfReader struct {
language int
functions map[string]*FunctionInfo
structures map[string]*StructureInfo
+ classes map[string]*ClassInfo
}
func (f *File) NewDwarfReader(attrNames ...string) (*DwarfReader, error) {
@@ -54,9 +55,50 @@ func (r *DwarfReader) GetStructure(name string)
*StructureInfo {
return r.structures[name]
}
+func (r *DwarfReader) GetStructMemberOffset(structName, memberName string)
(uint64, error) {
+ structure := r.GetStructure(structName)
+ if structure == nil {
+ return 0, fmt.Errorf("the struct not found: %s", structName)
+ }
+ field := structure.GetField(memberName)
+ if field == nil {
+ return 0, fmt.Errorf("the field not found, struct name: %s,
member name: %s", structName, memberName)
+ }
+ return uint64(field.Offset), nil
+}
+
+func (r *DwarfReader) GetClassParentOffset(className, parentClassName string)
(uint64, error) {
+ class := r.GetClass(className)
+ if class == nil {
+ return 0, fmt.Errorf("the class not found: %s", className)
+ }
+ offset := class.GetInheritanceOffset(parentClassName)
+ if offset >= 0 {
+ return uint64(offset), nil
+ }
+ return 0, fmt.Errorf("the parent class not found, class: %s, parent:
%s", className, parentClassName)
+}
+
+func (r *DwarfReader) GetClassMemberOffset(className, memberName string)
(uint64, error) {
+ class := r.GetClass(className)
+ if class == nil {
+ return 0, fmt.Errorf("the class not found: %s", className)
+ }
+ offset := class.GetMemberOffset(memberName)
+ if offset >= 0 {
+ return uint64(offset), nil
+ }
+ return 0, fmt.Errorf("the member not found, class: %s, member: %s",
className, memberName)
+}
+
+func (r *DwarfReader) GetClass(name string) *ClassInfo {
+ return r.classes[name]
+}
+
func (r *DwarfReader) init(data *dwarf.Data, readAttrNames []string) error {
r.functions = make(map[string]*FunctionInfo)
r.structures = make(map[string]*StructureInfo)
+ r.classes = make(map[string]*ClassInfo)
reader := data.Reader()
r.elfByteOrder = reader.ByteOrder()
@@ -78,6 +120,9 @@ func (r *DwarfReader) init(data *dwarf.Data, readAttrNames
[]string) error {
if err := r.processStructure(readAttrNames, data, entry); err
!= nil {
return err
}
+ if err := r.processClass(readAttrNames, data, entry); err !=
nil {
+ return err
+ }
}
return nil
}
diff --git a/pkg/tools/elf/dwarf_init.go b/pkg/tools/elf/dwarf_init.go
index cdfce40..7ada163 100644
--- a/pkg/tools/elf/dwarf_init.go
+++ b/pkg/tools/elf/dwarf_init.go
@@ -106,6 +106,40 @@ func (r *DwarfReader) processStructure(names []string,
data *dwarf.Data, entry *
return nil
}
+func (r *DwarfReader) processClass(names []string, data *dwarf.Data, entry
*dwarf.Entry) error {
+ if entry.Tag != dwarf.TagClassType {
+ return nil
+ }
+
+ name, ok := entry.Val(dwarf.AttrName).(string)
+ if !ok {
+ return nil
+ }
+ _, ok = entry.Val(dwarf.AttrDeclaration).(bool)
+ if ok {
+ return nil
+ }
+
+ found := false
+ for _, n := range names {
+ if n == name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return nil
+ }
+
+ info, err := r.getClassInfo(data, name, entry)
+ if err != nil {
+ return err
+ }
+ r.classes[name] = info
+
+ return nil
+}
+
func (r *DwarfReader) entryType(data *dwarf.Data, entry *dwarf.Entry)
(dwarf.Type, error) {
off, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
if !ok {
@@ -122,7 +156,7 @@ func (r *DwarfReader) IsRetArgs(entry *dwarf.Entry) (bool,
error) {
return isRet, nil
}
-func (r *DwarfReader) getStructureFields(data *dwarf.Data, entry *dwarf.Entry)
([]*StructureFieldInfo, error) {
+func (r *DwarfReader) getClassInfo(data *dwarf.Data, name string, entry
*dwarf.Entry) (*ClassInfo, error) {
reader := data.Reader()
reader.Seek(entry.Offset)
@@ -131,6 +165,66 @@ func (r *DwarfReader) getStructureFields(data *dwarf.Data,
entry *dwarf.Entry) (
return nil, err
}
+ c := &ClassInfo{
+ name: name,
+ }
+
+ foundMembers := false
+ for {
+ child, err := reader.Next()
+ if err != nil {
+ return nil, err
+ }
+ if child == nil || child.Tag == 0 {
+ break
+ }
+
+ if child.Tag == dwarf.TagInheritance {
+ offset, ok := child.Val(dwarf.AttrDataMemberLoc).(int64)
+ if !ok {
+ continue
+ }
+ entryType, err1 := r.entryType(data, child)
+ if err1 != nil {
+ continue
+ }
+ structType, ok := entryType.(*dwarf.StructType)
+ if !ok {
+ continue
+ }
+
+ c.inheritances = append(c.inheritances,
&ClassInheritance{
+ name: structType.StructName,
+ cType: structType,
+ offset: uint64(offset),
+ })
+ }
+
+ entryType, err := r.entryType(data, child)
+ if !foundMembers && err == nil && entryType != nil {
+ prtType, ok := entryType.(*dwarf.PtrType)
+ if !ok {
+ continue
+ }
+ structType, ok := prtType.Type.(*dwarf.StructType)
+ if !ok {
+ continue
+ }
+ c.members = structType.Field
+ foundMembers = true
+ }
+ }
+ return c, nil
+}
+
+func (r *DwarfReader) getStructureFields(data *dwarf.Data, entry *dwarf.Entry)
([]*StructureFieldInfo, error) {
+ reader := data.Reader()
+ reader.Seek(entry.Offset)
+ _, err := reader.Next()
+ if err != nil {
+ return nil, err
+ }
+
res := make([]*StructureFieldInfo, 0)
for {
child, err := reader.Next()
diff --git a/pkg/tools/offsets/nodejs.go b/pkg/tools/offsets/nodejs.go
new file mode 100644
index 0000000..10757fe
--- /dev/null
+++ b/pkg/tools/offsets/nodejs.go
@@ -0,0 +1,77 @@
+// 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 offsets
+
+import (
+ "github.com/apache/skywalking-rover/pkg/tools/elf"
+
+ "github.com/hashicorp/go-multierror"
+)
+
+var nodeTLSWrap = "TLSWrap"
+var nodeTLSStreamListener = "StreamListener"
+var nodeTLSStreamBase = "StreamBase"
+var nodeTLSStreamResource = "StreamResource"
+var nodeTLSStreamField = "stream_"
+var nodeTLSLibuvStreamWrap = "LibuvStreamWrap"
+var nodeTLSUVStream = "uv_stream_s"
+var nodeTLSIOWatcher = "io_watcher"
+var nodeTLSUVIO = "uv__io_s"
+var nodeTLSFD = "fd"
+
+type NodeTLSAddrInBPF struct {
+ TLSWrapStreamListenerOffset uint32
+ StreamListenerStreamOffset uint32
+ StreamBaseStreamResourceOffset uint32
+ LibuvStreamWrapStreamBaseOffset uint32
+ LibuvStreamWrapStreamOffset uint32
+ UVStreamSIOWatcherOffset uint32
+ UVIOSFDOffset uint32
+}
+
+func GenerateNodeTLSAddresses(path string) (*NodeTLSAddrInBPF, error) {
+ file, err := elf.NewFile(path)
+ if err != nil {
+ return nil, err
+ }
+ reader, err := file.NewDwarfReader(nodeTLSWrap, nodeTLSStreamListener,
nodeTLSStreamBase, nodeTLSLibuvStreamWrap,
+ nodeTLSLibuvStreamWrap, nodeTLSUVStream, nodeTLSUVIO)
+ if err != nil {
+ return nil, err
+ }
+
+ result := &NodeTLSAddrInBPF{}
+ err = findOrError(err, &result.TLSWrapStreamListenerOffset,
reader.GetClassParentOffset, nodeTLSWrap, nodeTLSStreamListener)
+ err = findOrError(err, &result.StreamListenerStreamOffset,
reader.GetClassMemberOffset, nodeTLSStreamListener, nodeTLSStreamField)
+ err = findOrError(err, &result.StreamBaseStreamResourceOffset,
reader.GetClassParentOffset, nodeTLSStreamBase, nodeTLSStreamResource)
+ err = findOrError(err, &result.LibuvStreamWrapStreamBaseOffset,
reader.GetClassParentOffset, nodeTLSLibuvStreamWrap, nodeTLSStreamBase)
+ err = findOrError(err, &result.LibuvStreamWrapStreamOffset,
reader.GetClassMemberOffset, nodeTLSLibuvStreamWrap, nodeTLSStreamField)
+ err = findOrError(err, &result.UVStreamSIOWatcherOffset,
reader.GetStructMemberOffset, nodeTLSUVStream, nodeTLSIOWatcher)
+ err = findOrError(err, &result.UVIOSFDOffset,
reader.GetStructMemberOffset, nodeTLSUVIO, nodeTLSFD)
+
+ return result, err
+}
+
+func findOrError(err error, target *uint32, f func(name1, name2 string)
(uint64, error), val1, val2 string) error {
+ d, e := f(val1, val2)
+ if e != nil {
+ return multierror.Append(err, e)
+ }
+ *target = uint32(d)
+ return err
+}
diff --git a/pkg/tools/version/version.go b/pkg/tools/version/version.go
new file mode 100644
index 0000000..75a3df5
--- /dev/null
+++ b/pkg/tools/version/version.go
@@ -0,0 +1,80 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package version
+
+import (
+ "fmt"
+ "strconv"
+)
+
+type Version struct {
+ Major int
+ Minor int
+ Patch int
+}
+
+func Build(major, minor, patch int) *Version {
+ return &Version{Major: major, Minor: minor, Patch: patch}
+}
+
+func Read(majorVal, minorVal, patchVal string) (*Version, error) {
+ result := &Version{}
+ var err error
+ result.Major, err = parseVal(err, "major", majorVal)
+ result.Minor, err = parseVal(err, "minor", minorVal)
+ result.Patch, err = parseVal(err, "patch", patchVal)
+ return result, err
+}
+
+func (v *Version) GreaterOrEquals(o *Version) bool {
+ var compare int
+ compare = v.compare(compare, v.Major, o.Major)
+ compare = v.compare(compare, v.Minor, o.Minor)
+ compare = v.compare(compare, v.Patch, o.Patch)
+ return compare >= 0
+}
+
+func (v *Version) compare(res, before, after int) int {
+ if res != 0 {
+ return res
+ }
+ if before > after {
+ return 1
+ } else if before == after {
+ return 0
+ }
+ return -1
+}
+
+func (v *Version) String() string {
+ return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
+}
+
+func parseVal(err error, name, val string) (int, error) {
+ if err != nil {
+ return 0, err
+ }
+ if val == "" {
+ return 0, nil
+ }
+ atoi, err := strconv.Atoi(val)
+ if err != nil {
+ return 0, fmt.Errorf("the %s version is a number, version: %s",
name, val)
+ }
+ return atoi, nil
+}
diff --git a/scripts/debug/nodejs/tls_gen_offsets.go
b/scripts/debug/nodejs/tls_gen_offsets.go
new file mode 100644
index 0000000..d2707fd
--- /dev/null
+++ b/scripts/debug/nodejs/tls_gen_offsets.go
@@ -0,0 +1,37 @@
+// 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 main
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+
+ "github.com/apache/skywalking-rover/pkg/tools/offsets"
+)
+
+func main() {
+ if len(os.Args) <= 1 {
+ log.Fatal("please input the node file path")
+ return
+ }
+ path := os.Args[1]
+ address, err := offsets.GenerateNodeTLSAddresses(path)
+ marshal, _ := json.Marshal(address)
+ log.Printf("the node addresses, node path: %s, address info: %s,
errors: %v", path, string(marshal), err)
+}
diff --git a/scripts/debug/nodejs/tls_gen_offsets.sh
b/scripts/debug/nodejs/tls_gen_offsets.sh
new file mode 100755
index 0000000..aa2922b
--- /dev/null
+++ b/scripts/debug/nodejs/tls_gen_offsets.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+set -ex
+
+VERSION=$1
+
+echo "ready to generate the offset of nodejs $VERSION"
+
+current_dir="$(cd "$(dirname $0)"; pwd)"
+WORK_DIR="$current_dir/nodejs"
+ROVER_DIR="$current_dir/../../"
+
+# download source code
+mkdir -p $WORK_DIR && cd $WORK_DIR
+wget -O node.tar.gz https://github.com/nodejs/node/archive/$VERSION.tar.gz
+tar -xf node.tar.gz --strip 1 && rm node.tar.gz
+
+# compile as the debug binary file
+./configure --debug && make -j8
+NODE_PATH="$WORK_DIR/out/Debug/node"
+
+# generate ssl certs and start https server
+bash
$ROVER_DIR/test/e2e/cases/profiling/network/base/ssl/gen-selfsigned-ssl.sh
service
+$NODE_PATH
+
+# run the go program
+cd $ROVER_DIR
+go run scripts/debug/node_tls_gen_offsets.go $NODE_PATH
\ No newline at end of file
diff --git a/test/e2e/cases/process/istio/kind.yaml
b/test/e2e/cases/process/istio/kind.yaml
index 2f9384f..f832efd 100644
--- a/test/e2e/cases/process/istio/kind.yaml
+++ b/test/e2e/cases/process/istio/kind.yaml
@@ -20,7 +20,11 @@ apiVersion: kind.x-k8s.io/v1alpha4
nodes:
# the control plane node config
- role: control-plane
+ image: kindest/node:v1.21.1
# the three workers
- role: worker
+ image: kindest/node:v1.21.1
- role: worker
+ image: kindest/node:v1.21.1
- role: worker
+ image: kindest/node:v1.21.1
diff --git a/test/e2e/cases/profiling/network/nodejs/Dockerfile
b/test/e2e/cases/profiling/network/nodejs/Dockerfile
new file mode 100644
index 0000000..be395a5
--- /dev/null
+++ b/test/e2e/cases/profiling/network/nodejs/Dockerfile
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+FROM node:16.17.0
+
+WORKDIR /
+COPY nodejs/service.js /service.js
+
+COPY base/ssl /usr/local/share/ca-certificates/ssl
+RUN update-ca-certificates
+
+ENV NODE_TLS_REJECT_UNAUTHORIZED=0
+
+CMD ["node", "/service.js"]
\ No newline at end of file
diff --git a/test/e2e/cases/profiling/network/nodejs/docker-compose.yml
b/test/e2e/cases/profiling/network/nodejs/docker-compose.yml
new file mode 100644
index 0000000..f941ace
--- /dev/null
+++ b/test/e2e/cases/profiling/network/nodejs/docker-compose.yml
@@ -0,0 +1,64 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+version: '2.1'
+
+services:
+ service:
+ build:
+ context: ../
+ dockerfile: nodejs/Dockerfile
+ networks:
+ - e2e
+ volumes:
+ - ./../base/ssl:/ssl_data
+ ports:
+ - 10443:10443
+ healthcheck:
+ test: [ "CMD", "bash", "-c", "cat < /dev/null >
/dev/tcp/127.0.0.1/10443" ]
+ interval: 5s
+ timeout: 60s
+ retries: 120
+
+ proxy:
+ extends:
+ file: ../base/docker-compose.yml
+ service: proxy
+ networks:
+ - e2e
+ depends_on:
+ service:
+ condition: service_healthy
+
+ oap:
+ extends:
+ file: ../base/docker-compose.yml
+ service: oap
+ ports:
+ - 12800:12800
+
+ rover:
+ extends:
+ file: ../base/docker-compose.yml
+ service: rover
+ environment:
+ ROVER_PROCESS_DISCOVERY_REGEX_SCANNER_MATCH_CMD2: service.js
+ ROVER_PROCESS_DISCOVERY_REGEX_SCANNER_PROCESS_NAME2: service
+ depends_on:
+ oap:
+ condition: service_healthy
+
+networks:
+ e2e:
\ No newline at end of file
diff --git a/test/e2e/cases/process/istio/kind.yaml
b/test/e2e/cases/profiling/network/nodejs/e2e.yaml
similarity index 50%
copy from test/e2e/cases/process/istio/kind.yaml
copy to test/e2e/cases/profiling/network/nodejs/e2e.yaml
index 2f9384f..e1bf3ab 100644
--- a/test/e2e/cases/process/istio/kind.yaml
+++ b/test/e2e/cases/profiling/network/nodejs/e2e.yaml
@@ -13,14 +13,33 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# this config file contains all config fields with comments
-# NOTE: this is not a particularly useful config file
-kind: Cluster
-apiVersion: kind.x-k8s.io/v1alpha4
-nodes:
- # the control plane node config
- - role: control-plane
- # the three workers
- - role: worker
- - role: worker
- - role: worker
+setup:
+ env: compose
+ file: docker-compose.yml
+ timeout: 20m
+ init-system-environment: ../../../../base/env
+ steps:
+ - name: set PATH
+ command: export PATH=/tmp/skywalking-infra-e2e/bin:$PATH
+ - name: install yq
+ command: bash test/e2e/base/scripts/prepare/setup-e2e-shell/install.sh yq
+ - name: install swctl
+ command: bash test/e2e/base/scripts/prepare/setup-e2e-shell/install.sh
swctl
+
+trigger:
+ action: http
+ interval: 3s
+ times: 10
+ url: https://${service_host}:${service_10443}/consumer
+ method: GET
+
+verify:
+ # verify with retry strategy
+ retry:
+ # max retry count
+ count: 20
+ # the interval between two retries, in millisecond.
+ interval: 10s
+ cases:
+ - includes:
+ - ../network-cases.yaml
\ No newline at end of file
diff --git a/test/e2e/cases/profiling/network/nodejs/service.js
b/test/e2e/cases/profiling/network/nodejs/service.js
new file mode 100644
index 0000000..04da859
--- /dev/null
+++ b/test/e2e/cases/profiling/network/nodejs/service.js
@@ -0,0 +1,52 @@
+// 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.
+
+const https = require('https');
+const fs = require('fs');
+
+const options = {
+ key: fs.readFileSync('/ssl_data/service.key'),
+ cert: fs.readFileSync('/ssl_data/service.crt')
+};
+
+https.createServer(options, function (req, res) {
+ if (req.url == '/provider') {
+ res.writeHead(200, {"Content-Type": "text/html"});
+ res.end('provider');
+ return;
+ }
+
+ const sendReq = https.request({
+ hostname: 'proxy',
+ port: 443,
+ path: '/provider',
+ method: 'GET'
+ }, proxyResp => {
+ proxyResp.on('data', d => {
+ res.writeHead(200, {"Content-Type": "text/html"});
+ res.end('success');
+ })
+ })
+ sendReq.on('error', error => {
+ res.writeHead(200, {"Content-Type": "text/html"});
+ console.log(error)
+ res.end('error');
+ })
+ sendReq.end()
+}).listen(10443);
+
+console.log("https server started!")