github.com/ks888/tgo@v0.0.0-20190130135156-80bf89407292/tracer/breakpoints.go (about)

     1  package tracer
     2  
     3  // Breakpoints manages the breakpoints. The breakpoint can be conditional, which means the breakpoint is considered as hit
     4  // only when the specific conditions are met.
     5  type Breakpoints struct {
     6  	currBreakpoints map[uint64]*conditionalBreakpoint
     7  	doSet           func(addr uint64) error
     8  	doClear         func(addr uint64) error
     9  }
    10  
    11  // NewBreakpoints returns new Breakpoints. Pass the functions to actually set and clear breakpoints.
    12  func NewBreakpoints(setBreakpiont, clearBreakpiont func(addr uint64) error) Breakpoints {
    13  	return Breakpoints{currBreakpoints: make(map[uint64]*conditionalBreakpoint), doSet: setBreakpiont, doClear: clearBreakpiont}
    14  }
    15  
    16  // Hit returns true if the breakpoint is not conditional or the condtional breakpoint meets its condition.
    17  func (b Breakpoints) Hit(addr uint64, goRoutineID int64) bool {
    18  	bp, ok := b.currBreakpoints[addr]
    19  	return ok && bp.Hit(goRoutineID)
    20  }
    21  
    22  // Exist returns true if the breakpoint exists.
    23  func (b Breakpoints) Exist(addr uint64) bool {
    24  	_, ok := b.currBreakpoints[addr]
    25  	return ok
    26  }
    27  
    28  // Clear clears the breakpoint at the specified address. Conditonal breakpoints for the same address are also cleared.
    29  func (b Breakpoints) Clear(addr uint64) error {
    30  	_, ok := b.currBreakpoints[addr]
    31  	if !ok {
    32  		return nil
    33  	}
    34  
    35  	if err := b.doClear(addr); err != nil {
    36  		return err
    37  	}
    38  
    39  	delete(b.currBreakpoints, addr)
    40  	return nil
    41  }
    42  
    43  // ClearConditional clears the conditional breakpoint for the specified address and go routine.
    44  // The physical breakpoint for the specified address may still exist if other conditional breakpoints specify
    45  // to that address.
    46  func (b Breakpoints) ClearConditional(addr uint64, goRoutineID int64) error {
    47  	bp, ok := b.currBreakpoints[addr]
    48  	if !ok {
    49  		return nil
    50  	}
    51  	bp.Disassociate(goRoutineID)
    52  
    53  	if !bp.NoAssociation() {
    54  		return nil
    55  	}
    56  
    57  	return b.Clear(addr)
    58  }
    59  
    60  // ClearAllByGoRoutineID clears all the breakpoints associated with the specified go routine.
    61  func (b Breakpoints) ClearAllByGoRoutineID(goRoutineID int64) error {
    62  	for addr, bp := range b.currBreakpoints {
    63  		for bp.Disassociate(goRoutineID) {
    64  		}
    65  
    66  		if !bp.NoAssociation() {
    67  			continue
    68  		}
    69  		if err := b.Clear(addr); err != nil {
    70  			return err
    71  		}
    72  	}
    73  
    74  	return nil
    75  }
    76  
    77  // Set sets the breakpoint at the specified address.
    78  // If `SetConditional` is called before for the same address, the conditions are removed.
    79  func (b Breakpoints) Set(addr uint64) error {
    80  	_, ok := b.currBreakpoints[addr]
    81  	if !ok {
    82  		if err := b.doSet(addr); err != nil {
    83  			return err
    84  		}
    85  	}
    86  
    87  	b.currBreakpoints[addr] = &conditionalBreakpoint{addr: addr, associateAll: true}
    88  	return nil
    89  }
    90  
    91  // SetConditional sets the conditional breakpoint which only the specified go routine is considered as hit.
    92  // If `Set` is called before for the same address, this function is no-op.
    93  func (b Breakpoints) SetConditional(addr uint64, goRoutineID int64) error {
    94  	bp, ok := b.currBreakpoints[addr]
    95  	if ok {
    96  		if !bp.NoAssociation() {
    97  			bp.Associate(goRoutineID)
    98  		}
    99  		return nil
   100  	}
   101  
   102  	if err := b.doSet(addr); err != nil {
   103  		return err
   104  	}
   105  
   106  	bp = &conditionalBreakpoint{addr: addr}
   107  	bp.Associate(goRoutineID)
   108  	b.currBreakpoints[addr] = bp
   109  	return nil
   110  }
   111  
   112  type association struct {
   113  	goRoutineID int64
   114  }
   115  
   116  // conditionalBreakpoint is the breakpoint which holds go routine id conditions to be considered as 'hit'
   117  type conditionalBreakpoint struct {
   118  	addr         uint64
   119  	associateAll bool
   120  	associations []int64
   121  }
   122  
   123  // Hit returns true if the specified go routine id is associated.
   124  func (b *conditionalBreakpoint) Hit(goRoutineID int64) bool {
   125  	if b.associateAll {
   126  		return true
   127  	}
   128  
   129  	for _, association := range b.associations {
   130  		if association == goRoutineID {
   131  			return true
   132  		}
   133  	}
   134  
   135  	return false
   136  }
   137  
   138  // NoAssociation returns true if the breakpoint has no associations.
   139  func (b *conditionalBreakpoint) NoAssociation() bool {
   140  	return !b.associateAll && len(b.associations) == 0
   141  }
   142  
   143  // Associate associates the specified go routine. Multiple same go routine id can be associated
   144  // because it's useful in the recursive call's case.
   145  func (b *conditionalBreakpoint) Associate(goRoutineID int64) {
   146  	if b.associateAll {
   147  		return
   148  	}
   149  
   150  	b.associations = append(b.associations, goRoutineID)
   151  	return
   152  }
   153  
   154  // Disassociate disassociates the specified go routine. It returns true if actually disassociated.
   155  func (b *conditionalBreakpoint) Disassociate(goRoutineID int64) bool {
   156  	if b.associateAll {
   157  		return false
   158  	}
   159  
   160  	for i, association := range b.associations {
   161  		if association == goRoutineID {
   162  			b.associations = append(b.associations[0:i], b.associations[i+1:len(b.associations)]...)
   163  			return true
   164  		}
   165  	}
   166  	return false
   167  }