Ver código fonte

Fixed #TASK_QT-9810 增加jvm版本号限制1.8.x

Carl 1 ano atrás
pai
commit
b2e347b2f0

+ 26 - 2
containers/container.go

@@ -2,6 +2,8 @@ package containers
 
 import (
 	debugelf "debug/elf"
+	"fmt"
+	"github.com/coroot/coroot-node-agent/utils"
 	"os"
 	"sort"
 	"strconv"
@@ -1469,16 +1471,38 @@ func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) erro
 	codeType := c.GetCodeTypeFromCache(pid)
 	if !p.jvmUprobesChecked {
 		p.jvmUprobesChecked = true
+		rootfs := c.getRootfs()
 		tracer.InitKProcInfo(pid, &c.AppInfo)
 
-		libNioProbes, err := tracer.AttachJavaNioReadUprobes(pid, codeType, c.getRootfs())
+		// TODO java Aot
+		if codeType.IsJvmCode() {
+			// check version
+			libjavaso, err := utils.GetSoPath(pid, "libjava.so", rootfs)
+			if err != nil {
+				klog.WithError(err).Errorf("[attach] Failed get so path")
+				return err
+			}
+			v, err := ebpftracer.GetJvmVersion(libjavaso)
+			if err != nil {
+				klog.WithError(err).Errorf("[attach] Failed get Java version")
+				return err
+			}
+			c.AppInfo.Version = v
+			major, minor, patch, err := ebpftracer.ParseVersion(v)
+			klog.Infof("[attach] version: %s (Major: %d, Minor: %d, Patch: %d)", v, major, minor, patch)
+			if major != 1 || minor != 8 {
+				return fmt.Errorf("[attach] Unsupported Java version")
+			}
+		}
+
+		libNioProbes, err := tracer.AttachJavaNioReadUprobes(pid, codeType, rootfs)
 		if err != nil {
 			klog.Error(err)
 			return err
 		}
 		p.uprobes = append(p.uprobes, libNioProbes...)
 
-		libNetProbes, err := tracer.AttachJavaNetWriteUprobes(pid, c.getRootfs())
+		libNetProbes, err := tracer.AttachJavaNetWriteUprobes(pid, rootfs)
 		if err != nil {
 			klog.Error(err)
 			return err

+ 87 - 3
ebpftracer/getjdkversion.go

@@ -1,13 +1,97 @@
 package ebpftracer
 
 import (
+	"bytes"
 	"fmt"
 	"os"
 	"os/exec"
-	"bytes"
+	"regexp"
+	"strconv"
 	"strings"
+	"unicode"
 )
 
+func GetJvmVersion(filePath string) (string, error) {
+	minLength := 4
+	var maxFileSize int64 = 3 * 1024 * 1024 // 3MB
+	// 打开文件
+	sofile, err := os.Open(filePath)
+	if err != nil {
+		return "", fmt.Errorf("failed to open file: %w", err)
+	}
+	defer sofile.Close()
+
+	// 检查文件大小
+	fileInfo, err := sofile.Stat()
+	if err != nil {
+		return "", fmt.Errorf("failed to stat file: %w", err)
+	}
+	if fileInfo.Size() > maxFileSize {
+		return "", fmt.Errorf("file size exceeds limit of %d bytes", maxFileSize)
+	}
+
+	var result []string
+	var buffer []rune
+
+	// 逐字节读取文件
+	buf := make([]byte, 4096) // 4KB 缓冲区
+	for {
+		n, err := sofile.Read(buf)
+		if n > 0 {
+			for _, b := range buf[:n] {
+				// 检查是否是可打印字符
+				if unicode.IsPrint(rune(b)) {
+					buffer = append(buffer, rune(b))
+				} else if len(buffer) >= minLength {
+					// 如果当前缓冲区长度满足要求,则存入结果
+					result = append(result, string(buffer))
+					buffer = nil
+				} else {
+					// 清空缓冲区
+					buffer = nil
+				}
+			}
+		}
+		if err != nil {
+			if err.Error() != "EOF" {
+				return "", fmt.Errorf("failed to read file: %w", err)
+			}
+			break
+		}
+	}
+
+	// 如果缓冲区有剩余字符串,添加到结果中
+	if len(buffer) >= minLength {
+		result = append(result, string(buffer))
+	}
+
+	// 查找 `java.version` 的下一行
+	keyword := "java.version"
+
+	for i, str := range result {
+		if str == keyword && i+1 < len(result) {
+			return result[i+1], nil // 返回关键字下一行内容
+		}
+	}
+
+	return "", fmt.Errorf("failed to extract java version from %s", filePath)
+}
+
+// 解析版本号为大版本、中间版本和小版本
+func ParseVersion(version string) (int, int, int, error) {
+	re := regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+)`)
+	matches := re.FindStringSubmatch(version)
+	if len(matches) != 4 {
+		return 0, 0, 0, fmt.Errorf("invalid version format")
+	}
+
+	major, _ := strconv.Atoi(matches[1])
+	minor, _ := strconv.Atoi(matches[2])
+	patch, _ := strconv.Atoi(matches[3])
+
+	return major, minor, patch, nil
+}
+
 func parseQuotedString(input string) (string, error) {
 	// 找到第一个双引号的位置
 	start := strings.Index(input, "\"")
@@ -39,7 +123,7 @@ func getExecutablePath(pid uint32) (string, error) {
 	return execPath, nil
 }
 
-func UsePIDToGetJDKVersion(pid uint32) string{
+func UsePIDToGetJDKVersion(pid uint32) string {
 	execPath, err := getExecutablePath(pid)
 	if err != nil {
 		fmt.Println("Error:", err)
@@ -69,5 +153,5 @@ func UsePIDToGetJDKVersion(pid uint32) string{
 		return ""
 	}
 	return version
-	
+
 }

+ 10 - 38
ebpftracer/jvm.go

@@ -4,19 +4,15 @@ import (
 	"debug/elf"
 	"errors"
 	"fmt"
+	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/inject"
+	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/utils"
 	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	klog "github.com/sirupsen/logrus"
-	"io/ioutil"
-	"path/filepath"
-	"runtime"
-	"strings"
-
-	"github.com/cilium/ebpf/link"
-	"github.com/coroot/coroot-node-agent/proc"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
+	"runtime"
 )
 
 const (
@@ -37,9 +33,13 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType CodeType, rootfs
 	if codeType.IsJavaAotCode() {
 		bpath = proc.Path(pid, "exe")
 	} else {
-		version := UsePIDToGetJDKVersion(pid)
-		klog.Infof("[attach] java version is %s", version)
-		bpath = getSoPath(pid, "libnio.so", rootfs)
+		//version := UsePIDToGetJDKVersion(pid)
+		//klog.Infof("[attach] java version is %s", version)
+		var err error
+		bpath, err = utils.GetSoPath(pid, "libnio.so", rootfs)
+		if err != nil {
+			return nil, err
+		}
 		if bpath == "" {
 			return nil, errors.New("can not find nio.so")
 		}
@@ -255,31 +255,3 @@ func getCallNextMoveOffsets(machine elf.Machine, instructions []byte) []int {
 	}
 	return res
 }
-
-func getSoPath(pid uint32, soname string, rootfs string) string {
-	// 获取进程的maps文件路径
-	mapsPath := fmt.Sprintf("/proc/%d/maps", pid)
-
-	// 读取maps文件内容
-	mapsData, err := ioutil.ReadFile(mapsPath)
-	if err != nil {
-		fmt.Println("lookup so error.")
-		return ""
-	}
-
-	lines := strings.Split(string(mapsData), "\n")
-	for _, line := range lines {
-		fields := strings.Fields(line)
-		if len(fields) >= 6 {
-			perms := fields[1]
-			path := fields[len(fields)-1]
-
-			if strings.Contains(perms, "x") && filepath.Ext(path) == ".so" {
-				if strings.Contains(path, soname) {
-					return rootfs + path
-				}
-			}
-		}
-	}
-	return ""
-}

+ 0 - 1
ebpftracer/tracer/inject/inject_linux_amd64.go

@@ -511,7 +511,6 @@ func (j *JvmInjector) findLibBaseByPathFromProcMaps(libPath string) (uint64, str
 			if len(fields) > 5 {
 				path := fields[5]
 				if strings.HasSuffix(path, ".so") {
-					fmt.Printf("Found library %s\n", path)
 					return start, path, nil
 				}
 			}

+ 1 - 1
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.mod

@@ -23,7 +23,7 @@ require (
 	github.com/cenkalti/backoff/v4 v4.2.1 // indirect
 	github.com/cilium/ebpf v0.11.0 // indirect
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
-	github.com/coroot/logparser v1.1.2 // indirect
+	github.com/coroot/logparser v1.1.5 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect

+ 1 - 0
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.sum

@@ -16,6 +16,7 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coroot/logparser v1.1.2 h1:9aH4zIBle14xMHq07YHqVFE2t68k3LE10X2yKHXtJG8=
 github.com/coroot/logparser v1.1.2/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
+github.com/coroot/logparser v1.1.5/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=

+ 1 - 1
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/go.mod

@@ -23,7 +23,7 @@ require (
 	github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
 	github.com/cilium/ebpf v0.11.0 // indirect
 	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
-	github.com/coroot/logparser v1.1.2 // indirect
+	github.com/coroot/logparser v1.1.5 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect

+ 1 - 0
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/go.sum

@@ -16,6 +16,7 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coroot/logparser v1.1.2 h1:9aH4zIBle14xMHq07YHqVFE2t68k3LE10X2yKHXtJG8=
 github.com/coroot/logparser v1.1.2/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
+github.com/coroot/logparser v1.1.5/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=

+ 1 - 0
utils/modelse/app_info.go

@@ -41,6 +41,7 @@ type AppInfo struct {
 	RegisterAt     int64         `json:"register_at"`
 	UpdateAt       int64         `json:"update_at"`
 	Status         APP_TYPE      `json:"status"`
+	Version        string        `json:"version"`
 }
 
 func (a *AppInfo) UpdateAtTime() {

+ 23 - 0
utils/util.go

@@ -922,3 +922,26 @@ func ToString(v interface{}) string {
 	}
 	return string(b)
 }
+
+func GetSoPath(pid uint32, soname string, rootfs string) (string, error) {
+	mapsFile := fmt.Sprintf("/proc/%d/maps", pid)
+	file, err := os.Open(mapsFile)
+	if err != nil {
+		return "", err
+	}
+	defer file.Close()
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if strings.Contains(line, soname) {
+			fields := strings.Fields(line)
+			if len(fields) > 5 {
+				path := fields[5]
+				if strings.HasSuffix(path, ".so") {
+					return path, nil
+				}
+			}
+		}
+	}
+	return "", fmt.Errorf("library %s not found in process.", soname)
+}