jvm.go 5.1 KB

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