process.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package containers
  2. import (
  3. "bytes"
  4. "context"
  5. . "github.com/coroot/coroot-node-agent/utils/modelse"
  6. "os"
  7. "time"
  8. "github.com/cilium/ebpf/link"
  9. "github.com/coroot/coroot-node-agent/ebpftracer"
  10. "github.com/coroot/coroot-node-agent/proc"
  11. "github.com/jpillora/backoff"
  12. "github.com/mdlayher/taskstats"
  13. )
  14. type Process struct {
  15. Pid uint32
  16. StartedAt time.Time
  17. netNsId string
  18. ctx context.Context
  19. cancelFunc context.CancelFunc
  20. dotNetMonitor *DotNetMonitor
  21. isGolangApp bool
  22. uprobes []link.Link
  23. goTlsUprobesChecked bool
  24. openSslUprobesChecked bool
  25. jvmUprobesChecked bool
  26. stackUprobesChecked bool
  27. codeType CodeType
  28. cmdline string
  29. pythonGilChecked bool
  30. }
  31. func NewProcess(pid uint32, stats *taskstats.Stats, tracer *ebpftracer.Tracer) *Process {
  32. p := &Process{Pid: pid, StartedAt: stats.BeginTime}
  33. p.ctx, p.cancelFunc = context.WithCancel(context.Background())
  34. go p.instrument(tracer)
  35. return p
  36. }
  37. func (p *Process) NetNsId() string {
  38. if p.netNsId == "" {
  39. ns, err := proc.GetNetNs(p.Pid)
  40. if err != nil {
  41. return ""
  42. }
  43. p.netNsId = ns.UniqueId()
  44. _ = ns.Close()
  45. }
  46. return p.netNsId
  47. }
  48. func (p *Process) isHostNs() bool {
  49. return p.NetNsId() == hostNetNsId
  50. }
  51. func (p *Process) instrument(tracer *ebpftracer.Tracer) {
  52. b := backoff.Backoff{Factor: 2, Min: time.Second, Max: time.Minute}
  53. for {
  54. select {
  55. case <-p.ctx.Done():
  56. return
  57. default:
  58. dest, err := os.Readlink(proc.Path(p.Pid, "exe"))
  59. if err != nil {
  60. return
  61. }
  62. if dest != "/" {
  63. p.instrumentPython(tracer)
  64. if dotNetAppName, err := dotNetApp(p.Pid); err == nil {
  65. if dotNetAppName != "" {
  66. p.dotNetMonitor = NewDotNetMonitor(p.ctx, p.Pid, dotNetAppName)
  67. }
  68. }
  69. return
  70. }
  71. time.Sleep(b.Duration())
  72. }
  73. }
  74. }
  75. func (p *Process) instrumentPython(tracer *ebpftracer.Tracer) {
  76. if p.pythonGilChecked {
  77. return
  78. }
  79. p.pythonGilChecked = true
  80. cmdline := proc.GetCmdline(p.Pid)
  81. if len(cmdline) == 0 {
  82. return
  83. }
  84. parts := bytes.Split(cmdline, []byte{0})
  85. cmd := parts[0]
  86. if len(cmd) == 0 {
  87. return
  88. }
  89. cmd = bytes.TrimSuffix(bytes.Fields(cmd)[0], []byte{':'})
  90. if !pythonCmd.Match(cmd) {
  91. return
  92. }
  93. p.uprobes = append(p.uprobes, tracer.AttachPythonThreadLockProbes(p.Pid)...)
  94. }
  95. func (p *Process) Close() {
  96. p.cancelFunc()
  97. for _, u := range p.uprobes {
  98. _ = u.Close()
  99. }
  100. }
  101. func (p *Process) DynamicClose() {
  102. for _, u := range p.uprobes {
  103. _ = u.Close()
  104. }
  105. p.goTlsUprobesChecked = false
  106. p.openSslUprobesChecked = false
  107. p.jvmUprobesChecked = false
  108. p.stackUprobesChecked = false
  109. p.uprobes = []link.Link{}
  110. }
  111. func (p *Process) GetCmdline() string {
  112. return p.cmdline
  113. }