diff --git a/README.md b/README.md index 4c7828e..edf9453 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ successfully output to `grf.out` ## Docs -[Principle](docs/principle.md) | [实现原理](docs/principle_cn.md) +[How it Works](docs/principle.md) | [实现原理](docs/principle_cn.md) ## Credit diff --git a/cmd/grf/cmds/commands.go b/cmd/grf/cmds/commands.go index 15dc121..fe8f88a 100644 --- a/cmd/grf/cmds/commands.go +++ b/cmd/grf/cmds/commands.go @@ -36,6 +36,9 @@ var ( conf *config.Config loadConfErr error outFile string + + // verbose is whether to log verbose info, like debug logs. + verbose bool ) // New returns an initialized command tree. @@ -89,21 +92,21 @@ You'll have to wait for goref until it outputs 'successfully output to ...', or coreCommand.Flags().StringVarP(&outFile, "out", "o", "grf.out", "output file name") rootCommand.AddCommand(coreCommand) - versionVerbose := false versionCommand := &cobra.Command{ Use: "version", Short: "Prints version.", Run: func(cmd *cobra.Command, args []string) { fmt.Printf("Goref Tool\n%s\n", version.DelveVersion) - if versionVerbose { + if verbose { fmt.Printf("Build Details: %s\n", version.BuildInfo()) } }, ValidArgsFunction: cobra.NoFileCompletions, } - versionCommand.Flags().BoolVarP(&versionVerbose, "verbose", "v", false, "print verbose version info") rootCommand.AddCommand(versionCommand) + rootCommand.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "print verbose info or enable debug logger") + return rootCommand } @@ -129,6 +132,13 @@ func coreCmd(_ *cobra.Command, args []string) { } func execute(attachPid int, exeFile, coreFile, outFile string, conf *config.Config) int { + if verbose { + if err := logflags.Setup(verbose, "", ""); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) + return 1 + } + defer logflags.Close() + } if loadConfErr != nil { logflags.DebuggerLogger().Errorf("%v", loadConfErr) } diff --git a/pkg/proc/address.go b/pkg/proc/address.go index 0ed093c..1029654 100644 --- a/pkg/proc/address.go +++ b/pkg/proc/address.go @@ -26,3 +26,9 @@ func (a Address) Sub(b Address) int64 { func (a Address) Add(x int64) Address { return a + Address(x) } + +// Align rounds a up to a multiple of x. +// x must be a power of 2. +func (a Address) Align(x int64) Address { + return (a + Address(x) - 1) & ^(Address(x) - 1) +} diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index 59f1253..98191ca 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -167,7 +167,7 @@ func extractVarInfoFromEntry(bi *proc.BinaryInfo, image *proc.Image, regs op.Dwa t, err = resolveParametricType(bi, mem, t, dictAddr, mds) if err != nil { // Log the error, keep going with t, which will be the shape type - logflags.DebuggerLogger().Errorf("could not resolve parametric type of %s: %v", n, err) + logflags.DebuggerLogger().Warnf("could not resolve parametric type of %s: %v", n, err) } addr, pieces, _, _ := bi.Location(entry, dwarf.AttrLocation, regs.PC(), regs, mem) diff --git a/pkg/proc/gcmask.go b/pkg/proc/gcmask.go new file mode 100644 index 0000000..6bf30dd --- /dev/null +++ b/pkg/proc/gcmask.go @@ -0,0 +1,84 @@ +// Copyright 2024 CloudWeGo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package proc + +import ( + "errors" + "math/bits" +) + +type gcMaskBitIterator struct { + base, end Address // cannot reach end + + maskBase Address + mask []uint64 + + addr Address // iterator address +} + +// nextPtr returns next ptr address starts from 'addr', returns 0 if not found. +// If ack == true, the 'addr' will automatically increment to the next +// starting address to be searched. +func (b *gcMaskBitIterator) nextPtr(ack bool) Address { + if b == nil { + return 0 + } + startOffset, endOffset := b.addr.Sub(b.maskBase), b.end.Sub(b.maskBase) + if startOffset >= endOffset || startOffset < 0 { + return 0 + } + for startOffset < endOffset { + ptrIdx := startOffset / 8 / 64 + i := startOffset / 8 % 64 + j := int64(bits.TrailingZeros64(b.mask[ptrIdx] >> i)) + if j == 64 { + // search the next ptr + startOffset = (ptrIdx + 1) * 64 * 8 + continue + } + addr := b.maskBase.Add(startOffset + j*8) + if addr >= b.end { + return 0 + } + if ack { + b.addr = addr.Add(8) + } + return addr + } + return 0 +} + +// resetGCMask will reset ptrMask corresponding to the address, +// which will never be marked again by the finalMark. +func (b *gcMaskBitIterator) resetGCMask(addr Address) error { + if b == nil { + return nil + } + if addr < b.base || addr >= b.end { + return errOutOfRange + } + // TODO: check gc mask + offset := addr.Sub(b.maskBase) + b.mask[offset/8/64] &= ^(1 << (offset / 8 % 64)) + return nil +} + +func newGCBitsIterator(base, end, maskBase Address, ptrMask []uint64) *gcMaskBitIterator { + return &gcMaskBitIterator{base: base, end: end, mask: ptrMask, addr: base, maskBase: maskBase} +} + +// To avoid traversing fields/elements that escape the actual valid scope. +// e.g. (*[1 << 16]scase)(unsafe.Pointer(cas0)) in runtime.selectgo. +var errOutOfRange = errors.New("out of heap span range") diff --git a/pkg/proc/heap.go b/pkg/proc/heap.go index b709832..4e7db08 100644 --- a/pkg/proc/heap.go +++ b/pkg/proc/heap.go @@ -56,22 +56,18 @@ func (sp *spanInfo) elemEnd(base Address) Address { } type segment struct { - start, end Address - visitMask []uint64 + gcMaskBitIterator + visitMask []uint64 } -func (s *segment) init(start, end Address) { - s.start, s.end = start, end - maskLen := (end - start) / 8 / 64 - if (end-start)/8%64 != 0 { - maskLen += 1 - } - s.visitMask = make([]uint64, maskLen) +func (s *segment) init(start, end Address, ptrMask []uint64) { + s.gcMaskBitIterator = *newGCBitsIterator(start, end, start, ptrMask) + s.visitMask = make([]uint64, CeilDivide(int64((end-start)/8), 64)) } func (s *segment) mark(addr Address) (success bool) { - if addr >= s.start && addr < s.end { - offset := addr.Sub(s.start) + if addr >= s.base && addr < s.end { + offset := addr.Sub(s.base) if s.visitMask[offset/8/64]&(1<<(offset/8%64)) != 0 { return false } else { @@ -100,9 +96,38 @@ func (ss segments) mark(addr Address) (success bool, seg *segment) { return false, nil } -// stack inherits segment. +type framePointerMask struct { + gcMaskBitIterator + funcName string +} + type stack struct { - segment + start, end Address + visitMask []uint64 + frames []*framePointerMask +} + +func (s *stack) init(start, end Address, frames []*framePointerMask) { + s.start, s.end = start, end + s.visitMask = make([]uint64, CeilDivide(int64((end-start)/8), 64)) + s.frames = frames +} + +func (s *stack) mark(addr Address) (success bool) { + if addr >= s.start && addr < s.end { + offset := addr.Sub(s.start) + if s.visitMask[offset/8/64]&(1<<(offset/8%64)) != 0 { + return false + } else { + s.visitMask[offset/8/64] |= 1 << (offset / 8 % 64) + return true + } + } + return false +} + +type funcExtra struct { + closureStructType *godwarf.StructType // closure struct type only support go 1.23 and later } // HeapScope contains the proc info for this round of scanning. @@ -135,7 +160,7 @@ type HeapScope struct { finalMarks []finalMarkParam - closureStructTypes map[*proc.Function]*godwarf.StructType + funcExtraMap map[*proc.Function]funcExtra } func (s *HeapScope) readHeap() error { @@ -194,10 +219,7 @@ func (s *HeapScope) readAllSpans(allspans *region, spanInUse, kindSpecialFinaliz if st.Uint8() != spanInUse { continue } - maskLen := spanSize / 8 / 64 - if spanSize/8%64 != 0 { - maskLen += 1 - } + maskLen := CeilDivide(spanSize/8, 64) spi := &spanInfo{ base: base, elemSize: elemSize, spanSize: spanSize, visitMask: make([]uint64, maskLen), ptrMask: make([]uint64, maskLen), @@ -351,7 +373,7 @@ func (s *HeapScope) readType(sp *spanInfo, typeAddr, addr, end Address) { } mask, err := readUintRaw(mem, uint64(gcDataAddr.Add(addr.Sub(elem)/64)), 8) if err != nil { - logflags.DebuggerLogger().Errorf("read gc data addr error: %v", err) + logflags.DebuggerLogger().Warnf("read gc data addr error: %v", err) break } var headBits int64 @@ -473,68 +495,6 @@ func (s *HeapScope) setHeapPtr(a Address) { sp.ptrMask[offset/8/64] |= uint64(1) << (offset / 8 % 64) } -type heapBits struct { - base Address // heap base - addr Address // iterator address - end Address // cannot reach end - sp *spanInfo // span info -} - -func newHeapBits(base, end Address, sp *spanInfo) *heapBits { - return &heapBits{base: base, addr: base, end: end, sp: sp} -} - -// To avoid traversing fields/elements that escape the actual valid scope. -// e.g. (*[1 << 16]scase)(unsafe.Pointer(cas0)) in runtime.selectgo. -var errOutOfRange = errors.New("out of heap span range") - -// resetGCMask will reset ptrMask corresponding to the address, -// which will never be marked again by the finalMark. -func (hb *heapBits) resetGCMask(addr Address) error { - if hb == nil { - return nil - } - if addr < hb.base || addr >= hb.end { - return errOutOfRange - } - // TODO: check gc mask - offset := addr.Sub(hb.sp.base) - hb.sp.ptrMask[offset/8/64] &= ^(1 << (offset / 8 % 64)) - return nil -} - -// nextPtr returns next ptr address starts from 'addr', returns 0 if not found. -// If ack == true, the 'addr' will automatically increment to the next -// starting address to be searched. -func (hb *heapBits) nextPtr(ack bool) Address { - if hb == nil { - return 0 - } - startOffset, endOffset := hb.addr.Sub(hb.sp.base), hb.end.Sub(hb.sp.base) - if startOffset >= endOffset || startOffset < 0 || endOffset > hb.sp.spanSize { - return 0 - } - for startOffset < endOffset { - ptrIdx := startOffset / 8 / 64 - i := startOffset / 8 % 64 - j := int64(bits.TrailingZeros64(hb.sp.ptrMask[ptrIdx] >> i)) - if j == 64 { - // search the next ptr - startOffset = (ptrIdx + 1) * 64 * 8 - continue - } - addr := hb.sp.base.Add(startOffset + j*8) - if addr >= hb.end { - return 0 - } - if ack { - hb.addr = addr.Add(8) - } - return addr - } - return 0 -} - func (s *HeapScope) spanOf(addr Address) *spanInfo { l1, l2, idx := s.indexes(addr) if l1 < uint(len(s.arenaInfo)) { @@ -559,16 +519,64 @@ func (s *HeapScope) readModuleData() error { return err } firstmoduledata := toRegion(tmp, s.bi) + for md := firstmoduledata; md.a != 0; md = md.Field("next").Deref() { - var data, bss segment - data.init(Address(md.Field("data").Uintptr()), Address(md.Field("edata").Uintptr())) - bss.init(Address(md.Field("bss").Uintptr()), Address(md.Field("ebss").Uintptr())) - s.data = append(s.data, &data) - s.bss = append(s.bss, &bss) + if data := s.parseSegment("data", md); data != nil && data.base != 0 { + s.data = append(s.data, data) + } + if bss := s.parseSegment("bss", md); bss != nil && bss.base != 0 { + s.bss = append(s.bss, bss) + } } return nil } +func (s *HeapScope) parseSegment(name string, md *region) *segment { + var seg segment + minAddr := Address(md.Field(name).Uintptr()) + maxAddr := Address(md.Field("e" + name).Uintptr()) + gcmask := md.Field("gc" + name + "mask").Field("bytedata").Address() + ptrNum := int64((maxAddr - minAddr) / 8) + ptrMask := make([]uint64, CeilDivide(ptrNum, 64)) + data := make([]byte, int(ptrNum/8)) + _, err := s.mem.ReadMemory(data, uint64(gcmask)) + if err != nil { + logflags.DebuggerLogger().Errorf("read gc data mask error: %v", err) + } + for i, mask := range data { + // convert to 64-bit mask + ptrMask[i/8] |= uint64(mask) << (8 * (i % 8)) + } + seg.init(minAddr, maxAddr, ptrMask) + return &seg +} + +// Support for stackmap greatly couples the underlying implementation of go runtime, +// which is extremely complex to handle and is not conducive to the maintenance of the project. +// Therefore, Goref adopts a conservative scanning scheme. +// NOTE: This may lead to scanning an additional portion of memory. +func (s *HeapScope) stackPtrMask(frames []proc.Stackframe) []*framePointerMask { + var frPtrMasks []*framePointerMask + for i := range frames { + pc := frames[i].Regs.PC() + fn := s.bi.PCToFunc(pc) + if fn == nil { + continue + } + sp := Address(frames[i].Regs.SP()) + fp := Address(frames[i].Regs.FrameBase) + ptrMask := make([]uint64, CeilDivide(fp.Sub(sp)/8, 64)) + for i := range ptrMask { + ptrMask[i] = ^uint64(0) + } + frPtrMasks = append(frPtrMasks, &framePointerMask{ + funcName: fn.Name, + gcMaskBitIterator: *newGCBitsIterator(sp, fp, sp, ptrMask), + }) + } + return frPtrMasks +} + type finalizer struct { p Address // finalized pointer fn Address // finalizer function, always 8 bytes diff --git a/pkg/proc/heap_test.go b/pkg/proc/heap_test.go index 2a2f530..a2ab97b 100644 --- a/pkg/proc/heap_test.go +++ b/pkg/proc/heap_test.go @@ -17,14 +17,11 @@ package proc import "testing" func TestHeapBits(t *testing.T) { - hb := newHeapBits(0, 1024, &spanInfo{ - spanSize: 1024, - ptrMask: make([]uint64, 2), - }) + hb := newGCBitsIterator(0, 1024, 0, make([]uint64, 2)) // set 16, 72, 208, 504, 928 as pointer offsets := []int64{16, 72, 208, 504, 928} for _, offset := range offsets { - hb.sp.ptrMask[offset/8/64] |= 1 << (offset / 8 % 64) + hb.mask[offset/8/64] |= 1 << (offset / 8 % 64) } for i, offset := range offsets { var nextOffset int64 diff --git a/pkg/proc/mem.go b/pkg/proc/mem.go index 0769f6c..5ae2a80 100644 --- a/pkg/proc/mem.go +++ b/pkg/proc/mem.go @@ -19,7 +19,10 @@ import ( "github.com/go-delve/delve/pkg/proc" ) -const cacheEnabled = true +const ( + cacheEnabled = true + cacheThreshold = 1024 * 1024 * 1024 // 1GB +) type memCache struct { loaded bool @@ -68,6 +71,9 @@ func cacheMemory(mem proc.MemoryReadWriter, addr uint64, size int) proc.MemoryRe // overflow return mem } + if size > cacheThreshold { + return mem + } switch cacheMem := mem.(type) { case *memCache: if cacheMem.contains(addr, size) { diff --git a/pkg/proc/reference.go b/pkg/proc/reference.go index e4aa4fb..0228ad0 100644 --- a/pkg/proc/reference.go +++ b/pkg/proc/reference.go @@ -16,6 +16,7 @@ package proc import ( "errors" + "fmt" "log" "os" "reflect" @@ -28,7 +29,10 @@ import ( "github.com/go-delve/delve/pkg/proc" ) -const maxRefDepth = 256 +const ( + maxRefDepth = 256 + disableDwarfSearching = false +) type ObjRefScope struct { *HeapScope @@ -43,25 +47,25 @@ func (s *ObjRefScope) findObject(addr Address, typ godwarf.Type, mem proc.Memory sp, base := s.findSpanAndBase(addr) if sp == nil { // not in heap - var seg *segment - var suc bool - if suc, seg = s.bss.mark(addr); suc { + var end Address + if suc, seg := s.bss.mark(addr); suc { // in bss segment + end = seg.end } else if suc, seg = s.data.mark(addr); suc { // in data segment + end = seg.end } else if s.g != nil && s.g.mark(addr) { // in g stack - seg = &s.g.segment + end = s.g.end + } else { + return } - if seg != nil { - if addr.Add(typ.Size()) > seg.end { - // There is an unsafe conversion, it is certain that another root object - // is referencing the memory, so there is no need to scan this object. - return - } - // TODO: using stackmap and gcbssmask - v = newReferenceVariable(addr, "", resolveTypedef(typ), mem, nil) + if addr.Add(typ.Size()) > end { + // There is an unsafe conversion, it is certain that another root object + // is referencing the memory, so there is no need to scan this object. + return } + v = newReferenceVariable(addr, "", resolveTypedef(typ), mem, nil) return } // Find mark bit @@ -71,7 +75,7 @@ func (s *ObjRefScope) findObject(addr Address, typ godwarf.Type, mem proc.Memory realBase := s.copyGCMask(sp, base) // heap bits searching - hb := newHeapBits(realBase, sp.elemEnd(base), sp) + hb := newGCBitsIterator(realBase, sp.elemEnd(base), sp.base, sp.ptrMask) if hb.nextPtr(false) != 0 { // has pointer, cache mem mem = cacheMemory(mem, uint64(base), int(sp.elemSize)) @@ -91,7 +95,7 @@ func (s *HeapScope) markObject(addr Address, mem proc.MemoryReadWriter) (size, c } realBase := s.copyGCMask(sp, base) size, count = sp.elemSize, 1 - hb := newHeapBits(realBase, sp.elemEnd(base), sp) + hb := newGCBitsIterator(realBase, sp.elemEnd(base), sp.base, sp.ptrMask) var cmem proc.MemoryReadWriter for { ptr := hb.nextPtr(true) @@ -121,10 +125,10 @@ func (s *ObjRefScope) record(idx *pprofIndex, size, count int64) { type finalMarkParam struct { idx *pprofIndex - hb *heapBits + hb *gcMaskBitIterator } -func (s *ObjRefScope) finalMark(idx *pprofIndex, hb *heapBits) { +func (s *ObjRefScope) finalMark(idx *pprofIndex, hb *gcMaskBitIterator) { var ptr Address var size, count int64 var cmem proc.MemoryReadWriter @@ -374,8 +378,9 @@ func (s *ObjRefScope) findRef(x *ReferenceVariable, idx *pprofIndex) (err error) } func (s *ObjRefScope) closureStructType(fn *proc.Function) *godwarf.StructType { - if st := s.closureStructTypes[fn]; st != nil { - return st + var fe funcExtra + if fe = s.funcExtraMap[fn]; fe.closureStructType != nil { + return fe.closureStructType } image := funcToImage(s.bi, fn) dwarfTree, err := getDwarfTree(image, getFunctionOffset(fn)) @@ -412,7 +417,8 @@ func (s *ObjRefScope) closureStructType(fn *proc.Function) *godwarf.StructType { lf := st.Field[len(st.Field)-1] st.ByteSize = lf.ByteOffset + lf.Type.Common().ByteSize } - s.closureStructTypes[fn] = st + fe.closureStructType = st + s.funcExtraMap[fn] = fe return st } @@ -460,7 +466,7 @@ func ObjectReference(t *proc.Target, filename string) error { return err } - heapScope := &HeapScope{mem: t.Memory(), bi: t.BinInfo(), scope: scope, closureStructTypes: make(map[*proc.Function]*godwarf.StructType)} + heapScope := &HeapScope{mem: t.Memory(), bi: t.BinInfo(), scope: scope, funcExtraMap: make(map[*proc.Function]funcExtra)} err = heapScope.readHeap() if err != nil { return err @@ -485,7 +491,7 @@ func ObjectReference(t *proc.Target, filename string) error { // Global variables pvs, _ := scope.PackageVariables(loadSingleValue) for _, pv := range pvs { - if pv.Addr == 0 { + if pv.Addr == 0 || disableDwarfSearching { continue } s.findRef(newReferenceVariable(Address(pv.Addr), pv.Name, pv.RealType, t.Memory(), nil), nil) @@ -497,11 +503,11 @@ func ObjectReference(t *proc.Target, filename string) error { for _, gr := range grs { s.g = &stack{} lo, hi := getStack(gr) - s.g.init(Address(lo), Address(hi)) if gr.Thread != nil { threadID = gr.Thread.ThreadID() } sf, _ := proc.GoroutineStacktrace(t, gr, 1024, 0) + s.g.init(Address(lo), Address(hi), s.stackPtrMask(sf)) if len(sf) > 0 { for i := range sf { ms := myEvalScope{EvalScope: *proc.FrameToScope(t, t.Memory(), gr, threadID, sf[i:]...)} @@ -511,7 +517,7 @@ func ObjectReference(t *proc.Target, filename string) error { continue } for _, l := range locals { - if l.Addr == 0 { + if l.Addr == 0 || disableDwarfSearching { continue } if l.Name[0] == '&' { @@ -523,9 +529,34 @@ func ObjectReference(t *proc.Target, filename string) error { } } } + // scan root gc bits in case dwarf searching failure + for _, fr := range s.g.frames { + it := &(fr.gcMaskBitIterator) + if it.nextPtr(false) != 0 { + // add to the finalMarks + idx := (*pprofIndex)(nil).pushHead(s.pb, fr.funcName) + s.finalMarks = append(s.finalMarks, finalMarkParam{idx, it}) + } + } } s.g = nil + // final mark segment root bits + for i, seg := range s.bss { + it := &(seg.gcMaskBitIterator) + if it.nextPtr(false) != 0 { + idx := (*pprofIndex)(nil).pushHead(s.pb, fmt.Sprintf("bss segment[%d]", i)) + s.finalMarks = append(s.finalMarks, finalMarkParam{idx, it}) + } + } + for i, seg := range s.data { + it := &(seg.gcMaskBitIterator) + if it.nextPtr(false) != 0 { + idx := (*pprofIndex)(nil).pushHead(s.pb, fmt.Sprintf("data segment[%d]", i)) + s.finalMarks = append(s.finalMarks, finalMarkParam{idx, it}) + } + } + // Finalizers for _, fin := range heapScope.finalizers { // scan object diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index bffc66c..b84ee15 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -58,8 +58,7 @@ type ReferenceVariable struct { mem proc.MemoryReadWriter // heap bits for this object - // hb.base equals to Addr, hb.end equals to min(Addr.Add(RealType.Size), heapBase.Add(elemSize)) - hb *heapBits + hb *gcMaskBitIterator // node size size int64 @@ -67,11 +66,11 @@ type ReferenceVariable struct { count int64 } -func newReferenceVariable(addr Address, name string, typ godwarf.Type, mem proc.MemoryReadWriter, hb *heapBits) *ReferenceVariable { +func newReferenceVariable(addr Address, name string, typ godwarf.Type, mem proc.MemoryReadWriter, hb *gcMaskBitIterator) *ReferenceVariable { return &ReferenceVariable{Addr: addr, Name: name, RealType: typ, mem: mem, hb: hb} } -func newReferenceVariableWithSizeAndCount(addr Address, name string, typ godwarf.Type, mem proc.MemoryReadWriter, hb *heapBits, size, count int64) *ReferenceVariable { +func newReferenceVariableWithSizeAndCount(addr Address, name string, typ godwarf.Type, mem proc.MemoryReadWriter, hb *gcMaskBitIterator, size, count int64) *ReferenceVariable { rv := newReferenceVariable(addr, name, typ, mem, hb) rv.size, rv.count = size, count return rv @@ -368,7 +367,6 @@ func (it *mapIterator) value() *ReferenceVariable { func (it *mapIterator) kv(v *ReferenceVariable) *ReferenceVariable { v.RealType = resolveTypedef(v.RealType.(*godwarf.ArrayType).Type) v.Addr = v.Addr.Add(v.RealType.Size() * (it.idx - 1)) - // fixme(@jayantxie): use stackmap to get gc bits. if v.hb != nil { // limit heap bits to a single value base, end := v.hb.base, v.hb.end @@ -381,7 +379,7 @@ func (it *mapIterator) kv(v *ReferenceVariable) *ReferenceVariable { if base >= end { return nil } - v.hb = newHeapBits(base, end, v.hb.sp) + v.hb = newGCBitsIterator(base, end, v.hb.maskBase, v.hb.mask) } return v } @@ -591,3 +589,11 @@ func resolveTypedef(typ godwarf.Type) godwarf.Type { } } } + +func CeilDivide(n, d int64) int64 { + r := n / d + if n%d > 0 { + r++ + } + return r +} diff --git a/testdata/alltypes/main.go b/testdata/alltypes/main.go index 2336c8a..577a210 100644 --- a/testdata/alltypes/main.go +++ b/testdata/alltypes/main.go @@ -43,7 +43,7 @@ var ( ) //go:noinline -func escape(req *Request, str interface{}, reqI interface{}, reqE ReqE, bbbb *[2112313131]Request) { +func escape(req *Request, str, reqI interface{}, reqE ReqE, bbbb *[2112313131]Request) { _, _ = json.Marshal(req) _, _ = json.Marshal(str) _, _ = json.Marshal(reqI)