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!")

Reply via email to