jvm.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. package ebpftracer
  2. import (
  3. "errors"
  4. "github.com/coroot/coroot-node-agent/ebpftracer/tracer/inject"
  5. "github.com/coroot/coroot-node-agent/utils"
  6. . "github.com/coroot/coroot-node-agent/utils/modelse"
  7. klog "github.com/sirupsen/logrus"
  8. "io/ioutil"
  9. "runtime"
  10. "strings"
  11. "debug/elf"
  12. "fmt"
  13. "path/filepath"
  14. "github.com/cilium/ebpf/link"
  15. "github.com/coroot/coroot-node-agent/proc"
  16. "golang.org/x/arch/arm64/arm64asm"
  17. "golang.org/x/arch/x86/x86asm"
  18. )
  19. const (
  20. // goServeHTTP = "net/http.serverHandler.ServeHTTP"
  21. // binPath = "/root/code/jdk8u/build/linux-x86_64-normal-server-release/jdk/lib/amd64/libnio.so"
  22. symbolsocketRead0 = "Java_sun_nio_ch_FileDispatcherImpl_read0"
  23. )
  24. func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType CodeType) ([]link.Link, error) {
  25. if t.DisableL7Tracing() {
  26. return nil, nil
  27. }
  28. var links []link.Link
  29. var bpath = ""
  30. // JavaAOT 逻辑
  31. if codeType.IsJavaAotCode() {
  32. bpath = proc.Path(uint32(pid), "exe")
  33. } else {
  34. version := UsePIDToGetJDKVersion(pid)
  35. klog.Infof("[attach] java version is %s", version)
  36. bpath = getSoPath(pid, "libnio.so")
  37. if bpath == "" {
  38. return nil, errors.New("can not find nio.so")
  39. }
  40. }
  41. klog.Infof("[attach] find the nio.so path is %s", bpath)
  42. ex, err := link.OpenExecutable(bpath)
  43. if err != nil {
  44. return nil, err
  45. }
  46. ef, err := elf.Open(bpath)
  47. if err != nil {
  48. return nil, err
  49. //PID: int(pid),
  50. }
  51. defer ef.Close()
  52. symbols, err := ef.DynamicSymbols()
  53. if err != nil {
  54. if errors.Is(err, elf.ErrNoSymbols) {
  55. return nil, err
  56. }
  57. return nil, err
  58. }
  59. textSection := ef.Section(".text")
  60. if textSection == nil {
  61. return nil, errors.New("can not find .text section")
  62. }
  63. textSectionData, err := textSection.Data()
  64. if err != nil {
  65. return nil, err
  66. }
  67. textSectionLen := uint64(len(textSectionData) - 1)
  68. // opt := link.UprobeOptions{
  69. // Offset: 61,
  70. // }
  71. // upread02, err := ex.Uprobe(symbolsocketRead0, t.uprobes["uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0"], &opt)
  72. // if err != nil {
  73. // return nil
  74. // }
  75. // links = append(links, upread01)
  76. for _, s := range symbols {
  77. if elf.ST_TYPE(s.Info) != elf.STT_FUNC || s.Size == 0 {
  78. continue
  79. }
  80. switch s.Name {
  81. case symbolsocketRead0:
  82. default:
  83. continue
  84. }
  85. address := s.Value
  86. for _, p := range ef.Progs {
  87. if p.Type != elf.PT_LOAD || (p.Flags&elf.PF_X) == 0 {
  88. continue
  89. }
  90. if p.Vaddr <= s.Value && s.Value < (p.Vaddr+p.Memsz) {
  91. address = s.Value - p.Vaddr + p.Off
  92. break
  93. }
  94. }
  95. klog.Infof("[attach] java symbol name is %s", s.Name)
  96. switch s.Name {
  97. case symbolsocketRead0:
  98. sStart := s.Value - textSection.Addr
  99. sEnd := sStart + s.Size
  100. if sEnd > textSectionLen {
  101. continue
  102. }
  103. sBytes := textSectionData[sStart:sEnd]
  104. returnOffsets := getCallNextMoveOffsets(ef.Machine, sBytes)
  105. if len(returnOffsets) == 0 {
  106. return nil, fmt.Errorf("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe")
  107. }
  108. for _, offset := range returnOffsets {
  109. 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)})
  110. if err != nil {
  111. return nil, fmt.Errorf("failed to attach uprobe_ret_Java_sun_nio_ch_FileDispatcherImpl_read0 uprobe")
  112. }
  113. links = append(links, l)
  114. }
  115. }
  116. }
  117. if len(links) == 0 {
  118. return nil, fmt.Errorf("no links found for %s", bpath)
  119. }
  120. klog.WithField("pid", pid).Infof("[attach] libnio attached!")
  121. return links, nil
  122. }
  123. func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32) ([]link.Link, error) {
  124. if t.DisableL7Tracing() {
  125. return nil, nil
  126. }
  127. // 关闭横向串联
  128. if t.DisableE2ETracing() {
  129. return nil, nil
  130. }
  131. cwJvmLibPath := utils.GetDefaultLibsPath("jvm", "cwlibnet.so")
  132. //inject
  133. originFunc := "Java_java_net_SocketOutputStream_socketWrite0"
  134. uProbeData := inject.UprobeData{
  135. Offset: 53,
  136. Func: originFunc,
  137. ELFPath: cwJvmLibPath,
  138. }
  139. if runtime.GOARCH == "arm64" {
  140. uProbeData = inject.UprobeData{
  141. Offset: 8,
  142. Func: "CW_" + originFunc,
  143. ELFPath: cwJvmLibPath,
  144. }
  145. }
  146. jvmInjector := &inject.JvmInjector{
  147. Pid: int(pid),
  148. ReleaseLibNetInfo: inject.LibNetInfo{
  149. LibName: "libnet.so",
  150. FuncSymbol: inject.InstInfo{
  151. SymName: originFunc,
  152. },
  153. },
  154. DebugLibNetInfo: inject.LibNetInfo{
  155. // TODO 根据版本设置
  156. LibName: filepath.Base(uProbeData.ELFPath),
  157. // TODO 根据版本设置
  158. LibPath: uProbeData.ELFPath,
  159. FuncSymbol: inject.InstInfo{
  160. SymName: uProbeData.Func,
  161. },
  162. },
  163. RecodeInfo: inject.LibNetInfo{FuncSymbol: inject.InstInfo{SymName: "CW_RECODE_" + originFunc}},
  164. Uprobe: uProbeData,
  165. }
  166. err := inject.JvmInject(jvmInjector)
  167. if err != nil {
  168. return nil, err
  169. }
  170. var links []link.Link
  171. ex, err := link.OpenExecutable(jvmInjector.Uprobe.ELFPath)
  172. if err != nil {
  173. return nil, err
  174. }
  175. opt := link.UprobeOptions{
  176. Offset: uint64(jvmInjector.Uprobe.Offset),
  177. PID: int(pid),
  178. }
  179. upread02, err := ex.Uprobe(jvmInjector.Uprobe.Func, t.uprobes["uprobe_Java_java_net_SocketOutputStream_socketWrite0"], &opt)
  180. if err != nil {
  181. return nil, err
  182. }
  183. links = append(links, upread02)
  184. if len(links) == 0 {
  185. return nil, errors.New("can not find uprobe_Java_net_SocketOutputStream_socketWrite0")
  186. }
  187. klog.WithField("pid", pid).Infoln("[jvm] libnet attached")
  188. return links, nil
  189. }
  190. func getCallNextMoveOffsets(machine elf.Machine, instructions []byte) []int {
  191. var res []int
  192. firstCall := 0
  193. switch machine {
  194. case elf.EM_X86_64:
  195. for i := 0; i < len(instructions); {
  196. ins, err := x86asm.Decode(instructions[i:], 64)
  197. if err == nil && ins.Op == x86asm.CALL {
  198. if firstCall == 0 {
  199. firstCall = 1
  200. } else {
  201. i += ins.Len
  202. res = append(res, i)
  203. return res
  204. }
  205. }
  206. i += ins.Len
  207. }
  208. case elf.EM_AARCH64:
  209. for i := 0; i < len(instructions); {
  210. ins, err := arm64asm.Decode(instructions[i:])
  211. if err == nil && ins.Op == arm64asm.BL {
  212. if firstCall == 0 {
  213. firstCall = 1
  214. } else {
  215. i += 4
  216. res = append(res, i)
  217. return res
  218. }
  219. }
  220. i += 4
  221. }
  222. }
  223. return res
  224. }
  225. func getSoPath(pid uint32, soname string) string {
  226. // 获取进程的maps文件路径
  227. mapsPath := fmt.Sprintf("/proc/%d/maps", pid)
  228. // 读取maps文件内容
  229. mapsData, err := ioutil.ReadFile(mapsPath)
  230. if err != nil {
  231. fmt.Println("lookup so error.")
  232. return ""
  233. }
  234. lines := strings.Split(string(mapsData), "\n")
  235. for _, line := range lines {
  236. fields := strings.Fields(line)
  237. if len(fields) >= 6 {
  238. perms := fields[1]
  239. path := fields[len(fields)-1]
  240. if strings.Contains(perms, "x") && filepath.Ext(path) == ".so" {
  241. if strings.Contains(path, soname) {
  242. return path
  243. }
  244. }
  245. }
  246. }
  247. return ""
  248. }