jvm.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package ebpftracer
  2. import (
  3. "errors"
  4. "io/ioutil"
  5. "strings"
  6. "fmt"
  7. "debug/elf"
  8. "path/filepath"
  9. "github.com/coroot/coroot-node-agent/utils"
  10. "github.com/cilium/ebpf/link"
  11. "golang.org/x/arch/x86/x86asm"
  12. )
  13. const (
  14. // goServeHTTP = "net/http.serverHandler.ServeHTTP"
  15. // binPath = "/root/code/jdk8u/build/linux-x86_64-normal-server-release/jdk/lib/amd64/libnio.so"
  16. symbolsocketRead0 = "Java_sun_nio_ch_FileDispatcherImpl_read0"
  17. )
  18. func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, insID utils.ID) []link.Link {
  19. if t.disableL7Tracing {
  20. return nil
  21. }
  22. version := UsePIDToGetJDKVersion(pid)
  23. fmt.Println("java version is ", version)
  24. var links []link.Link
  25. bpath := getSoPath(pid, "nio.so")
  26. if bpath == ""{
  27. fmt.Println("can,t find the nio.so")
  28. return nil
  29. }
  30. fmt.Println("find the nio.so path is ", bpath)
  31. ex, err := link.OpenExecutable(bpath)
  32. if err != nil {
  33. return nil
  34. }
  35. ef, err := elf.Open(bpath)
  36. if err != nil {
  37. return nil
  38. }
  39. defer ef.Close()
  40. symbols, err := ef.Symbols()
  41. if err != nil {
  42. if errors.Is(err, elf.ErrNoSymbols) {
  43. return nil
  44. }
  45. return nil
  46. }
  47. textSection := ef.Section(".text")
  48. if textSection == nil {
  49. return nil
  50. }
  51. textSectionData, err := textSection.Data()
  52. if err != nil {
  53. return nil
  54. }
  55. textSectionLen := uint64(len(textSectionData) - 1)
  56. // opt := link.UprobeOptions{
  57. // Offset: 61,
  58. // }
  59. // upread02, err := ex.Uprobe(symbolsocketRead0, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &opt)
  60. // if err != nil {
  61. // return nil
  62. // }
  63. // links = append(links, upread01)
  64. for _, s := range symbols {
  65. if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 {
  66. continue
  67. }
  68. switch s.Name {
  69. case symbolsocketRead0:
  70. default:
  71. continue
  72. }
  73. address := s.Value
  74. for _, p := range ef.Progs {
  75. if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 {
  76. continue
  77. }
  78. if p.Vaddr <= s.Value && s.Value < (p.Vaddr+p.Memsz) {
  79. address = s.Value - p.Vaddr + p.Off
  80. break
  81. }
  82. }
  83. fmt.Println("s.Name-----:", s.Name)
  84. switch s.Name {
  85. case symbolsocketRead0:
  86. sStart := s.Value - textSection.Addr
  87. sEnd := sStart + s.Size
  88. if sEnd > textSectionLen {
  89. continue
  90. }
  91. sBytes := textSectionData[sStart:sEnd]
  92. returnOffsets := getCallNextMoveOffsets(ef.Machine, sBytes)
  93. if len(returnOffsets) == 0 {
  94. return nil
  95. }
  96. for _, offset := range returnOffsets {
  97. l, err := ex.Uprobe(s.Name, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
  98. if err != nil {
  99. fmt.Println("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe")
  100. return nil
  101. }
  102. links = append(links, l)
  103. }
  104. }
  105. }
  106. if len(links) == 0 {
  107. return nil
  108. }
  109. fmt.Println("jvm uprobes attached, pid is ", pid)
  110. return links
  111. }
  112. func getCallNextMoveOffsets(machine elf.Machine, instructions []byte) []int {
  113. var res []int
  114. firstCall := 0
  115. switch machine {
  116. case elf.EM_X86_64:
  117. for i := 0; i < len(instructions); {
  118. ins, err := x86asm.Decode(instructions[i:], 64)
  119. if err == nil && ins.Op == x86asm.CALL {
  120. if firstCall == 0{
  121. firstCall = 1
  122. }else{
  123. i += ins.Len
  124. res = append(res, i)
  125. return res
  126. }
  127. }
  128. i += ins.Len
  129. }
  130. }
  131. return res
  132. }
  133. func getSoPath(pid uint32, soname string) string{
  134. // 获取进程的maps文件路径
  135. mapsPath := fmt.Sprintf("/proc/%d/maps", pid)
  136. // 读取maps文件内容
  137. mapsData, err := ioutil.ReadFile(mapsPath)
  138. if err != nil {
  139. fmt.Println("无法读取maps文件")
  140. return ""
  141. }
  142. lines := strings.Split(string(mapsData), "\n")
  143. for _, line := range lines {
  144. fields := strings.Fields(line)
  145. if len(fields) >= 6 {
  146. perms := fields[1]
  147. path := fields[len(fields)-1]
  148. if strings.Contains(perms, "x") && filepath.Ext(path) == ".so" {
  149. if strings.Contains(path, soname){
  150. return path
  151. }
  152. }
  153. }
  154. }
  155. return ""
  156. }