jvm.go 7.3 KB

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