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  }