Skip to content

Commit 3b698e6

Browse files
ast-huggerConstellation
authored andcommitted
Add support for core JSC types to lldb_webkit.py
https://bugs.webkit.org/show_bug.cgi?id=294220 rdar://152866196 Reviewed by Yusuke Suzuki. This patch adds support for core JSC types, specifically JSValue, EncodedJSValue, JSCell, and JSString to lldb_webkit.py, so that the debugger shows descriptive summaries when displaying values of those types. * Tools/lldb/lldb_webkit.py: (__lldb_init_module): (WTFMediaTimeProvider.hasDoubleValue): (SummarizeJSValue): (SummarizeEncodedJSValue): (SummarizeJSString): (JSValueMirror): (JSValueMirror.__init__): (JSValueMirror.isNull): (JSValueMirror.isUndefined): (JSValueMirror.isEmpty): (JSValueMirror.isBool): (JSValueMirror.isTrue): (JSValueMirror.isFalse): (JSValueMirror.isInt32): (JSValueMirror.isNumber): (JSValueMirror.isDouble): (JSValueMirror.isCell): (JSValueMirror.toInt32): (JSValueMirror.toDouble): (JSValueMirror.summary): (JSCellMirror): (JSCellMirror.__init__): (JSCellMirror.enumMemberValue): (JSCellMirror.enumMemberName): (JSCellMirror.isString): (JSCellMirror.isObject): (JSCellMirror.isCustomGetterSetter): (JSCellMirror.summary): (JSStringMirror): (JSStringMirror.__init__): (JSStringMirror.isRopeString): (JSStringMirror.summary): (JSStringMirror.maybeTrim): (castSBValueToPointerType): (hexPointerString): Canonical link: https://commits.webkit.org/296021@main
1 parent adb15d9 commit 3b698e6

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

