[tools] Add basic pointer compression to grokdump
Distinguish between tagged and machine pointer sizes in grokdump, and dump a tagged memory view in addition to the machine-word dump when they don't match. This tagged view tries to decompress pointers for link targets, by masking the slot they're in to get the cage root. Drive-by: Add a .style.yapf to opt in to python formatting using git cl format. Change-Id: Ic5272cd865f995fc670ab2fb7d5e464f317af1bf Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3439906 Reviewed-by: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Auto-Submit: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Commit-Queue: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/main@{#78948}
This commit is contained in:
parent
6bb35b8c83
commit
c97337ff5e
2
.style.yapf
Normal file
2
.style.yapf
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[style]
|
||||||
|
based_on_style = chromium
|
@ -168,7 +168,7 @@ def FullDump(reader, heap):
|
|||||||
print("%s - %s" % (reader.FormatIntPtr(start),
|
print("%s - %s" % (reader.FormatIntPtr(start),
|
||||||
reader.FormatIntPtr(start + size)))
|
reader.FormatIntPtr(start + size)))
|
||||||
print(start + size + 1);
|
print(start + size + 1);
|
||||||
for i in range(0, size, reader.PointerSize()):
|
for i in range(0, size, reader.MachinePointerSize()):
|
||||||
slot = start + i
|
slot = start + i
|
||||||
maybe_address = reader.ReadUIntPtr(slot)
|
maybe_address = reader.ReadUIntPtr(slot)
|
||||||
heap_object = heap.FindObject(maybe_address)
|
heap_object = heap.FindObject(maybe_address)
|
||||||
@ -765,7 +765,7 @@ class MinidumpReader(object):
|
|||||||
return self.FindLocation(address) is not None
|
return self.FindLocation(address) is not None
|
||||||
|
|
||||||
def IsAlignedAddress(self, address):
|
def IsAlignedAddress(self, address):
|
||||||
return (address % self.PointerSize()) == 0
|
return (address % self.MachinePointerSize()) == 0
|
||||||
|
|
||||||
def IsExceptionStackAddress(self, address):
|
def IsExceptionStackAddress(self, address):
|
||||||
if not self.IsAlignedAddress(address): return False
|
if not self.IsAlignedAddress(address): return False
|
||||||
@ -804,11 +804,29 @@ class MinidumpReader(object):
|
|||||||
return (self.arch == MD_CPU_ARCHITECTURE_ARM64 or
|
return (self.arch == MD_CPU_ARCHITECTURE_ARM64 or
|
||||||
self.arch == MD_CPU_ARCHITECTURE_AMD64)
|
self.arch == MD_CPU_ARCHITECTURE_AMD64)
|
||||||
|
|
||||||
|
def IsPointerCompressed(self):
|
||||||
|
# Assume all 64-bit builds are pointer compressed.
|
||||||
|
return self.Is64()
|
||||||
|
|
||||||
|
def Is32BitTagged(self):
|
||||||
|
return not self.Is64() or self.IsPointerCompressed()
|
||||||
|
|
||||||
|
def ReadTagged(self, address):
|
||||||
|
if self.Is32BitTagged():
|
||||||
|
return self.ReadU32(address)
|
||||||
|
return self.ReadU64(address)
|
||||||
|
|
||||||
def ReadUIntPtr(self, address):
|
def ReadUIntPtr(self, address):
|
||||||
if self.Is64():
|
if self.Is64():
|
||||||
return self.ReadU64(address)
|
return self.ReadU64(address)
|
||||||
return self.ReadU32(address)
|
return self.ReadU32(address)
|
||||||
|
|
||||||
|
def ReadSized(self, address, size):
|
||||||
|
if size == 8:
|
||||||
|
return self.ReadU64(address)
|
||||||
|
assert (size == 4)
|
||||||
|
return self.ReadU32(address)
|
||||||
|
|
||||||
def ReadBytes(self, address, size):
|
def ReadBytes(self, address, size):
|
||||||
location = self.FindLocation(address)
|
location = self.FindLocation(address)
|
||||||
return self.minidump[location:location + size]
|
return self.minidump[location:location + size]
|
||||||
@ -819,8 +837,10 @@ class MinidumpReader(object):
|
|||||||
return ctypes.c_uint32.from_buffer(self.minidump, location).value
|
return ctypes.c_uint32.from_buffer(self.minidump, location).value
|
||||||
|
|
||||||
def ReadAsciiPtr(self, address):
|
def ReadAsciiPtr(self, address):
|
||||||
ascii_content = [c if c >= '\x20' and c < '\x7f' else '.'
|
ascii_content = [
|
||||||
for c in self.ReadBytes(address, self.PointerSize())]
|
c if c >= '\x20' and c < '\x7f' else '.'
|
||||||
|
for c in self.ReadBytes(address, self.MachinePointerSize())
|
||||||
|
]
|
||||||
return ''.join(ascii_content)
|
return ''.join(ascii_content)
|
||||||
|
|
||||||
def ReadAsciiString(self, address):
|
def ReadAsciiString(self, address):
|
||||||
@ -908,7 +928,7 @@ class MinidumpReader(object):
|
|||||||
def FindWord(self, word, alignment=0):
|
def FindWord(self, word, alignment=0):
|
||||||
def search_inside_region(reader, start, size, location):
|
def search_inside_region(reader, start, size, location):
|
||||||
location = (location + alignment) & ~alignment
|
location = (location + alignment) & ~alignment
|
||||||
for i in range(size - self.PointerSize()):
|
for i in range(size - self.MachinePointerSize()):
|
||||||
loc = location + i
|
loc = location + i
|
||||||
if reader._ReadWord(loc) == word:
|
if reader._ReadWord(loc) == word:
|
||||||
slot = start + (loc - location)
|
slot = start + (loc - location)
|
||||||
@ -920,7 +940,7 @@ class MinidumpReader(object):
|
|||||||
aligned_res = []
|
aligned_res = []
|
||||||
unaligned_res = []
|
unaligned_res = []
|
||||||
def search_inside_region(reader, start, size, location):
|
def search_inside_region(reader, start, size, location):
|
||||||
for i in range(size - self.PointerSize()):
|
for i in range(size - self.MachinePointerSize()):
|
||||||
loc = location + i
|
loc = location + i
|
||||||
if reader._ReadWord(loc) == word:
|
if reader._ReadWord(loc) == word:
|
||||||
slot = start + (loc - location)
|
slot = start + (loc - location)
|
||||||
@ -1023,11 +1043,21 @@ class MinidumpReader(object):
|
|||||||
return "%016x" % value
|
return "%016x" % value
|
||||||
return "%08x" % value
|
return "%08x" % value
|
||||||
|
|
||||||
def PointerSize(self):
|
def FormatTagged(self, value):
|
||||||
|
if self.Is64() and not self.IsPointerCompressed():
|
||||||
|
return "%016x" % value
|
||||||
|
return "%08x" % value
|
||||||
|
|
||||||
|
def MachinePointerSize(self):
|
||||||
if self.Is64():
|
if self.Is64():
|
||||||
return 8
|
return 8
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
|
def TaggedPointerSize(self):
|
||||||
|
if self.IsPointerCompressed():
|
||||||
|
return 4
|
||||||
|
return self.MachinePointerSize()
|
||||||
|
|
||||||
def Register(self, name):
|
def Register(self, name):
|
||||||
return self.exception_context.__getattribute__(name)
|
return self.exception_context.__getattribute__(name)
|
||||||
|
|
||||||
@ -1173,11 +1203,11 @@ class HeapObject(object):
|
|||||||
instance_type)
|
instance_type)
|
||||||
|
|
||||||
def ObjectField(self, offset):
|
def ObjectField(self, offset):
|
||||||
field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
|
field_value = self.heap.reader.ReadTagged(self.address + offset)
|
||||||
return self.heap.FindObjectOrSmi(field_value)
|
return self.heap.FindObjectOrSmi(field_value)
|
||||||
|
|
||||||
def SmiField(self, offset):
|
def SmiField(self, offset):
|
||||||
field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
|
field_value = self.heap.reader.ReadTagged(self.address + offset)
|
||||||
if self.heap.IsSmi(field_value):
|
if self.heap.IsSmi(field_value):
|
||||||
return self.heap.SmiUntag(field_value)
|
return self.heap.SmiUntag(field_value)
|
||||||
return None
|
return None
|
||||||
@ -1189,7 +1219,7 @@ class Map(HeapObject):
|
|||||||
|
|
||||||
# Instance Sizes
|
# Instance Sizes
|
||||||
def InstanceSizesOffset(self):
|
def InstanceSizesOffset(self):
|
||||||
return self.heap.PointerSize()
|
return self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def InstanceSizeOffset(self):
|
def InstanceSizeOffset(self):
|
||||||
return self.InstanceSizesOffset()
|
return self.InstanceSizesOffset()
|
||||||
@ -1224,28 +1254,29 @@ class Map(HeapObject):
|
|||||||
return self.InstanceAttributesOffset() + self.heap.IntSize()
|
return self.InstanceAttributesOffset() + self.heap.IntSize()
|
||||||
|
|
||||||
def PrototypeOffset(self):
|
def PrototypeOffset(self):
|
||||||
return self.BitField3Offset() + self.heap.PointerSize()
|
return self.BitField3Offset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def ConstructorOrBackPointerOffset(self):
|
def ConstructorOrBackPointerOffset(self):
|
||||||
return self.PrototypeOffset() + self.heap.PointerSize()
|
return self.PrototypeOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def TransitionsOrPrototypeInfoOffset(self):
|
def TransitionsOrPrototypeInfoOffset(self):
|
||||||
return self.ConstructorOrBackPointerOffset() + self.heap.PointerSize()
|
return self.ConstructorOrBackPointerOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def DescriptorsOffset(self):
|
def DescriptorsOffset(self):
|
||||||
return self.TransitionsOrPrototypeInfoOffset() + self.heap.PointerSize()
|
return (self.TransitionsOrPrototypeInfoOffset() +
|
||||||
|
self.heap.TaggedPointerSize())
|
||||||
|
|
||||||
def CodeCacheOffset(self):
|
def CodeCacheOffset(self):
|
||||||
return self.DescriptorsOffset() + self.heap.PointerSize()
|
return self.DescriptorsOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def DependentCodeOffset(self):
|
def DependentCodeOffset(self):
|
||||||
return self.CodeCacheOffset() + self.heap.PointerSize()
|
return self.CodeCacheOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def ReadByte(self, offset):
|
def ReadByte(self, offset):
|
||||||
return self.heap.reader.ReadU8(self.address + offset)
|
return self.heap.reader.ReadU8(self.address + offset)
|
||||||
|
|
||||||
def ReadWord(self, offset):
|
def ReadSlot(self, offset):
|
||||||
return self.heap.reader.ReadUIntPtr(self.address + offset)
|
return self.heap.reader.ReadTagged(self.address + offset)
|
||||||
|
|
||||||
def Print(self, p):
|
def Print(self, p):
|
||||||
p.Print("Map(%08x)" % (self.address))
|
p.Print("Map(%08x)" % (self.address))
|
||||||
@ -1264,7 +1295,7 @@ class Map(HeapObject):
|
|||||||
|
|
||||||
p.Print(" - kind: %s" % (self.Decode(3, 5, bitfield2)))
|
p.Print(" - kind: %s" % (self.Decode(3, 5, bitfield2)))
|
||||||
|
|
||||||
bitfield3 = self.ReadWord(self.BitField3Offset())
|
bitfield3 = self.ReadSlot(self.BitField3Offset())
|
||||||
|
|
||||||
p.Print(
|
p.Print(
|
||||||
" - EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
|
" - EnumLength: %d NumberOfOwnDescriptors: %d OwnsDescriptors: %s" % (
|
||||||
@ -1299,7 +1330,7 @@ class Map(HeapObject):
|
|||||||
class String(HeapObject):
|
class String(HeapObject):
|
||||||
def LengthOffset(self):
|
def LengthOffset(self):
|
||||||
# First word after the map is the hash, the second is the length.
|
# First word after the map is the hash, the second is the length.
|
||||||
return self.heap.PointerSize() * 2
|
return self.heap.TaggedPointerSize() * 2
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
@ -1317,7 +1348,7 @@ class String(HeapObject):
|
|||||||
|
|
||||||
class SeqString(String):
|
class SeqString(String):
|
||||||
def CharsOffset(self):
|
def CharsOffset(self):
|
||||||
return self.heap.PointerSize() * 3
|
return self.heap.TaggedPointerSize() * 3
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
String.__init__(self, heap, map, address)
|
String.__init__(self, heap, map, address)
|
||||||
@ -1360,10 +1391,10 @@ class ExternalString(String):
|
|||||||
|
|
||||||
class ConsString(String):
|
class ConsString(String):
|
||||||
def LeftOffset(self):
|
def LeftOffset(self):
|
||||||
return self.heap.PointerSize() * 3
|
return self.heap.TaggedPointerSize() * 3
|
||||||
|
|
||||||
def RightOffset(self):
|
def RightOffset(self):
|
||||||
return self.heap.PointerSize() * 4
|
return self.heap.TaggedPointerSize() * 4
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
String.__init__(self, heap, map, address)
|
String.__init__(self, heap, map, address)
|
||||||
@ -1390,13 +1421,13 @@ class Oddball(HeapObject):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def ToStringOffset(self):
|
def ToStringOffset(self):
|
||||||
return self.heap.PointerSize()
|
return self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def ToNumberOffset(self):
|
def ToNumberOffset(self):
|
||||||
return self.ToStringOffset() + self.heap.PointerSize()
|
return self.ToStringOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def KindOffset(self):
|
def KindOffset(self):
|
||||||
return self.ToNumberOffset() + self.heap.PointerSize()
|
return self.ToNumberOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
@ -1418,13 +1449,13 @@ class Oddball(HeapObject):
|
|||||||
|
|
||||||
class FixedArray(HeapObject):
|
class FixedArray(HeapObject):
|
||||||
def LengthOffset(self):
|
def LengthOffset(self):
|
||||||
return self.heap.PointerSize()
|
return self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def ElementsOffset(self):
|
def ElementsOffset(self):
|
||||||
return self.heap.PointerSize() * 2
|
return self.heap.TaggedPointerSize() * 2
|
||||||
|
|
||||||
def MemberOffset(self, i):
|
def MemberOffset(self, i):
|
||||||
return self.ElementsOffset() + self.heap.PointerSize() * i
|
return self.ElementsOffset() + self.heap.TaggedPointerSize() * i
|
||||||
|
|
||||||
def Get(self, i):
|
def Get(self, i):
|
||||||
return self.ObjectField(self.MemberOffset(i))
|
return self.ObjectField(self.MemberOffset(i))
|
||||||
@ -1561,10 +1592,10 @@ class TransitionArray(object):
|
|||||||
|
|
||||||
class JSFunction(HeapObject):
|
class JSFunction(HeapObject):
|
||||||
def CodeEntryOffset(self):
|
def CodeEntryOffset(self):
|
||||||
return 3 * self.heap.PointerSize()
|
return 3 * self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def SharedOffset(self):
|
def SharedOffset(self):
|
||||||
return 5 * self.heap.PointerSize()
|
return 5 * self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
@ -1611,19 +1642,19 @@ class JSFunction(HeapObject):
|
|||||||
|
|
||||||
class SharedFunctionInfo(HeapObject):
|
class SharedFunctionInfo(HeapObject):
|
||||||
def CodeOffset(self):
|
def CodeOffset(self):
|
||||||
return 2 * self.heap.PointerSize()
|
return 2 * self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def ScriptOffset(self):
|
def ScriptOffset(self):
|
||||||
return 7 * self.heap.PointerSize()
|
return 7 * self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def InferredNameOffset(self):
|
def InferredNameOffset(self):
|
||||||
return 9 * self.heap.PointerSize()
|
return 9 * self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def EndPositionOffset(self):
|
def EndPositionOffset(self):
|
||||||
return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
|
return 12 * self.heap.TaggedPointerSize() + 4 * self.heap.IntSize()
|
||||||
|
|
||||||
def StartPositionAndTypeOffset(self):
|
def StartPositionAndTypeOffset(self):
|
||||||
return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
|
return 12 * self.heap.TaggedPointerSize() + 5 * self.heap.IntSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
@ -1631,7 +1662,7 @@ class SharedFunctionInfo(HeapObject):
|
|||||||
self.code = self.ObjectField(self.CodeOffset())
|
self.code = self.ObjectField(self.CodeOffset())
|
||||||
self.script = self.ObjectField(self.ScriptOffset())
|
self.script = self.ObjectField(self.ScriptOffset())
|
||||||
self.inferred_name = self.ObjectField(self.InferredNameOffset())
|
self.inferred_name = self.ObjectField(self.InferredNameOffset())
|
||||||
if heap.PointerSize() == 8:
|
if heap.TaggedPointerSize() == 8:
|
||||||
start_position_and_type = \
|
start_position_and_type = \
|
||||||
heap.reader.ReadU32(self.StartPositionAndTypeOffset())
|
heap.reader.ReadU32(self.StartPositionAndTypeOffset())
|
||||||
self.start_position = start_position_and_type >> 2
|
self.start_position = start_position_and_type >> 2
|
||||||
@ -1653,10 +1684,10 @@ class SharedFunctionInfo(HeapObject):
|
|||||||
|
|
||||||
class Script(HeapObject):
|
class Script(HeapObject):
|
||||||
def SourceOffset(self):
|
def SourceOffset(self):
|
||||||
return self.heap.PointerSize()
|
return self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def NameOffset(self):
|
def NameOffset(self):
|
||||||
return self.SourceOffset() + self.heap.PointerSize()
|
return self.SourceOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
@ -1666,10 +1697,10 @@ class Script(HeapObject):
|
|||||||
|
|
||||||
class CodeCache(HeapObject):
|
class CodeCache(HeapObject):
|
||||||
def DefaultCacheOffset(self):
|
def DefaultCacheOffset(self):
|
||||||
return self.heap.PointerSize()
|
return self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def NormalTypeCacheOffset(self):
|
def NormalTypeCacheOffset(self):
|
||||||
return self.DefaultCacheOffset() + self.heap.PointerSize()
|
return self.DefaultCacheOffset() + self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
@ -1689,12 +1720,12 @@ class Code(HeapObject):
|
|||||||
CODE_ALIGNMENT_MASK = (1 << 5) - 1
|
CODE_ALIGNMENT_MASK = (1 << 5) - 1
|
||||||
|
|
||||||
def InstructionSizeOffset(self):
|
def InstructionSizeOffset(self):
|
||||||
return self.heap.PointerSize()
|
return self.heap.TaggedPointerSize()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def HeaderSize(heap):
|
def HeaderSize(heap):
|
||||||
return (heap.PointerSize() + heap.IntSize() + \
|
return (heap.TaggedPointerSize() + heap.IntSize() + \
|
||||||
4 * heap.PointerSize() + 3 * heap.IntSize() + \
|
4 * heap.TaggedPointerSize() + 3 * heap.IntSize() + \
|
||||||
Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
|
Code.CODE_ALIGNMENT_MASK) & ~Code.CODE_ALIGNMENT_MASK
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
@ -1763,7 +1794,7 @@ class V8Heap(object):
|
|||||||
if not self.IsTaggedObjectAddress(tagged_address): return None
|
if not self.IsTaggedObjectAddress(tagged_address): return None
|
||||||
address = tagged_address - 1
|
address = tagged_address - 1
|
||||||
if not self.reader.IsValidAddress(address): return None
|
if not self.reader.IsValidAddress(address): return None
|
||||||
map_tagged_address = self.reader.ReadUIntPtr(address)
|
map_tagged_address = self.reader.ReadTagged(address)
|
||||||
if tagged_address == map_tagged_address:
|
if tagged_address == map_tagged_address:
|
||||||
# Meta map?
|
# Meta map?
|
||||||
meta_map = Map(self, None, address)
|
meta_map = Map(self, None, address)
|
||||||
@ -1796,11 +1827,17 @@ class V8Heap(object):
|
|||||||
def IntSize(self):
|
def IntSize(self):
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
def PointerSize(self):
|
def MachinePointerSize(self):
|
||||||
return self.reader.PointerSize()
|
return self.reader.MachinePointerSize()
|
||||||
|
|
||||||
|
def TaggedPointerSize(self):
|
||||||
|
return self.reader.TaggedPointerSize()
|
||||||
|
|
||||||
|
def IsPointerCompressed(self):
|
||||||
|
return self.reader.IsPointerCompressed()
|
||||||
|
|
||||||
def ObjectAlignmentMask(self):
|
def ObjectAlignmentMask(self):
|
||||||
return self.PointerSize() - 1
|
return self.TaggedPointerSize() - 1
|
||||||
|
|
||||||
def IsTaggedObjectAddress(self, address):
|
def IsTaggedObjectAddress(self, address):
|
||||||
return (address & self.ObjectAlignmentMask()) == 1
|
return (address & self.ObjectAlignmentMask()) == 1
|
||||||
@ -1829,13 +1866,14 @@ class V8Heap(object):
|
|||||||
return (address & self.ObjectAlignmentMask()) == 1
|
return (address & self.ObjectAlignmentMask()) == 1
|
||||||
|
|
||||||
def IsSmi(self, tagged_address):
|
def IsSmi(self, tagged_address):
|
||||||
if self.reader.Is64():
|
if self.reader.Is64() and not self.reader.IsPointerCompressed():
|
||||||
return (tagged_address & 0xFFFFFFFF) == 0
|
return (tagged_address & 0xFFFFFFFF) == 0
|
||||||
return not self.IsTaggedAddress(tagged_address)
|
return not self.IsTaggedAddress(tagged_address)
|
||||||
|
|
||||||
def SmiUntag(self, tagged_address):
|
def SmiUntag(self, tagged_address):
|
||||||
if self.reader.Is64(): return tagged_address >> 32
|
if self.reader.Is64() and not self.reader.IsPointerCompressed():
|
||||||
return tagged_address >> 1
|
return tagged_address >> 32
|
||||||
|
return (tagged_address >> 1) & 0xFFFFFFFF
|
||||||
|
|
||||||
def AddressTypeMarker(self, address):
|
def AddressTypeMarker(self, address):
|
||||||
if not self.reader.IsValidAddress(address): return " "
|
if not self.reader.IsValidAddress(address): return " "
|
||||||
@ -1858,7 +1896,7 @@ class V8Heap(object):
|
|||||||
if self.IsTaggedObjectAddress(address):
|
if self.IsTaggedObjectAddress(address):
|
||||||
address -= 1
|
address -= 1
|
||||||
if not self.reader.IsValidAlignedAddress(address): return None
|
if not self.reader.IsValidAlignedAddress(address): return None
|
||||||
offset = (address - slot) / self.PointerSize()
|
offset = (address - slot) / self.MachinePointerSize()
|
||||||
|
|
||||||
lower_limit = -32
|
lower_limit = -32
|
||||||
upper_limit = 128
|
upper_limit = 128
|
||||||
@ -1873,12 +1911,12 @@ class V8Heap(object):
|
|||||||
def FindObjectPointers(self, start=0, end=0):
|
def FindObjectPointers(self, start=0, end=0):
|
||||||
objects = set()
|
objects = set()
|
||||||
def find_object_in_region(reader, start, size, location):
|
def find_object_in_region(reader, start, size, location):
|
||||||
for slot in range(start, start+size, self.reader.PointerSize()):
|
for slot in range(start, start + size, self.reader.TaggedPointerSize()):
|
||||||
if not self.reader.IsValidAddress(slot): break
|
if not self.reader.IsValidAddress(slot): break
|
||||||
# Collect only tagged pointers (object) to tagged pointers (map)
|
# Collect only tagged pointers (object) to tagged pointers (map)
|
||||||
tagged_address = self.reader.ReadUIntPtr(slot)
|
tagged_address = self.reader.ReadTagged(slot)
|
||||||
if not self.IsValidTaggedObjectAddress(tagged_address): continue
|
if not self.IsValidTaggedObjectAddress(tagged_address): continue
|
||||||
map_address = self.reader.ReadUIntPtr(tagged_address - 1)
|
map_address = self.reader.ReadTagged(tagged_address - 1)
|
||||||
if not self.IsTaggedMapAddress(map_address): continue
|
if not self.IsTaggedMapAddress(map_address): continue
|
||||||
objects.add(tagged_address)
|
objects.add(tagged_address)
|
||||||
|
|
||||||
@ -1951,10 +1989,12 @@ class InspectionInfo(object):
|
|||||||
exception_thread.stack.memory.data_size
|
exception_thread.stack.memory.data_size
|
||||||
frame_pointer = self.reader.ExceptionFP()
|
frame_pointer = self.reader.ExceptionFP()
|
||||||
self.styles[frame_pointer] = "frame"
|
self.styles[frame_pointer] = "frame"
|
||||||
for slot in range(stack_top, stack_bottom, self.reader.PointerSize()):
|
for slot in range(stack_top, stack_bottom,
|
||||||
|
self.reader.MachinePointerSize()):
|
||||||
# stack address
|
# stack address
|
||||||
self.styles[slot] = "sa"
|
self.styles[slot] = "sa"
|
||||||
for slot in range(stack_top, stack_bottom, self.reader.PointerSize()):
|
for slot in range(stack_top, stack_bottom,
|
||||||
|
self.reader.MachinePointerSize()):
|
||||||
maybe_address = self.reader.ReadUIntPtr(slot)
|
maybe_address = self.reader.ReadUIntPtr(slot)
|
||||||
# stack value
|
# stack value
|
||||||
self.styles[maybe_address] = "sv"
|
self.styles[maybe_address] = "sv"
|
||||||
@ -2026,7 +2066,7 @@ class InspectionPadawan(object):
|
|||||||
# Frame markers only occur directly after a frame pointer and only on the
|
# Frame markers only occur directly after a frame pointer and only on the
|
||||||
# stack.
|
# stack.
|
||||||
if not self.reader.IsExceptionStackAddress(slot): return False
|
if not self.reader.IsExceptionStackAddress(slot): return False
|
||||||
next_slot = slot + self.reader.PointerSize()
|
next_slot = slot + self.reader.MachinePointerSize()
|
||||||
if not self.reader.IsValidAddress(next_slot): return False
|
if not self.reader.IsValidAddress(next_slot): return False
|
||||||
next_address = self.reader.ReadUIntPtr(next_slot)
|
next_address = self.reader.ReadUIntPtr(next_slot)
|
||||||
return self.reader.IsExceptionStackAddress(next_address)
|
return self.reader.IsExceptionStackAddress(next_address)
|
||||||
@ -2058,7 +2098,7 @@ class InspectionPadawan(object):
|
|||||||
if found_obj: return found_obj
|
if found_obj: return found_obj
|
||||||
address = tagged_address - 1
|
address = tagged_address - 1
|
||||||
if self.reader.IsValidAddress(address):
|
if self.reader.IsValidAddress(address):
|
||||||
map_tagged_address = self.reader.ReadUIntPtr(address)
|
map_tagged_address = self.reader.ReadTagged(address)
|
||||||
map = self.SenseMap(map_tagged_address)
|
map = self.SenseMap(map_tagged_address)
|
||||||
if map is None: return None
|
if map is None: return None
|
||||||
instance_type_name = INSTANCE_TYPES.get(map.instance_type)
|
instance_type_name = INSTANCE_TYPES.get(map.instance_type)
|
||||||
@ -2118,7 +2158,7 @@ class InspectionPadawan(object):
|
|||||||
Returns the first address where the normal stack starts again.
|
Returns the first address where the normal stack starts again.
|
||||||
"""
|
"""
|
||||||
# Only look at the first 1k words on the stack
|
# Only look at the first 1k words on the stack
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
if start is None: start = self.reader.ExceptionSP()
|
if start is None: start = self.reader.ExceptionSP()
|
||||||
if not self.reader.IsValidAddress(start): return start
|
if not self.reader.IsValidAddress(start): return start
|
||||||
end = start + ptr_size * 1024 * 4
|
end = start + ptr_size * 1024 * 4
|
||||||
@ -2140,7 +2180,7 @@ class InspectionPadawan(object):
|
|||||||
print_message)
|
print_message)
|
||||||
|
|
||||||
def TryExtractStackTrace(self, slot, start, end, print_message):
|
def TryExtractStackTrace(self, slot, start, end, print_message):
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
assert self.reader.ReadUIntPtr(slot) & 0xFFFFFFFF == STACK_TRACE_MARKER
|
assert self.reader.ReadUIntPtr(slot) & 0xFFFFFFFF == STACK_TRACE_MARKER
|
||||||
end_marker = STACK_TRACE_MARKER + 1;
|
end_marker = STACK_TRACE_MARKER + 1;
|
||||||
header_size = 10
|
header_size = 10
|
||||||
@ -2163,7 +2203,7 @@ class InspectionPadawan(object):
|
|||||||
return stack_start
|
return stack_start
|
||||||
|
|
||||||
def FindPtr(self, expected_value, start, end):
|
def FindPtr(self, expected_value, start, end):
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
for slot in range(start, end, ptr_size):
|
for slot in range(start, end, ptr_size):
|
||||||
if not self.reader.IsValidAddress(slot): return None
|
if not self.reader.IsValidAddress(slot): return None
|
||||||
value = self.reader.ReadUIntPtr(slot)
|
value = self.reader.ReadUIntPtr(slot)
|
||||||
@ -2171,7 +2211,7 @@ class InspectionPadawan(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def TryExtractErrorMessage(self, slot, start, end, print_message):
|
def TryExtractErrorMessage(self, slot, start, end, print_message):
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
end_marker = ERROR_MESSAGE_MARKER + 1;
|
end_marker = ERROR_MESSAGE_MARKER + 1;
|
||||||
header_size = 1
|
header_size = 1
|
||||||
end_search = start + 1024 + (header_size * ptr_size);
|
end_search = start + 1024 + (header_size * ptr_size);
|
||||||
@ -2186,7 +2226,7 @@ class InspectionPadawan(object):
|
|||||||
|
|
||||||
def TryExtractOldStyleStackTrace(self, message_slot, start, end,
|
def TryExtractOldStyleStackTrace(self, message_slot, start, end,
|
||||||
print_message):
|
print_message):
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
if message_slot == 0:
|
if message_slot == 0:
|
||||||
"""
|
"""
|
||||||
On Mac we don't always get proper magic markers, so just try printing
|
On Mac we don't always get proper magic markers, so just try printing
|
||||||
@ -2225,7 +2265,7 @@ class InspectionPadawan(object):
|
|||||||
print(" Use `dsa` to print the message with annotated addresses.")
|
print(" Use `dsa` to print the message with annotated addresses.")
|
||||||
print("")
|
print("")
|
||||||
return
|
return
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
# Annotate all addresses in the dumped message
|
# Annotate all addresses in the dumped message
|
||||||
prog = re.compile("[0-9a-fA-F]{%s}" % ptr_size*2)
|
prog = re.compile("[0-9a-fA-F]{%s}" % ptr_size*2)
|
||||||
addresses = list(set(prog.findall(message)))
|
addresses = list(set(prog.findall(message)))
|
||||||
@ -2252,7 +2292,7 @@ class InspectionPadawan(object):
|
|||||||
|
|
||||||
def TryInferContext(self, address):
|
def TryInferContext(self, address):
|
||||||
if self.context: return
|
if self.context: return
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.MachinePointerSize()
|
||||||
possible_context = dict()
|
possible_context = dict()
|
||||||
count = 0
|
count = 0
|
||||||
while self.reader.IsExceptionStackAddress(address):
|
while self.reader.IsExceptionStackAddress(address):
|
||||||
@ -2287,7 +2327,7 @@ class InspectionPadawan(object):
|
|||||||
in_oom_dump_area = False
|
in_oom_dump_area = False
|
||||||
is_stack = self.reader.IsExceptionStackAddress(start)
|
is_stack = self.reader.IsExceptionStackAddress(start)
|
||||||
free_space_end = 0
|
free_space_end = 0
|
||||||
ptr_size = self.reader.PointerSize()
|
ptr_size = self.reader.TaggedPointerSize()
|
||||||
|
|
||||||
for slot in range(start, end, ptr_size):
|
for slot in range(start, end, ptr_size):
|
||||||
if not self.reader.IsValidAddress(slot):
|
if not self.reader.IsValidAddress(slot):
|
||||||
@ -2309,7 +2349,7 @@ class InspectionPadawan(object):
|
|||||||
if isinstance(heap_object, KnownMap) and \
|
if isinstance(heap_object, KnownMap) and \
|
||||||
heap_object.known_name == "FreeSpaceMap":
|
heap_object.known_name == "FreeSpaceMap":
|
||||||
# The free-space length is is stored as a Smi in the next slot.
|
# The free-space length is is stored as a Smi in the next slot.
|
||||||
length = self.reader.ReadUIntPtr(slot + ptr_size)
|
length = self.reader.ReadTagged(slot + ptr_size)
|
||||||
if self.heap.IsSmi(length):
|
if self.heap.IsSmi(length):
|
||||||
length = self.heap.SmiUntag(length)
|
length = self.heap.SmiUntag(length)
|
||||||
free_space_end = slot + length - ptr_size
|
free_space_end = slot + length - ptr_size
|
||||||
@ -2711,7 +2751,8 @@ class InspectionWebFormatter(object):
|
|||||||
stack_bottom = exception_thread.stack.start + \
|
stack_bottom = exception_thread.stack.start + \
|
||||||
exception_thread.stack.memory.data_size
|
exception_thread.stack.memory.data_size
|
||||||
stack_map = {self.reader.ExceptionIP(): -1}
|
stack_map = {self.reader.ExceptionIP(): -1}
|
||||||
for slot in range(stack_top, stack_bottom, self.reader.PointerSize()):
|
for slot in range(stack_top, stack_bottom,
|
||||||
|
self.reader.MachinePointerSize()):
|
||||||
maybe_address = self.reader.ReadUIntPtr(slot)
|
maybe_address = self.reader.ReadUIntPtr(slot)
|
||||||
if not maybe_address in stack_map:
|
if not maybe_address in stack_map:
|
||||||
stack_map[maybe_address] = slot
|
stack_map[maybe_address] = slot
|
||||||
@ -2757,6 +2798,18 @@ class InspectionWebFormatter(object):
|
|||||||
return ("<a %s href=s?%s&val=%s>%s</a>" %
|
return ("<a %s href=s?%s&val=%s>%s</a>" %
|
||||||
(style_class, self.encfilename, straddress, straddress))
|
(style_class, self.encfilename, straddress, straddress))
|
||||||
|
|
||||||
|
def format_onheap_address(self, size, maybeaddress, uncompressed):
|
||||||
|
if maybeaddress is None:
|
||||||
|
return "not in dump"
|
||||||
|
else:
|
||||||
|
straddress = "0x" + self.reader.FormatTagged(maybeaddress)
|
||||||
|
struncompressed = "0x" + self.reader.FormatIntPtr(uncompressed)
|
||||||
|
style_class = ""
|
||||||
|
if not self.reader.IsValidAddress(maybeaddress):
|
||||||
|
style_class = "class=nd"
|
||||||
|
return ("<a %s href=s?%s&val=%s>%s</a>" %
|
||||||
|
(style_class, self.encfilename, struncompressed, straddress))
|
||||||
|
|
||||||
def output_header(self, f):
|
def output_header(self, f):
|
||||||
f.write(WEB_HEADER %
|
f.write(WEB_HEADER %
|
||||||
{ "query_dump" : self.encfilename,
|
{ "query_dump" : self.encfilename,
|
||||||
@ -2779,7 +2832,8 @@ class InspectionWebFormatter(object):
|
|||||||
stack_bottom = min(exception_thread.stack.start + \
|
stack_bottom = min(exception_thread.stack.start + \
|
||||||
exception_thread.stack.memory.data_size,
|
exception_thread.stack.memory.data_size,
|
||||||
stack_top + self.MAX_CONTEXT_STACK)
|
stack_top + self.MAX_CONTEXT_STACK)
|
||||||
self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack")
|
self.output_words(f, stack_top - 16, stack_bottom, stack_top, "Stack",
|
||||||
|
self.heap.MachinePointerSize())
|
||||||
|
|
||||||
f.write('</div>')
|
f.write('</div>')
|
||||||
self.output_footer(f)
|
self.output_footer(f)
|
||||||
@ -2900,7 +2954,11 @@ class InspectionWebFormatter(object):
|
|||||||
return
|
return
|
||||||
region = self.reader.FindRegion(address)
|
region = self.reader.FindRegion(address)
|
||||||
if datakind == "address":
|
if datakind == "address":
|
||||||
self.output_words(f, region[0], region[0] + region[1], address, "Dump")
|
self.output_words(f, region[0], region[0] + region[1], address, "Dump",
|
||||||
|
self.heap.MachinePointerSize())
|
||||||
|
if datakind == "tagged":
|
||||||
|
self.output_words(f, region[0], region[0] + region[1], address,
|
||||||
|
"Tagged Dump", self.heap.TaggedPointerSize())
|
||||||
elif datakind == "ascii":
|
elif datakind == "ascii":
|
||||||
self.output_ascii(f, region[0], region[0] + region[1], address)
|
self.output_ascii(f, region[0], region[0] + region[1], address)
|
||||||
self.output_footer(f)
|
self.output_footer(f)
|
||||||
@ -2909,14 +2967,13 @@ class InspectionWebFormatter(object):
|
|||||||
f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
|
f.write("<h3>Unrecognized address format \"%s\".</h3>" % straddress)
|
||||||
return
|
return
|
||||||
|
|
||||||
def output_words(self, f, start_address, end_address,
|
def output_words(self, f, start_address, end_address, highlight_address, desc,
|
||||||
highlight_address, desc):
|
size):
|
||||||
region = self.reader.FindRegion(highlight_address)
|
region = self.reader.FindRegion(highlight_address)
|
||||||
if region is None:
|
if region is None:
|
||||||
f.write("<h3>Address 0x%x not found in the dump.</h3>" %
|
f.write("<h3>Address 0x%x not found in the dump.</h3>" %
|
||||||
(highlight_address))
|
(highlight_address))
|
||||||
return
|
return
|
||||||
size = self.heap.PointerSize()
|
|
||||||
start_address = self.align_down(start_address, size)
|
start_address = self.align_down(start_address, size)
|
||||||
low = self.align_down(region[0], size)
|
low = self.align_down(region[0], size)
|
||||||
high = self.align_up(region[0] + region[1], size)
|
high = self.align_up(region[0] + region[1], size)
|
||||||
@ -2943,6 +3000,7 @@ class InspectionWebFormatter(object):
|
|||||||
slot = start_address + j
|
slot = start_address + j
|
||||||
heap_object = ""
|
heap_object = ""
|
||||||
maybe_address = None
|
maybe_address = None
|
||||||
|
maybe_uncompressed_address = None
|
||||||
end_region = region[0] + region[1]
|
end_region = region[0] + region[1]
|
||||||
if slot < region[0] or slot + size > end_region:
|
if slot < region[0] or slot + size > end_region:
|
||||||
straddress = "0x"
|
straddress = "0x"
|
||||||
@ -2954,10 +3012,20 @@ class InspectionWebFormatter(object):
|
|||||||
for i in range(slot, region[0]):
|
for i in range(slot, region[0]):
|
||||||
straddress += "??"
|
straddress += "??"
|
||||||
else:
|
else:
|
||||||
maybe_address = self.reader.ReadUIntPtr(slot)
|
maybe_address = self.reader.ReadSized(slot, size)
|
||||||
straddress = self.format_address(maybe_address)
|
if size == self.reader.MachinePointerSize():
|
||||||
|
maybe_uncompressed_address = maybe_address
|
||||||
|
else:
|
||||||
|
maybe_uncompressed_address = (slot & (0xFFFFFF << 32)) | (
|
||||||
|
maybe_address & 0xFFFFFF)
|
||||||
|
|
||||||
|
if size == self.reader.TaggedPointerSize():
|
||||||
|
straddress = self.format_onheap_address(size, maybe_address,
|
||||||
|
maybe_uncompressed_address)
|
||||||
if maybe_address:
|
if maybe_address:
|
||||||
heap_object = self.format_object(maybe_address)
|
heap_object = self.format_object(maybe_address)
|
||||||
|
else:
|
||||||
|
straddress = self.format_address(maybe_address)
|
||||||
|
|
||||||
address_fmt = "%s </td>"
|
address_fmt = "%s </td>"
|
||||||
if slot == highlight_address:
|
if slot == highlight_address:
|
||||||
@ -2974,12 +3042,12 @@ class InspectionWebFormatter(object):
|
|||||||
f.write("</td>")
|
f.write("</td>")
|
||||||
self.td_from_address(f, slot)
|
self.td_from_address(f, slot)
|
||||||
f.write(address_fmt % self.format_address(slot))
|
f.write(address_fmt % self.format_address(slot))
|
||||||
self.td_from_address(f, maybe_address)
|
self.td_from_address(f, maybe_uncompressed_address)
|
||||||
f.write(": %s </td>" % straddress)
|
f.write(": %s </td>" % straddress)
|
||||||
f.write("<td>")
|
f.write("<td>")
|
||||||
if maybe_address != None:
|
if maybe_uncompressed_address != None:
|
||||||
self.output_comment_box(
|
self.output_comment_box(f, "sv-" + self.reader.FormatIntPtr(slot),
|
||||||
f, "sv-" + self.reader.FormatIntPtr(slot), maybe_address)
|
maybe_uncompressed_address)
|
||||||
f.write("</td>")
|
f.write("</td>")
|
||||||
f.write("<td>%s</td>" % (heap_object or ''))
|
f.write("<td>%s</td>" % (heap_object or ''))
|
||||||
f.write("</tr>")
|
f.write("</tr>")
|
||||||
@ -3230,7 +3298,12 @@ class InspectionWebFormatter(object):
|
|||||||
straddress)
|
straddress)
|
||||||
else:
|
else:
|
||||||
# Print as words
|
# Print as words
|
||||||
self.output_words(f, address - 8, address + 32, address, "Dump")
|
self.output_words(f, address - 8, address + 32, address, "Dump",
|
||||||
|
self.heap.MachinePointerSize())
|
||||||
|
|
||||||
|
if self.heap.IsPointerCompressed():
|
||||||
|
self.output_words(f, address - 8, address + 32, address,
|
||||||
|
"Tagged Dump", self.heap.TaggedPointerSize())
|
||||||
|
|
||||||
# Print as ASCII
|
# Print as ASCII
|
||||||
f.write("<hr>")
|
f.write("<hr>")
|
||||||
@ -3534,10 +3607,10 @@ class InspectionShell(cmd.Cmd):
|
|||||||
self.dd_start = self.ParseAddressExpr(args[0])
|
self.dd_start = self.ParseAddressExpr(args[0])
|
||||||
self.dd_num = int(args[1], 16) if len(args) > 1 else 0x10
|
self.dd_num = int(args[1], 16) if len(args) > 1 else 0x10
|
||||||
else:
|
else:
|
||||||
self.dd_start += self.dd_num * self.reader.PointerSize()
|
self.dd_start += self.dd_num * self.reader.MachinePointerSize()
|
||||||
if not self.reader.IsAlignedAddress(self.dd_start):
|
if not self.reader.IsAlignedAddress(self.dd_start):
|
||||||
print("Warning: Dumping un-aligned memory, is this what you had in mind?")
|
print("Warning: Dumping un-aligned memory, is this what you had in mind?")
|
||||||
end = self.dd_start + self.reader.PointerSize() * self.dd_num
|
end = self.dd_start + self.reader.MachinePointerSize() * self.dd_num
|
||||||
self.padawan.InterpretMemory(self.dd_start, end)
|
self.padawan.InterpretMemory(self.dd_start, end)
|
||||||
|
|
||||||
def do_do(self, address):
|
def do_do(self, address):
|
||||||
@ -3828,7 +3901,7 @@ def AnalyzeMinidump(options, minidump_name):
|
|||||||
stack_top = reader.ExceptionSP()
|
stack_top = reader.ExceptionSP()
|
||||||
stack_bottom = reader.StackBottom()
|
stack_bottom = reader.StackBottom()
|
||||||
stack_map = {reader.ExceptionIP(): -1}
|
stack_map = {reader.ExceptionIP(): -1}
|
||||||
for slot in range(stack_top, stack_bottom, reader.PointerSize()):
|
for slot in range(stack_top, stack_bottom, reader.MachinePointerSize()):
|
||||||
maybe_address = reader.ReadUIntPtr(slot)
|
maybe_address = reader.ReadUIntPtr(slot)
|
||||||
if not maybe_address in stack_map:
|
if not maybe_address in stack_map:
|
||||||
stack_map[maybe_address] = slot
|
stack_map[maybe_address] = slot
|
||||||
|
Loading…
Reference in New Issue
Block a user