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  }