github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/breakpoint.go (about) 1 package instrumentation 2 3 import ( 4 "strings" 5 6 "github.com/Rookout/GoSDK/pkg/augs" 7 "github.com/Rookout/GoSDK/pkg/locations_set" 8 "github.com/Rookout/GoSDK/pkg/logger" 9 "github.com/Rookout/GoSDK/pkg/rookoutErrors" 10 "github.com/Rookout/GoSDK/pkg/services/collection/variable" 11 "github.com/Rookout/GoSDK/pkg/services/instrumentation/binary_info" 12 "github.com/Rookout/GoSDK/pkg/services/instrumentation/hooker" 13 "github.com/Rookout/GoSDK/pkg/services/instrumentation/module" 14 "github.com/google/uuid" 15 ) 16 17 type functionGetter func(addr uint64) (*binary_info.Function, *augs.Function, rookoutErrors.RookoutError) 18 19 type breakpointWriter struct { 20 filename string 21 lineno int 22 getFunction functionGetter 23 storage *locations_set.BreakpointStorage 24 binaryInfo *binary_info.BinaryInfo 25 hooker hooker.Hooker 26 addrs []uint64 27 } 28 29 func writeBreakpoint(filename string, lineno int, getFunction functionGetter, addrs []uint64, storage *locations_set.BreakpointStorage, binaryInfo *binary_info.BinaryInfo, hooker hooker.Hooker) (*augs.Breakpoint, rookoutErrors.RookoutError) { 30 breakpointWriter := breakpointWriter{ 31 filename: filename, 32 lineno: lineno, 33 getFunction: getFunction, 34 storage: storage, 35 binaryInfo: binaryInfo, 36 hooker: hooker, 37 addrs: addrs, 38 } 39 return breakpointWriter.writeBreakpoint() 40 } 41 42 func (b *breakpointWriter) writeBreakpoint() (*augs.Breakpoint, rookoutErrors.RookoutError) { 43 breakpointID, rookErr := createBreakpointID() 44 if rookErr != nil { 45 return nil, rookErr 46 } 47 bp := &augs.Breakpoint{ 48 File: b.filename, 49 Line: b.lineno, 50 Stacktrace: maxStackFrames, 51 Name: "rookout" + breakpointID, 52 } 53 54 for _, addr := range b.addrs { 55 bpInstance, err := b.writeBreakpointInstance(bp, addr) 56 if err != nil { 57 return nil, err 58 } 59 bp.Instances = append(bp.Instances, bpInstance) 60 } 61 return bp, nil 62 } 63 64 func (b *breakpointWriter) writeBreakpointInstance(bp *augs.Breakpoint, addr uint64) (*augs.BreakpointInstance, rookoutErrors.RookoutError) { 65 if bpInstance, ok := b.storage.FindBreakpointByAddr(addr); ok { 66 return bpInstance, nil 67 } 68 69 biFunction, function, rookErr := b.getFunction(addr) 70 if rookErr != nil { 71 return nil, rookErr 72 } 73 bpInstance := augs.NewBreakpointInstance(addr, bp, function) 74 75 logger.Logger().Debugf("Adding breakpoint in %s:%d address=0x%x", b.filename, b.lineno, addr) 76 77 flowRunner, err := b.hooker.StartWritingBreakpoint(bpInstance) 78 if err != nil { 79 return nil, rookoutErrors.NewFailedToAddBreakpoint(b.filename, b.lineno, err) 80 } 81 82 rookErr = applyBreakpointState(flowRunner, b.filename, b.lineno) 83 if rookErr != nil { 84 return nil, rookErr 85 } 86 87 variableLocators, err := variable.GetVariableLocators(addr, b.lineno, biFunction, b.binaryInfo) 88 if err != nil { 89 return nil, rookoutErrors.NewFailedToGetVariableLocators(b.filename, b.lineno, err) 90 } 91 bpInstance.VariableLocators = variableLocators 92 93 b.storage.AddBreakpointInstance(bpInstance) 94 return bpInstance, nil 95 } 96 97 type breakpointEraser struct { 98 breakpoint *augs.Breakpoint 99 hooker hooker.Hooker 100 storage *locations_set.BreakpointStorage 101 } 102 103 func eraseBreakpoint(breakpoint *augs.Breakpoint, hooker hooker.Hooker, storage *locations_set.BreakpointStorage) rookoutErrors.RookoutError { 104 breakpointEraser := &breakpointEraser{ 105 breakpoint: breakpoint, 106 hooker: hooker, 107 storage: storage, 108 } 109 return breakpointEraser.eraseBreakpoint() 110 } 111 112 func (b *breakpointEraser) eraseBreakpoint() rookoutErrors.RookoutError { 113 var remainingInstances []*augs.BreakpointInstance 114 for _, bpInstance := range b.breakpoint.Instances { 115 err := b.eraseBreakpointInstance(bpInstance) 116 if err != nil { 117 logger.Logger().WithError(err).Warningf("Failed to erase an instance of the breakpoint") 118 remainingInstances = append(remainingInstances, bpInstance) 119 } 120 } 121 122 if remainingInstances != nil { 123 b.breakpoint.Instances = remainingInstances 124 return rookoutErrors.NewFailedToEraseAllBreakpointInstances() 125 } 126 return nil 127 } 128 129 func (b *breakpointEraser) eraseBreakpointInstance(bpInstance *augs.BreakpointInstance) rookoutErrors.RookoutError { 130 flowRunner, err := b.hooker.StartErasingBreakpoint(bpInstance) 131 if err != nil { 132 return rookoutErrors.NewFailedToRemoveBreakpoint(b.breakpoint.File, b.breakpoint.Line, err) 133 } 134 135 rookErr := applyBreakpointState(flowRunner, b.breakpoint.File, b.breakpoint.Line) 136 if rookErr != nil { 137 return rookErr 138 } 139 b.storage.RemoveBreakpointInstance(bpInstance) 140 141 return nil 142 } 143 144 func createBreakpointID() (string, rookoutErrors.RookoutError) { 145 breakpointID, err := uuid.NewUUID() 146 if err != nil { 147 return "", rookoutErrors.NewFailedToCreateID(err) 148 } 149 return strings.ReplaceAll(breakpointID.String(), "-", ""), nil 150 } 151 152 153 func applyBreakpointState(runner hooker.BreakpointFlowRunner, filename string, lineno int) rookoutErrors.RookoutError { 154 if !runner.IsUnhookedState() { 155 addressMappings, offsetMappings, err := runner.GetAddressMapping() 156 if err != nil { 157 return rookoutErrors.NewFailedToGetAddressMapping(filename, lineno, err) 158 } 159 160 161 162 163 if err = module.PatchModuleData(addressMappings, offsetMappings, runner.ID()); err != nil { 164 return rookoutErrors.NewFailedToPatchModule(filename, lineno, err) 165 } 166 } 167 168 err := runner.ApplyBreakpointsState() 169 if err != nil { 170 return rookoutErrors.NewFailedToApplyBreakpointState(filename, lineno, err) 171 } 172 173 return nil 174 }