package ebpftracer import ( "errors" "io/ioutil" "strings" "fmt" "debug/elf" "path/filepath" "github.com/coroot/coroot-node-agent/utils" "github.com/cilium/ebpf/link" "golang.org/x/arch/x86/x86asm" "golang.org/x/arch/arm64/arm64asm" ) const ( // goServeHTTP = "net/http.serverHandler.ServeHTTP" // binPath = "/root/code/jdk8u/build/linux-x86_64-normal-server-release/jdk/lib/amd64/libnio.so" symbolsocketRead0 = "Java_sun_nio_ch_FileDispatcherImpl_read0" ) func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, insID utils.ID) []link.Link { if t.disableL7Tracing { return nil } version := UsePIDToGetJDKVersion(pid) fmt.Println("java version is ", version) var links []link.Link bpath := getSoPath(pid, "nio.so") if bpath == ""{ fmt.Println("can,t find the nio.so") return nil } fmt.Println("find the nio.so path is ", bpath) ex, err := link.OpenExecutable(bpath) if err != nil { return nil } ef, err := elf.Open(bpath) if err != nil { return nil //PID: int(pid), } defer ef.Close() symbols, err := ef.Symbols() if err != nil { if errors.Is(err, elf.ErrNoSymbols) { return nil } return nil } textSection := ef.Section(".text") if textSection == nil { return nil } textSectionData, err := textSection.Data() if err != nil { return nil } textSectionLen := uint64(len(textSectionData) - 1) // opt := link.UprobeOptions{ // Offset: 61, // } // upread02, err := ex.Uprobe(symbolsocketRead0, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &opt) // if err != nil { // return nil // } // links = append(links, upread01) for _, s := range symbols { if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 { continue } switch s.Name { case symbolsocketRead0: default: continue } address := s.Value for _, p := range ef.Progs { if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 { continue } if p.Vaddr <= s.Value && s.Value < (p.Vaddr+p.Memsz) { address = s.Value - p.Vaddr + p.Off break } } fmt.Println("s.Name-----:", s.Name) switch s.Name { case symbolsocketRead0: sStart := s.Value - textSection.Addr sEnd := sStart + s.Size if sEnd > textSectionLen { continue } sBytes := textSectionData[sStart:sEnd] returnOffsets := getCallNextMoveOffsets(ef.Machine, sBytes) if len(returnOffsets) == 0 { return nil } for _, offset := range returnOffsets { l, err := ex.Uprobe(s.Name, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &link.UprobeOptions{Address: address, Offset: uint64(offset)}) if err != nil { fmt.Println("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe") return nil } links = append(links, l) } } } if len(links) == 0 { return nil } fmt.Println("jvm uprobes attached, pid is ", pid) return links } func (t *Tracer) AttachJavaNetReadUprobes(pid uint32, insID utils.ID) []link.Link { if t.disableL7Tracing { return nil } // //if pid != 251719 { // return nil //} var libnetSo = "/opt/github/jdk8u/build/linux-x86_64-normal-server-slowdebug/jdk/lib/amd64/libnet.so" var sys = "Java_java_net_SocketOutputStream_socketWrite0" var links []link.Link ex, err := link.OpenExecutable(libnetSo) if err != nil { return nil } opt := link.UprobeOptions{ Offset: 66, PID: int(pid), } upread02, err := ex.Uprobe(sys, t.uprobes["uprobe_Java_java_net_SocketOutputStream_socketWrite0"], &opt) if err != nil { return nil } links = append(links, upread02) if len(links) == 0 { return nil } fmt.Println("jvm client uprobes attached", pid) return links } func getCallNextMoveOffsets(machine elf.Machine, instructions []byte) []int { var res []int firstCall := 0 switch machine { case elf.EM_X86_64: for i := 0; i < len(instructions); { ins, err := x86asm.Decode(instructions[i:], 64) if err == nil && ins.Op == x86asm.CALL { if firstCall == 0{ firstCall = 1 }else{ i += ins.Len res = append(res, i) return res } } i += ins.Len } case elf.EM_AARCH64: for i := 0; i < len(instructions); { ins, err := arm64asm.Decode(instructions[i:]) if err == nil && ins.Op == arm64asm.BL { if firstCall == 0{ firstCall = 1 }else{ i += 4 res = append(res, i) return res } } i += 4 } } return res } func getSoPath(pid uint32, soname string) string{ // 获取进程的maps文件路径 mapsPath := fmt.Sprintf("/proc/%d/maps", pid) // 读取maps文件内容 mapsData, err := ioutil.ReadFile(mapsPath) if err != nil { fmt.Println("无法读取maps文件") 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 path } } } } return "" }