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  }