python.go 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. package ebpftracer
  2. import (
  3. "bufio"
  4. "os"
  5. "regexp"
  6. "strings"
  7. "github.com/cilium/ebpf/link"
  8. "github.com/coroot/coroot-node-agent/proc"
  9. "golang.org/x/exp/maps"
  10. "k8s.io/klog/v2"
  11. )
  12. var (
  13. libcRegexp = regexp.MustCompile(`libc[\.-]`)
  14. muslRegexp = regexp.MustCompile(`musl[\.-]`)
  15. )
  16. func (t *Tracer) AttachPythonThreadLockProbes(pid uint32) []link.Link {
  17. log := func(libPath, msg string, err error) {
  18. if err != nil {
  19. for _, s := range []string{"no such file or directory", "no such process", "permission denied"} {
  20. if strings.HasSuffix(err.Error(), s) {
  21. return
  22. }
  23. }
  24. klog.ErrorfDepth(1, "pid=%d lib=%s: %s: %s", pid, libPath, msg, err)
  25. return
  26. }
  27. klog.InfofDepth(1, "pid=%d lib=%s: %s", pid, libPath, msg)
  28. }
  29. var (
  30. lastErr error
  31. links []link.Link
  32. libPath string
  33. )
  34. for _, libPath = range getPthreadLibs(pid) {
  35. exe, err := link.OpenExecutable(libPath)
  36. if err != nil {
  37. log(libPath, "failed to open executable", err)
  38. return nil
  39. }
  40. var uprobe, uretprobe link.Link
  41. uprobe, lastErr = exe.Uprobe("pthread_cond_timedwait", t.uprobes["pthread_cond_timedwait_enter"], nil)
  42. if lastErr != nil {
  43. continue
  44. }
  45. links = append(links, uprobe)
  46. uretprobe, lastErr = exe.Uretprobe("pthread_cond_timedwait", t.uprobes["pthread_cond_timedwait_exit"], nil)
  47. if lastErr != nil {
  48. continue
  49. }
  50. links = append(links, uretprobe)
  51. log(libPath, "python uprobes attached", nil)
  52. break
  53. }
  54. if lastErr != nil {
  55. log(libPath, "failed to attach uprobe", lastErr)
  56. }
  57. return links
  58. }
  59. func getPthreadLibs(pid uint32) []string {
  60. f, err := os.Open(proc.Path(pid, "maps"))
  61. if err != nil {
  62. return nil
  63. }
  64. defer f.Close()
  65. scanner := bufio.NewScanner(f)
  66. scanner.Split(bufio.ScanLines)
  67. libs := map[string]bool{}
  68. for scanner.Scan() {
  69. parts := strings.Fields(scanner.Text())
  70. if len(parts) <= 5 {
  71. continue
  72. }
  73. libPath := parts[5]
  74. if libcRegexp.MatchString(libPath) || muslRegexp.MatchString(libPath) || strings.Contains(libPath, "libpthread") {
  75. libs[proc.Path(pid, "root", libPath)] = true
  76. }
  77. }
  78. return maps.Keys(libs)
  79. }