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 }