1"""lldb data formatters for clang classes.
2
3Usage
4--
5import this file in your ~/.lldbinit by adding this line:
6
7command script import /path/to/ClangDataFormat.py
8
9After that, instead of getting this:
10
11(lldb) p Tok.Loc
12(clang::SourceLocation) $0 = {
13  (unsigned int) ID = 123582
14}
15
16you'll get:
17
18(lldb) p Tok.Loc
19(clang::SourceLocation) $4 = "/usr/include/i386/_types.h:37:1" (offset: 123582, file, local)
20"""
21
22import lldb
23
24def __lldb_init_module(debugger, internal_dict):
25	debugger.HandleCommand("type summary add -F ClangDataFormat.SourceLocation_summary clang::SourceLocation")
26	debugger.HandleCommand("type summary add -F ClangDataFormat.QualType_summary clang::QualType")
27	debugger.HandleCommand("type summary add -F ClangDataFormat.StringRef_summary llvm::StringRef")
28
29def SourceLocation_summary(srcloc, internal_dict):
30	return SourceLocation(srcloc).summary()
31
32def QualType_summary(qualty, internal_dict):
33	return QualType(qualty).summary()
34
35def StringRef_summary(strref, internal_dict):
36	return StringRef(strref).summary()
37
38class SourceLocation(object):
39	def __init__(self, srcloc):
40		self.srcloc = srcloc
41		self.ID = srcloc.GetChildAtIndex(0).GetValueAsUnsigned()
42		self.frame = srcloc.GetFrame()
43
44	def offset(self):
45		return getValueFromExpression(self.srcloc, ".getOffset()").GetValueAsUnsigned()
46
47	def isInvalid(self):
48		return self.ID == 0
49
50	def isMacro(self):
51		return getValueFromExpression(self.srcloc, ".isMacroID()").GetValueAsUnsigned()
52
53	def isLocal(self, srcmgr_path):
54		return self.frame.EvaluateExpression("(%s).isLocalSourceLocation(%s)" % (srcmgr_path, getExpressionPath(self.srcloc))).GetValueAsUnsigned()
55
56	def getPrint(self, srcmgr_path):
57		print_str = getValueFromExpression(self.srcloc, ".printToString(%s)" % srcmgr_path)
58		return print_str.GetSummary()
59
60	def summary(self):
61		if self.isInvalid():
62			return "<invalid loc>"
63		srcmgr_path = findObjectExpressionPath("clang::SourceManager", self.frame)
64		if srcmgr_path:
65			return "%s (offset: %d, %s, %s)" % (self.getPrint(srcmgr_path), self.offset(), "macro" if self.isMacro() else "file", "local" if self.isLocal(srcmgr_path) else "loaded")
66		return "(offset: %d, %s)" % (self.offset(), "macro" if self.isMacro() else "file")
67
68class QualType(object):
69	def __init__(self, qualty):
70		self.qualty = qualty
71
72	def getAsString(self):
73		std_str = getValueFromExpression(self.qualty, ".getAsString()")
74		return std_str.GetSummary()
75
76	def summary(self):
77		desc = self.getAsString()
78		if desc == '"NULL TYPE"':
79			return "<NULL TYPE>"
80		return desc
81
82class StringRef(object):
83	def __init__(self, strref):
84		self.strref = strref
85		self.Data_value = strref.GetChildAtIndex(0)
86		self.Length = strref.GetChildAtIndex(1).GetValueAsUnsigned()
87
88	def summary(self):
89		if self.Length == 0:
90			return '""'
91		data = self.Data_value.GetPointeeData(0, self.Length)
92		error = lldb.SBError()
93		string = data.ReadRawData(error, 0, data.GetByteSize())
94		if error.Fail():
95			return None
96		return '"%s"' % string
97
98
99# Key is a (function address, type name) tuple, value is the expression path for
100# an object with such a type name from inside that function.
101FramePathMapCache = {}
102
103def findObjectExpressionPath(typename, frame):
104	func_addr = frame.GetFunction().GetStartAddress().GetFileAddress()
105	key = (func_addr, typename)
106	try:
107		return FramePathMapCache[key]
108	except KeyError:
109		#print "CACHE MISS"
110		path = None
111		obj = findObject(typename, frame)
112		if obj:
113			path = getExpressionPath(obj)
114		FramePathMapCache[key] = path
115		return path
116
117def findObject(typename, frame):
118	def getTypename(value):
119		# FIXME: lldb should provide something like getBaseType
120		ty = value.GetType()
121		if ty.IsPointerType() or ty.IsReferenceType():
122			return ty.GetPointeeType().GetName()
123		return ty.GetName()
124
125	def searchForType(value, searched):
126		tyname = getTypename(value)
127		#print "SEARCH:", getExpressionPath(value), value.GetType().GetName()
128		if tyname == typename:
129			return value
130		ty = value.GetType()
131		if not (ty.IsPointerType() or
132		        ty.IsReferenceType() or
133				# FIXME: lldb should provide something like getCanonicalType
134		        tyname.startswith("llvm::IntrusiveRefCntPtr<") or
135		        tyname.startswith("llvm::OwningPtr<")):
136			return None
137		# FIXME: Hashing for SBTypes does not seem to work correctly, uses the typename instead,
138		# and not the canonical one unfortunately.
139		if tyname in searched:
140			return None
141		searched.add(tyname)
142		for i in range(value.GetNumChildren()):
143			child = value.GetChildAtIndex(i, 0, False)
144			found = searchForType(child, searched)
145			if found:
146				return found
147
148	searched = set()
149	value_list = frame.GetVariables(True, True, True, True)
150	for val in value_list:
151		found = searchForType(val, searched)
152		if found:
153			return found if not found.TypeIsPointerType() else found.Dereference()
154
155def getValueFromExpression(val, expr):
156	return val.GetFrame().EvaluateExpression(getExpressionPath(val) + expr)
157
158def getExpressionPath(val):
159	stream = lldb.SBStream()
160	val.GetExpressionPath(stream)
161	return stream.GetData()
162