package ebpftracer import ( "bufio" "fmt" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/coroot/coroot-node-agent/ebpftracer/tracer" "github.com/coroot/coroot-node-agent/ebpftracer/tracer/jattach" "github.com/coroot/coroot-node-agent/utils" . "github.com/coroot/coroot-node-agent/utils/modelse" klog "github.com/sirupsen/logrus" "os" "path/filepath" "strconv" "strings" "syscall" ) // AttachStackUprobes // default process stack func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link.Link { var links []link.Link ex, err := link.OpenExecutable(path) if err != nil { return nil } klog.Infoln("[stack] Attach Start", path) for i, up := range uprobes { klog.Debugf("[stack] attaching %d -> %d -> %s -> 0x%x -> 0x%x -> 0x%x", i, len(uprobes), up.Funcname, up.AbsOffset, up.Address, up.AbsOffset+up.Address) var prog *ebpf.Program switch up.Location { case tracer.AtEntry: prog = t.uprobes["ent"] case tracer.AtRet: prog = t.uprobes["ret"] case tracer.AtDotNetEntry: prog = t.uprobes["dotnetent"] } uplink, err := ex.Uprobe(up.Funcname, prog, &link.UprobeOptions{Address: up.Address, Offset: up.AbsOffset}) if err != nil { klog.Errorf("[stack] attachingERROR:%v, %v, %v", err, up, uplink) // return nil } else { links = append(links, uplink) } } return links } // JVM process stack func (t *Tracer) JattachJvm(pid uint32, appInfo AppInfo, whiteList, blackList, rootfs string) error { klog.Infoln("[Jvm stack uprobe] Attach Start AttachJVMStackUprobes", pid) // TODO tiny Agent 注入 // TODO copy agent到 jre conf/lib/plugins //argkv := "/opt/github/euspace/dist/package_dir/agents/NativeAgent/lib/apmAgent.jar=/opt/github/euspace/dist/package_dir/agents/NativeAgent" if whiteList == "" && blackList == "" { return fmt.Errorf("no stack rule") } whiteList = strings.ReplaceAll(whiteList, ",", "") blackList = strings.ReplaceAll(blackList, ",", "") var stackRule string if whiteList == "" { stackRule = fmt.Sprintf("blackStack=%s", blackList) // 仅保留黑名单 } else if blackList == "" { stackRule = fmt.Sprintf("whiteStack=%s", whiteList) // 仅保留白名单 } else { stackRule = fmt.Sprintf("whiteStack=%s,blackStack=%s", whiteList, blackList) } //nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent") nativeBasePath := "/tmp/NativeAgent" kvPairs := []string{ fmt.Sprintf("%s=%s", filepath.Join(nativeBasePath, "lib", "apmAgent.jar"), nativeBasePath), fmt.Sprintf("%s=%d", "appId", appInfo.AppIdHash.IntVal), fmt.Sprintf("%s=%d", "agentId", appInfo.AgentId), fmt.Sprintf("%s=%d", "hostId", utils.GetHostID()), fmt.Sprintf("%s=%d", "accountId", utils.GetAccountID()), stackRule, } argkv := strings.Join(kvPairs, ",") klog.Infof("[Jvm stack uprobe] params:[%s]", argkv) args := []string{"load", "instrument", "false", argkv} jattacher := jattach.JvmJattacher{ Pid: pid, Args: args, PrintOutput: 1, } res, err := jattacher.JAttach() klog.Infof("[Jvm stack uprobe] JAttach Result: %d", res) if err != nil { return err } return nil } func (t *Tracer) AttachJVMStackUprobes(pid uint32, appInfo AppInfo, rootfs string) ([]link.Link, error) { //path = utils.GetDefaultAgentsPath("NativeAgent", "libnativeAgent.so") //tmp/NativeAgentSo2297066477572820801.tmp path, err := FindNativeSoFromMapped(pid, "NativeAgentSo", ".tmp") if err != nil { klog.Error(err) return nil, err } path = filepath.Join(rootfs, path) setNodeEnter := "Java_com_cloudwise_agent_common_natives_TraceNative_setNodeEnter" setNodeReturn := "Java_com_cloudwise_agent_common_natives_TraceNative_setNodeReturn" klog.Infoln("[Jvm stack uprobe] Attach Start AttachJVMStackUprobes", path) var links []link.Link ex, err := link.OpenExecutable(path) if err != nil { klog.Error(err) return nil, err } klog.Infof("[Jvm stack uprobe] Attach Start [%s] [%s] ", setNodeEnter, setNodeReturn) uplink, err := ex.Uprobe(setNodeEnter, t.uprobes["setNodeEnter"], &link.UprobeOptions{Offset: 0x0, PID: int(pid)}) if err != nil { klog.Errorf("[Jvm stack uprobe] Attaching ERROR: %v, %v, %v\n", err, setNodeEnter, uplink) return nil, err } else { links = append(links, uplink) } klog.Infoln("Attach Start " + setNodeReturn) uplink, err = ex.Uprobe(setNodeReturn, t.uprobes["setNodeReturn"], &link.UprobeOptions{Offset: 0x0}) if err != nil { klog.Errorf("[Jvm stack uprobe] Attaching ERROR: %v, %v, %v\n", err, setNodeReturn, uplink) return nil, err } else { links = append(links, uplink) } return links, nil } func FindNativeSoFromMapped(pid uint32, prefix, suffix string) (string, error) { mapsFile := fmt.Sprintf("/proc/%d/maps", pid) tmpFile, err := os.Open(mapsFile) if err != nil { return "", fmt.Errorf("error opening maps file: %v", err) } defer tmpFile.Close() var selectedPath string var maxTimestamp int64 fallbackPaths := make(map[string]struct{}) // 使用 map 去重 scanner := bufio.NewScanner(tmpFile) for scanner.Scan() { line := scanner.Text() if strings.Contains(line, prefix) && strings.HasSuffix(line, suffix) { parts := strings.Fields(line) if len(parts) > 5 { path := parts[len(parts)-1] baseName := filepath.Base(path) if strings.HasPrefix(baseName, prefix) && strings.HasSuffix(baseName, suffix) { middle := strings.TrimSuffix(strings.TrimPrefix(baseName, prefix), suffix) segments := strings.Split(middle, ".") if len(segments) >= 2 { timestampStr := segments[len(segments)-1] timestamp, err := strconv.ParseInt(timestampStr, 10, 64) if err == nil && timestamp > maxTimestamp { maxTimestamp = timestamp selectedPath = path } } else { fallbackPaths[path] = struct{}{} } } } } } if err = scanner.Err(); err != nil { return "", fmt.Errorf("error reading maps file: %v", err) } if selectedPath != "" { return selectedPath, nil } var latestPath string var latestModTime int64 for path := range fallbackPaths { info, err := os.Stat(path) if err != nil { continue } stat, ok := info.Sys().(*syscall.Stat_t) if !ok { continue } modTime := stat.Mtim.Sec if modTime > latestModTime { latestModTime = modTime latestPath = path } } if latestPath != "" { return latestPath, nil } return "", fmt.Errorf("no matching path found") }