github.com/ks888/tgo@v0.0.0-20190130135156-80bf89407292/service/service.go (about) 1 package service 2 3 import ( 4 "errors" 5 "net" 6 "net/rpc" 7 "sync" 8 9 "github.com/ks888/tgo/log" 10 "github.com/ks888/tgo/tracer" 11 ) 12 13 const serviceVersion = 1 // increment whenever any changes are aded to service methods. 14 15 // Tracer is the wrapper of the actual tracer in tgo/tracer package. 16 // 17 // The simple name 'Tracer' is chosen because it becomes a part of the service methods 18 // the rpc client uses. 19 type Tracer struct { 20 controller *tracer.Controller 21 errCh chan error 22 mtx sync.Mutex // protects controller 23 } 24 25 // AttachArgs is the input argument of the service method 'Tracer.Attach' 26 type AttachArgs struct { 27 Pid int 28 TraceLevel, ParseLevel int 29 // This parameter is required because the tracer may not have a chance to set the new trace points 30 // after the attached tracee starts running without trace points. 31 InitialStartTracePoint uintptr 32 Verbose bool 33 GoVersion, ProgramPath string 34 FirstModuleDataAddr uintptr 35 } 36 37 // Version returns the service version. The backward compatibility may be broken if the version is not same as the expected one. 38 func (t *Tracer) Version(args struct{}, reply *int) error { 39 *reply = serviceVersion 40 return nil 41 } 42 43 // Attach lets the server attach to the specified process. It does nothing if the server is already attached. 44 func (t *Tracer) Attach(args AttachArgs, reply *struct{}) error { 45 t.mtx.Lock() 46 defer t.mtx.Unlock() 47 if t.controller != nil { 48 return errors.New("already attached") 49 } 50 51 t.controller = tracer.NewController() 52 attrs := tracer.Attributes{ 53 ProgramPath: args.ProgramPath, 54 CompiledGoVersion: args.GoVersion, 55 FirstModuleDataAddr: uint64(args.FirstModuleDataAddr), 56 } 57 if err := t.controller.AttachTracee(args.Pid, attrs); err != nil { 58 return err 59 } 60 t.controller.SetTraceLevel(args.TraceLevel) 61 t.controller.SetParseLevel(args.ParseLevel) 62 t.controller.AddStartTracePoint(uint64(args.InitialStartTracePoint)) 63 64 go func() { 65 err := t.controller.MainLoop() 66 if err != nil && err != tracer.ErrInterrupted { 67 log.Debug(err) 68 } 69 t.errCh <- err 70 }() 71 return nil 72 } 73 74 // Detach lets the server detach from the attached process. 75 func (t *Tracer) Detach(args struct{}, reply *struct{}) error { 76 t.mtx.Lock() 77 if t.controller == nil { 78 t.mtx.Unlock() 79 return nil 80 } 81 82 // TODO: the tracer may be killed before detached (and before breakpoints cleared). Implement the cancellation mechanism which can wait until the process is detached. 83 t.controller.Interrupt() 84 go func() { 85 defer t.mtx.Unlock() 86 if err := <-t.errCh; err != nil && err != tracer.ErrInterrupted { 87 log.Printf("%v", err) 88 } else { 89 log.Printf("detached") 90 } 91 t.controller = nil 92 }() 93 return nil 94 } 95 96 // AddStartTracePoint adds a new start trace point. 97 func (t *Tracer) AddStartTracePoint(args uintptr, reply *struct{}) error { 98 t.mtx.Lock() 99 defer t.mtx.Unlock() 100 101 if t.controller == nil { 102 return nil 103 } 104 return t.controller.AddStartTracePoint(uint64(args)) 105 } 106 107 // AddEndTracePoint adds a new end trace point. 108 func (t *Tracer) AddEndTracePoint(args uintptr, reply *struct{}) error { 109 t.mtx.Lock() 110 defer t.mtx.Unlock() 111 112 if t.controller == nil { 113 return nil 114 } 115 return t.controller.AddEndTracePoint(uint64(args)) 116 } 117 118 // Serve serves the tracer service. 119 func Serve(address string) error { 120 tracer := &Tracer{errCh: make(chan error)} 121 rpc.Register(tracer) 122 123 listener, err := net.Listen("tcp", address) 124 if err != nil { 125 return err 126 } 127 128 // The server is running only for 1 client. So close the listener socket immediately and 129 // do not create a new go routine for a new connection. 130 conn, err := listener.Accept() 131 listener.Close() 132 if err != nil { 133 return err 134 } 135 136 rpc.ServeConn(conn) 137 conn.Close() // connection may be closed already 138 return nil 139 }