package containers import ( "bytes" "context" "fmt" "os" "path/filepath" "strings" "time" "github.com/coroot/coroot-node-agent/ebpftracer/tracer/jattach" . "github.com/coroot/coroot-node-agent/utils/modelse" "github.com/cilium/ebpf/link" "github.com/coroot/coroot-node-agent/ebpftracer" "github.com/coroot/coroot-node-agent/proc" "github.com/jpillora/backoff" "github.com/mdlayher/taskstats" ) type Process struct { Pid uint32 StartedAt time.Time netNsId string ctx context.Context cancelFunc context.CancelFunc dotNetMonitor *DotNetMonitor isGolangApp bool uprobes []link.Link stackUprobes []link.Link goTlsUprobesChecked bool openSslUprobesChecked bool jvmAttachOnce bool stackAttachOnce bool stackStatus StackStatus versionFailed bool codeType CodeType cmdline string pythonGilChecked bool } func NewProcess(pid uint32, stats *taskstats.Stats, tracer *ebpftracer.Tracer) *Process { p := &Process{Pid: pid, StartedAt: stats.BeginTime} p.ctx, p.cancelFunc = context.WithCancel(context.Background()) //go p.instrument(tracer) return p } func (p *Process) NetNsId() string { if p.netNsId == "" { ns, err := proc.GetNetNs(p.Pid) if err != nil { return "" } p.netNsId = ns.UniqueId() _ = ns.Close() } return p.netNsId } func (p *Process) isHostNs() bool { return p.NetNsId() == hostNetNsId } func (p *Process) instrument(tracer *ebpftracer.Tracer) { b := backoff.Backoff{Factor: 2, Min: time.Second, Max: time.Minute} for { select { case <-p.ctx.Done(): return default: dest, err := os.Readlink(proc.Path(p.Pid, "exe")) if err != nil { return } if dest != "/" { p.instrumentPython(tracer) if dotNetAppName, err := dotNetApp(p.Pid); err == nil { if dotNetAppName != "" { p.dotNetMonitor = NewDotNetMonitor(p.ctx, p.Pid, dotNetAppName) } } return } time.Sleep(b.Duration()) } } } func (p *Process) instrumentPython(tracer *ebpftracer.Tracer) { if p.pythonGilChecked { return } p.pythonGilChecked = true cmdline := proc.GetCmdline(p.Pid) if len(cmdline) == 0 { return } parts := bytes.Split(cmdline, []byte{0}) cmd := parts[0] if len(cmd) == 0 { return } cmd = bytes.TrimSuffix(bytes.Fields(cmd)[0], []byte{':'}) if !pythonCmd.Match(cmd) { return } p.uprobes = append(p.uprobes, tracer.AttachPythonThreadLockProbes(p.Pid)...) } func (p *Process) Close() { p.cancelFunc() for _, u := range p.uprobes { _ = u.Close() } } func (p *Process) closeStackUprobes() error { for _, u := range p.stackUprobes { err := u.Close() if err != nil { return err } } p.stackUprobes = []link.Link{} p.stackStatus.StackUprobesClose() return nil } func (p *Process) DynamicClose(closeType int) { for _, u := range p.uprobes { _ = u.Close() } //p.goTlsUprobesChecked = false //p.openSslUprobesChecked = false //p.jvmUprobesChecked = false //if p.codeType.IsJvmCode() && p.stackUprobesChecked { // nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent") // kvPairs := []string{ // fmt.Sprintf("%s=%s", filepath.Join(nativeBasePath, "lib", "apmAgent.jar"), nativeBasePath), // "UNINSTALL", // } // argkv := strings.Join(kvPairs, ",") // args := []string{"load", "instrument", "false", argkv} // jattacher := jattach.JvmJattacher{ // Pid: p.Pid, // Args: args, // PrintOutput: 1, // } // _, err := jattacher.JDetach() // if err == nil { // //p.stackUprobesChecked = false // } //} p.uprobes = []link.Link{} } func (p *Process) uninstallJavaAgent() error { if p.codeType.IsJvmCode() && p.stackAttachOnce { //nativeBasePath := utils.GetDefaultAgentsPath("NativeAgent") nativeBasePath := "/tmp/NativeAgent" kvPairs := []string{ fmt.Sprintf("%s=%s", filepath.Join(nativeBasePath, "lib", "apmAgent.jar"), nativeBasePath), "UNINSTALL", } argkv := strings.Join(kvPairs, ",") args := []string{"load", "instrument", "false", argkv} jattacher := jattach.JvmJattacher{ Pid: p.Pid, Args: args, PrintOutput: 1, } _, err := jattacher.JDetach() if err != nil { return err } p.stackStatus.JattachClose() return nil } return nil } func (p *Process) GetCmdline() string { return p.cmdline }