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 }