github.com/Rookout/GoSDK@v0.1.48/pkg/services/collection/variable/map_iterator.go (about) 1 // The MIT License (MIT) 2 3 // Copyright (c) 2014 Derek Parker 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 // this software and associated documentation files (the "Software"), to deal in 7 // the Software without restriction, including without limitation the rights to 8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 // the Software, and to permit persons to whom the Software is furnished to do so, 10 // subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 package variable 23 24 import ( 25 "errors" 26 "fmt" 27 "reflect" 28 29 "github.com/Rookout/GoSDK/pkg/services/collection/memory" 30 "github.com/Rookout/GoSDK/pkg/services/instrumentation/binary_info" 31 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/godwarf" 32 ) 33 34 type mapIterator struct { 35 v *Variable 36 numbuckets uint64 37 oldmask uint64 38 buckets *Variable 39 oldbuckets *Variable 40 b *Variable 41 bidx uint64 42 43 tophashes *Variable 44 keys *Variable 45 values *Variable 46 overflow *Variable 47 48 idx int64 49 50 hashTophashEmptyOne uint64 51 hashMinTopHash uint64 52 } 53 54 55 func (v *Variable) newMapIterator() (*mapIterator, error) { 56 sv := v.clone() 57 sv.RealType = resolveTypedef(&(sv.RealType.(*godwarf.MapType).TypedefType)) 58 sv = sv.MaybeDereference() 59 v.Base = sv.Addr 60 61 maptype, ok := sv.RealType.(*godwarf.StructType) 62 if !ok { 63 return nil, fmt.Errorf("wrong real type for map") 64 } 65 66 it := &mapIterator{v: v, bidx: 0, b: nil, idx: 0} 67 68 if sv.Addr == 0 { 69 it.numbuckets = 0 70 return it, nil 71 } 72 73 for _, f := range maptype.Field { 74 var err error 75 field, _ := sv.toField(f) 76 switch f.Name { 77 case "count": 78 v.Len, err = field.asInt() 79 case "B": 80 var b uint64 81 b, err = field.asUint() 82 it.numbuckets = 1 << b 83 it.oldmask = (1 << (b - 1)) - 1 84 case "buckets": 85 it.buckets = field.MaybeDereference() 86 case "oldbuckets": 87 it.oldbuckets = field.MaybeDereference() 88 } 89 if err != nil { 90 return nil, err 91 } 92 } 93 94 if it.buckets.Kind != reflect.Struct || it.oldbuckets.Kind != reflect.Struct { 95 return nil, errors.New("malformed map type: buckets, oldbuckets or overflow field not a struct") 96 } 97 98 it.hashTophashEmptyOne = hashTophashEmptyZero 99 it.hashMinTopHash = hashMinTopHashGo111 100 if binary_info.GoVersionAfterOrEqual(1, 12) { 101 it.hashTophashEmptyOne = hashTophashEmptyOne 102 it.hashMinTopHash = hashMinTopHashGo112 103 } 104 105 return it, nil 106 } 107 108 const ( 109 hashTophashEmptyZero = 0 110 hashTophashEmptyOne = 1 111 hashMinTopHashGo111 = 4 112 hashMinTopHashGo112 = 5 113 ) 114 115 func (it *mapIterator) next() bool { 116 for { 117 if it.b == nil || it.idx >= it.tophashes.Len { 118 r, _ := it.nextBucket() 119 if !r { 120 return false 121 } 122 it.idx = 0 123 } 124 tophash, _ := it.tophashes.sliceAccess(int(it.idx)) 125 h, err := tophash.asUint() 126 if err != nil { 127 it.v.Unreadable = fmt.Errorf("unreadable tophash: %v", err) 128 return false 129 } 130 it.idx++ 131 if h != hashTophashEmptyZero && h != it.hashTophashEmptyOne { 132 return true 133 } 134 } 135 } 136 137 func (it *mapIterator) key() *Variable { 138 k, _ := it.keys.sliceAccess(int(it.idx - 1)) 139 return k 140 } 141 142 func (it *mapIterator) value() *Variable { 143 v, _ := it.values.sliceAccess(int(it.idx - 1)) 144 return v 145 } 146 147 func (it *mapIterator) mapEvacuated(b *Variable) bool { 148 if b.Addr == 0 { 149 return true 150 } 151 for _, f := range b.DwarfType.(*godwarf.StructType).Field { 152 if f.Name != "tophash" { 153 continue 154 } 155 tophashes, _ := b.toField(f) 156 tophash0var, _ := tophashes.sliceAccess(0) 157 tophash0, err := tophash0var.asUint() 158 if err != nil { 159 return true 160 } 161 162 return tophash0 > it.hashTophashEmptyOne && tophash0 < it.hashMinTopHash 163 } 164 return true 165 } 166 167 func (v *Variable) sliceAccess(idx int) (*Variable, error) { 168 wrong := false 169 if v.Flags&VariableCPtr == 0 { 170 wrong = idx < 0 || int64(idx) >= v.Len 171 } else { 172 wrong = idx < 0 173 } 174 if wrong { 175 return nil, fmt.Errorf("index out of bounds") 176 } 177 mem := v.Mem 178 if v.Kind != reflect.Array { 179 mem = memory.DereferenceMemory(mem) 180 } 181 return v.spawn("", v.Base+uint64(int64(idx)*v.stride), v.fieldType, mem), nil 182 } 183 184 func (it *mapIterator) nextBucket() (bool, error) { 185 if it.overflow != nil && it.overflow.Addr > 0 { 186 it.b = it.overflow 187 } else { 188 it.b = nil 189 190 for it.bidx < it.numbuckets { 191 it.b = it.buckets.clone() 192 it.b.Addr += uint64(it.buckets.DwarfType.Size()) * it.bidx 193 194 if it.oldbuckets.Addr <= 0 { 195 break 196 } 197 198 199 200 201 202 203 204 205 206 oldbidx := it.bidx & it.oldmask 207 oldb := it.oldbuckets.clone() 208 oldb.Addr += uint64(it.oldbuckets.DwarfType.Size()) * oldbidx 209 210 if it.mapEvacuated(oldb) { 211 break 212 } 213 214 if oldbidx == it.bidx { 215 it.b = oldb 216 break 217 } 218 219 220 221 it.b = nil 222 it.bidx++ 223 } 224 225 if it.b == nil { 226 return false, nil 227 } 228 it.bidx++ 229 } 230 231 if it.b.Addr <= 0 { 232 return false, nil 233 } 234 235 it.b.Mem = memory.CacheMemory(it.b.Mem, it.b.Addr, int(it.b.RealType.Size())) 236 237 it.tophashes = nil 238 it.keys = nil 239 it.values = nil 240 it.overflow = nil 241 242 for _, f := range it.b.DwarfType.(*godwarf.StructType).Field { 243 field, err := it.b.toField(f) 244 if err != nil { 245 it.v.Unreadable = err 246 return false, err 247 } 248 if field.Unreadable != nil { 249 it.v.Unreadable = field.Unreadable 250 return false, field.Unreadable 251 } 252 253 switch f.Name { 254 case "tophash": 255 it.tophashes = field 256 case "keys": 257 it.keys = field 258 case "values": 259 it.values = field 260 case "overflow": 261 it.overflow = field.MaybeDereference() 262 } 263 } 264 265 266 if it.tophashes == nil || it.keys == nil || it.values == nil { 267 it.v.Unreadable = fmt.Errorf("malformed map type") 268 return false, it.v.Unreadable 269 } 270 271 if it.tophashes.Kind != reflect.Array || it.keys.Kind != reflect.Array || it.values.Kind != reflect.Array { 272 it.v.Unreadable = errors.New("malformed map type: keys, values or tophash of a bucket is not an array") 273 return false, it.v.Unreadable 274 } 275 276 if it.tophashes.Len != it.keys.Len { 277 it.v.Unreadable = errors.New("malformed map type: inconsistent array length in bucket") 278 return false, it.v.Unreadable 279 } 280 281 if it.values.fieldType.Size() > 0 && it.tophashes.Len != it.values.Len { 282 283 284 it.v.Unreadable = errors.New("malformed map type: inconsistent array length in bucket") 285 return false, it.v.Unreadable 286 } 287 288 if it.overflow.Kind != reflect.Struct { 289 it.v.Unreadable = errors.New("malformed map type: buckets, oldbuckets or overflow field not a struct") 290 return false, it.v.Unreadable 291 } 292 293 return true, nil 294 }