Tools/lldb/lldb_webkit.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
3030
"""
3131

32+
import ctypes
3233
import re
3334
import string
3435
import struct
@@ -90,6 +91,10 @@ def __lldb_init_module(debugger, dict):
9091
debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFOptionSet_SummaryProvider -x "^WTF::OptionSet<.+>$"')
9192
debugger.HandleCommand('type summary add --expand -F lldb_webkit.WTFCompactPointerTuple_SummaryProvider -x "^WTF::CompactPointerTuple<.+,.+>$"')
9293

94+
debugger.HandleCommand('type summary add --expand -F lldb_webkit.SummarizeEncodedJSValue JSC::EncodedJSValue')
95+
debugger.HandleCommand('type summary add --expand -F lldb_webkit.SummarizeJSString JSC::JSString')
96+
debugger.HandleCommand('type summary add --expand -F lldb_webkit.SummarizeJSValue JSC::JSValue')
97+
9398
debugger.HandleCommand('type summary add -F lldb_webkit.WTFURL_SummaryProvider WTF::URL')
9499
debugger.HandleCommand('type summary add -F lldb_webkit.WebCoreColor_SummaryProvider WebCore::Color')
95100

@@ -1301,3 +1306,200 @@ def isIndefinite(self):
13011306

13021307
def hasDoubleValue(self):
13031308
return self.valobj.GetChildMemberWithName('m_timeFlags').GetValueAsSigned(0) & (1 << 5)
1309+
1310+
1311+
def SummarizeJSValue(valobj, dict):
1312+
mirror = JSValueMirror(valobj)
1313+
return mirror.summary()
1314+
1315+
1316+
def SummarizeEncodedJSValue(valobj, dict):
1317+
mirror = JSValueMirror(valobj, valobj.GetValueAsUnsigned(0))
1318+
return mirror.summary()
1319+
1320+
1321+
def SummarizeJSString(valobj, dict):
1322+
mirror = JSStringMirror(valobj)
1323+
return mirror.summary()
1324+
1325+
1326+
class JSValueMirror:
1327+
"""Reflects on a JSValue or EncodedJSValue."""
1328+
1329+
OTHER_TAG = 0b0010
1330+
BOOL_TAG = 0b0100
1331+
UNDEFINED_TAG = 0b1000
1332+
1333+
NATIVE_CALLEE_TAG = OTHER_TAG | 1
1334+
NATIVE_CALLEE_MASK = 0b0111
1335+
1336+
TOP_15_BITS_MASK = 0xFFFE << 48
1337+
TOP_15_BITS_POINTER_TAG = 0
1338+
TOP_15_BITS_INTEGER_TAG = 0xFFFE << 48
1339+
1340+
NUMBER_TAG = 0xFFFE000000000000
1341+
MISC_TAG = OTHER_TAG | BOOL_TAG | UNDEFINED_TAG
1342+
1343+
NOT_CELL_MASK = NUMBER_TAG | OTHER_TAG
1344+
1345+
VALUE_NULL = OTHER_TAG
1346+
VALUE_UNDEFINED = OTHER_TAG | UNDEFINED_TAG
1347+
VALUE_TRUE = OTHER_TAG | BOOL_TAG | 1
1348+
VALUE_FALSE = OTHER_TAG | BOOL_TAG | 0
1349+
1350+
VALUE_EMPTY = 0
1351+
VALUE_DELETED = 4
1352+
1353+
def __init__(self, valobj, bits=None):
1354+
assert valobj.GetTypeName() == "JSC::JSValue" or valobj.GetTypeName() == "JSC::EncodedJSValue"
1355+
self.valobj = valobj
1356+
self.bits = bits if bits is not None else valobj.GetChildMemberWithName("u").GetChildMemberWithName("asInt64").GetValueAsUnsigned(0)
1357+
1358+
def isNull(self):
1359+
return self.bits == self.VALUE_NULL
1360+
1361+
def isUndefined(self):
1362+
return self.bits == self.VALUE_UNDEFINED
1363+
1364+
def isEmpty(self):
1365+
return self.bits == self.VALUE_EMPTY
1366+
1367+
def isBool(self):
1368+
return self.bits & ~1 == self.VALUE_FALSE
1369+
1370+
def isTrue(self):
1371+
return self.bits == self.VALUE_TRUE
1372+
1373+
def isFalse(self):
1374+
return self.bits == self.VALUE_TRUE
1375+
1376+
def isInt32(self):
1377+
return self.bits & self.NUMBER_TAG == self.NUMBER_TAG
1378+
1379+
def isNumber(self):
1380+
return self.bits & self.NUMBER_TAG != 0
1381+
1382+
def isDouble(self):
1383+
return self.isNumber() and not self.isInt32()
1384+
1385+
def isCell(self):
1386+
return self.bits & self.NOT_CELL_MASK == 0
1387+
1388+
def toInt32(self):
1389+
assert self.isInt32()
1390+
masked = self.bits & 0xFFFFFFFF
1391+
if masked > 0x80000000:
1392+
return masked - 0x100000000 # => -1
1393+
else:
1394+
return masked
1395+
1396+
def toDouble(self):
1397+
assert self.isDouble()
1398+
corrected = self.bits - (1 << 49)
1399+
return struct.unpack('<d', corrected.to_bytes(8, 'little'))[0]
1400+
1401+
def summary(self):
1402+
if self.isCell():
1403+
cell = castSBValueToPointerType("JSC::JSCell", self.valobj)
1404+
mirror = JSCellMirror(cell, self.bits)
1405+
return mirror.summary()
1406+
if self.isNull():
1407+
return "{JS null}"
1408+
if self.isUndefined():
1409+
return "{JS undefined}"
1410+
if self.isTrue():
1411+
return "{JS true}"
1412+
if self.isFalse():
1413+
return "{JS false}"
1414+
if self.isEmpty():
1415+
return "{JS empty}"
1416+
if self.isInt32():
1417+
return f"{{JS int32={self.toInt32()}}}"
1418+
if self.isDouble():
1419+
return f"{{JS double={self.toDouble()}}}"
1420+
return "{JSC ?}"
1421+
1422+
1423+
class JSCellMirror:
1424+
def __init__(self, valobj, bits):
1425+
assert valobj.GetTypeName() == "JSC::JSCell *"
1426+
self.valobj = valobj
1427+
self.bits = bits
1428+
self.type = valobj.GetChildMemberWithName("m_type").GetValueAsUnsigned(0)
1429+
typeEnum = valobj.GetTarget().FindFirstType("JSC::JSType")
1430+
self.enumMembers = typeEnum.GetEnumMembers()
1431+
1432+
def enumMemberValue(self, enumMemberName):
1433+
return self.enumMembers[enumMemberName].unsigned
1434+
1435+
def enumMemberName(self, enumMemberValue):
1436+
# try bisecting first, but it that fails give it another try linearly just to be sure
1437+
index = bisect_right(self.enumMembers, enumMemberValue, key=lambda mem: mem.unsigned)
1438+
guess = self.enumMembers[index - 1]
1439+
if guess.unsigned == enumMemberValue:
1440+
return guess.name
1441+
for member in self.enumMembers:
1442+
if member.unsigned == enumMemberValue:
1443+
return member.name
1444+
return "?"
1445+
1446+
def isString(self):
1447+
return self.type == self.enumMemberValue("StringType")
1448+
1449+
def isObject(self):
1450+
return self.type >= self.enumMemberValue("ObjectType")
1451+
1452+
def isCustomGetterSetter(self):
1453+
return self.type == self.enumMemberValue("CustomGetterSetterType")
1454+
1455+
def summary(self):
1456+
if self.isString():
1457+
string = castSBValueToPointerType("JSC::JSString", self.valobj)
1458+
mirror = JSStringMirror(string)
1459+
return mirror.summary(verbose=False)
1460+
if self.isObject():
1461+
return "{JS object %s}" % (self.enumMemberName(self.type))
1462+
return "{{JS cell %s}}}" % (self.enumMemberName(self.type))
1463+
1464+
1465+
class JSStringMirror:
1466+
def __init__(self, valobj):
1467+
assert valobj.GetTypeName() == "JSC::JSString *"
1468+
self.fiber = valobj.GetChildMemberWithName("m_fiber")
1469+
1470+
def isRopeString(self):
1471+
return self.fiber.GetValueAsUnsigned(0) & 1 != 0
1472+
1473+
def summary(self, verbose=True):
1474+
provider = WTFStringImplProvider(self.fiber, {})
1475+
if not provider.is_initialized():
1476+
raise RuntimeError("null fiber pointer in JSString mirror")
1477+
contents = self.maybeTrim(provider.to_string())
1478+
if verbose:
1479+
bitCount = 8 if provider.is_8bit() else 16
1480+
if self.isRopeString():
1481+
return '{JS ropeString bits=%d fiber1 length=%d %s ...}' % (bitCount, provider.get_length(), contents)
1482+
else:
1483+
return '{JS string bits=%d length=%d %s}' % (bitCount, provider.get_length(), contents)
1484+
else:
1485+
if self.isRopeString():
1486+
return '{JS ropeString fiber1=%s}' % (contents)
1487+
else:
1488+
return '{JS string %s}' % (contents)
1489+
1490+
def maybeTrim(self, string):
1491+
escaped = repr(string)
1492+
if len(escaped) > 90:
1493+
return f'{escaped[:80]} [... trimmed]'
1494+
else:
1495+
return escaped
1496+
1497+
1498+
def castSBValueToPointerType(typeName, value):
1499+
ptrType = value.GetTarget().FindFirstType(typeName).GetPointerType()
1500+
return value.Cast(ptrType)
1501+
1502+
1503+
def hexPointerString(bits):
1504+
"""Convert bits, which is an unsigned integer, to a string that would be produced by %p in C."""
1505+
return f"{bits:#0{2 + ctypes.sizeof(ctypes.c_void_p) * 2}x}"

0 commit comments

Comments
 (0)