github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/process_manager.go (about)

     1  package instrumentation
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"reflect"
     7  	"sync/atomic"
     8  	"time"
     9  	"unsafe"
    10  
    11  	"github.com/Rookout/GoSDK/pkg/augs"
    12  	"github.com/Rookout/GoSDK/pkg/locations_set"
    13  	"github.com/Rookout/GoSDK/pkg/logger"
    14  	"github.com/Rookout/GoSDK/pkg/rookoutErrors"
    15  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/binary_info"
    16  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/callback"
    17  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/hooker"
    18  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/hooker/prologue"
    19  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/module"
    20  	"github.com/Rookout/GoSDK/pkg/utils"
    21  )
    22  
    23  const maxStackFrames = 128 
    24  
    25  type ProcessManager struct {
    26  	hooker            hooker.Hooker
    27  	binaryInfo        *binary_info.BinaryInfo
    28  	locations         *locations_set.LocationsSet
    29  	trampolineManager *trampolineManager
    30  	cancelGCControl   context.CancelFunc
    31  	cancelBPMonitor   context.CancelFunc
    32  }
    33  
    34  func NewProcessManager(locations *locations_set.LocationsSet, breakpointMonitorInterval time.Duration) (pm *ProcessManager, err rookoutErrors.RookoutError) {
    35  	bi, err := createBinaryInfo()
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	h, err := createHooker()
    41  	defer func() {
    42  		if err != nil {
    43  			_ = hooker.Destroy()
    44  		}
    45  	}()
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	module.Init()
    51  	p := &ProcessManager{hooker: h, binaryInfo: bi, locations: locations, trampolineManager: newTrampolineManager()}
    52  
    53  	ctxGCControl, cancelGCControl := context.WithCancel(context.Background())
    54  	defer func() {
    55  		if err != nil {
    56  			cancelGCControl()
    57  		}
    58  	}()
    59  	p.cancelGCControl = cancelGCControl
    60  	triggerChan := make(chan bool, 10000)
    61  	gcController := newGCController()
    62  	utils.CreateGoroutine(func() {
    63  		gcController.start(ctxGCControl, triggerChan)
    64  	})
    65  	callback.SetTriggerChan(triggerChan)
    66  
    67  	ctxBPMonitor, cancelBPMonitor := context.WithCancel(context.Background())
    68  	defer func() {
    69  		if err != nil {
    70  			cancelBPMonitor()
    71  		}
    72  	}()
    73  	p.cancelBPMonitor = cancelBPMonitor
    74  	utils.CreateGoroutine(func() {
    75  		p.monitorBreakpoints(ctxBPMonitor, breakpointMonitorInterval)
    76  	})
    77  
    78  	err = prologue.Init(p.binaryInfo)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return p, nil
    84  }
    85  
    86  func (p *ProcessManager) monitorBreakpoints(ctx context.Context, breakpointMonitorInterval time.Duration) {
    87  	for {
    88  		monitorTimeout := time.NewTimer(breakpointMonitorInterval)
    89  		select {
    90  		case <-ctx.Done():
    91  			return
    92  
    93  		case <-monitorTimeout.C:
    94  			bpInstances := p.locations.GetBreakpointInstances()
    95  			for _, bpInstance := range bpInstances {
    96  				failedCounter := atomic.SwapUint64(bpInstance.FailedCounter, 0)
    97  				if failedCounter > 0 {
    98  					locations, ok := p.locations.FindLocationsByBreakpointName(bpInstance.Breakpoint.Name)
    99  					if !ok {
   100  						continue
   101  					}
   102  
   103  					for i := range locations {
   104  						locations[i].SetError(rookoutErrors.NewFailedToExecuteBreakpoint(failedCounter))
   105  					}
   106  				}
   107  			}
   108  		}
   109  	}
   110  }
   111  
   112  func createHooker() (hooker.Hooker, rookoutErrors.RookoutError) {
   113  	h := hooker.New(unsafe.Pointer(reflect.ValueOf(callback.Callback).Pointer()))
   114  
   115  	err := hooker.Init(funcForInit)
   116  	if err != nil {
   117  		logger.Logger().WithError(err).Errorf("Unable to start hooker")
   118  		return nil, err
   119  	}
   120  
   121  	return h, nil
   122  }
   123  
   124  func createBinaryInfo() (*binary_info.BinaryInfo, rookoutErrors.RookoutError) {
   125  	bi := binary_info.NewBinaryInfo()
   126  	exec, err := os.Executable()
   127  	if err != nil {
   128  		return nil, rookoutErrors.NewFailedToGetExecutable(err)
   129  	}
   130  	err = bi.LoadBinaryInfo(exec, binary_info.GetEntrypoint(exec), nil)
   131  	if err != nil {
   132  		return nil, rookoutErrors.NewFailedToLoadBinaryInfo(err)
   133  	}
   134  	bi.Dwarf = bi.Images[0].Dwarf 
   135  
   136  	callback.SetBinaryInfo(bi)
   137  	return bi, nil
   138  }
   139  
   140  func (p *ProcessManager) WriteBreakpoint(filename string, lineno int) (*augs.Breakpoint, rookoutErrors.RookoutError) {
   141  	addrs, _, _, err := p.binaryInfo.FindFileLocation(filename, lineno)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	if bp, ok := p.locations.FindBreakpointByAddrs(addrs); ok {
   146  		return bp, nil
   147  	}
   148  
   149  	return writeBreakpoint(filename, lineno, p.getFunction, addrs, p.locations.BreakpointStorage, p.binaryInfo, p.hooker)
   150  }
   151  
   152  func (p *ProcessManager) getFunction(addr uint64) (*binary_info.Function, *augs.Function, rookoutErrors.RookoutError) {
   153  	filename, lineno, biFunction := p.binaryInfo.PCToLine(addr)
   154  	if function, ok := p.locations.FindFunctionByEntry(biFunction.Entry); ok {
   155  		return biFunction, function, nil
   156  	}
   157  
   158  	function, err := NewFunction(biFunction, filename, lineno, p.binaryInfo, p.hooker, p.trampolineManager)
   159  	if err != nil {
   160  		return nil, nil, err
   161  	}
   162  	p.locations.AddFunction(function)
   163  	return biFunction, function, nil
   164  }
   165  
   166  func (p *ProcessManager) EraseBreakpoint(bp *augs.Breakpoint) rookoutErrors.RookoutError {
   167  	return eraseBreakpoint(bp, p.hooker, p.locations.BreakpointStorage)
   168  }
   169  
   170  
   171  func (_ *ProcessManager) Clean() rookoutErrors.RookoutError {
   172  	err := hooker.Destroy()
   173  	if err != nil {
   174  		return rookoutErrors.NewFailedToDestroyNative(err)
   175  	}
   176  	return nil
   177  }