diff options
Diffstat (limited to 'python/lldbutils')
-rw-r--r-- | python/lldbutils/README.txt | 221 | ||||
-rw-r--r-- | python/lldbutils/lldbutils/__init__.py | 13 | ||||
-rw-r--r-- | python/lldbutils/lldbutils/content.py | 21 | ||||
-rw-r--r-- | python/lldbutils/lldbutils/general.py | 105 | ||||
-rw-r--r-- | python/lldbutils/lldbutils/gfx.py | 130 | ||||
-rw-r--r-- | python/lldbutils/lldbutils/layout.py | 20 | ||||
-rw-r--r-- | python/lldbutils/lldbutils/utils.py | 70 |
7 files changed, 580 insertions, 0 deletions
diff --git a/python/lldbutils/README.txt b/python/lldbutils/README.txt new file mode 100644 index 000000000..a8db723f1 --- /dev/null +++ b/python/lldbutils/README.txt @@ -0,0 +1,221 @@ +lldb debugging functionality for Gecko +====================================== + +This directory contains a module, lldbutils, which is imported by the +in-tree .lldbinit file. The lldbutil modules define some lldb commands +that are handy for debugging Gecko. + +If you want to add a new command or Python-implemented type summary, either add +it to one of the existing broad area Python files (such as lldbutils/layout.py +for layout-related commands) or create a new file if none of the existing files +is appropriate. If you add a new file, make sure you add it to __all__ in +lldbutils/__init__.py. + + +Supported commands +------------------ + +Most commands below that can take a pointer to an object also support being +called with a smart pointer like nsRefPtr or nsCOMPtr. + + +* frametree EXPR, ft EXPR + frametreelimited EXPR, ftl EXPR + + Shows information about a frame tree. EXPR is an expression that + is evaluated, and must be an nsIFrame*. frametree displays the + entire frame tree that contains the given frame. frametreelimited + displays a subtree of the frame tree rooted at the given frame. + + (lldb) p this + (nsBlockFrame *) $4 = 0x000000011687fcb8 + (lldb) ftl this + Block(div)(-1)@0x11687fcb8 {0,0,7380,690} [state=0002100000d04601] [content=0x11688c0c0] [sc=0x11687f990:-moz-scrolled-content]< + line 0x116899130: count=1 state=inline,clean,prevmarginclean,not impacted,not wrapped,before:nobr,after:nobr[0x100] {60,0,0,690} vis-overflow=60,510,0,0 scr-overflow=60,510,0,0 < + Text(0)""@0x1168990c0 {60,510,0,0} [state=0001000020404000] [content=0x11687ca10] [sc=0x11687fd88:-moz-non-element,parent=0x11687eb00] [run=0x115115e80][0,0,T] + > + > + (lldb) ft this + Viewport(-1)@0x116017430 [view=0x115efe190] {0,0,60,60} [state=000b063000002623] [sc=0x1160170f8:-moz-viewport]< + HTMLScroll(html)(-1)@0x1160180d0 {0,0,0,0} [state=000b020000000403] [content=0x115e4d640] [sc=0x116017768:-moz-viewport-scroll]< + ... + Canvas(html)(-1)@0x116017e08 {0,0,60,60} vis-overflow=0,0,8340,2196 scr-overflow=0,0,8220,2196 [state=000b002000000601] [content=0x115e4d640] [sc=0x11687e0f8:-moz-scrolled-canvas]< + Block(html)(-1)@0x11687e578 {0,0,60,2196} vis-overflow=0,0,8340,2196 scr-overflow=0,0,8220,2196 [state=000b100000d00601] [content=0x115e4d640] [sc=0x11687e4b8,parent=0x0]< + line 0x11687ec48: count=1 state=block,clean,prevmarginclean,not impacted,not wrapped,before:nobr,after:nobr[0x48] bm=480 {480,480,0,1236} vis-overflow=360,426,7980,1410 scr-overflow=480,480,7740,1236 < + Block(body)(1)@0x11687ebb0 {480,480,0,1236} vis-overflow=-120,-54,7980,1410 scr-overflow=0,0,7740,1236 [state=000b120000100601] [content=0x115ed8980] [sc=0x11687e990]< + line 0x116899170: count=1 state=inline,clean,prevmarginclean,not impacted,not wrapped,before:nobr,after:nobr[0x0] {0,0,7740,1236} vis-overflow=-120,-54,7980,1410 scr-overflow=0,0,7740,1236 < + nsTextControlFrame@0x11687f068 {0,66,7740,1170} vis-overflow=-120,-120,7980,1410 scr-overflow=0,0,7740,1170 [state=0002000000004621] [content=0x115ca2c50] [sc=0x11687ea40]< + HTMLScroll(div)(-1)@0x11687f6b0 {180,240,7380,690} [state=0002000000084409] [content=0x11688c0c0] [sc=0x11687eb00]< + Block(div)(-1)@0x11687fcb8 {0,0,7380,690} [state=0002100000d04601] [content=0x11688c0c0] [sc=0x11687f990:-moz-scrolled-content]< + line 0x116899130: count=1 state=inline,clean,prevmarginclean,not impacted,not wrapped,before:nobr,after:nobr[0x100] {60,0,0,690} vis-overflow=60,510,0,0 scr-overflow=60,510,0,0 < + Text(0)""@0x1168990c0 {60,510,0,0} [state=0001000020404000] [content=0x11687ca10] [sc=0x11687fd88:-moz-non-element,parent=0x11687eb00] [run=0x115115e80][0,0,T] + ... + + +* js + + Dumps the current JS stack. + + (lldb) js + 0 anonymous(aForce = false) ["chrome://browser/content/browser.js":13414] + this = [object Object] + 1 updateAppearance() ["chrome://browser/content/browser.js":13326] + this = [object Object] + 2 handleEvent(aEvent = [object Event]) ["chrome://browser/content/tabbrowser.xml":3811] + this = [object XULElement] + + +* prefcnt EXPR + + Shows the refcount of a given object. EXPR is an expression that is + evaluated, and can be either a pointer to or an actual refcounted + object. The object can be a standard nsISupports-like refcounted + object, a cycle-collected object or a mozilla::RefCounted<T> object. + + (lldb) p this + (nsHTMLDocument *) $1 = 0x0000000116e9d800 + (lldb) prefcnt this + 20 + (lldb) p mDocumentURI + (nsCOMPtr<nsIURI>) $3 = { + mRawPtr = 0x0000000117163e50 + } + (lldb) prefcnt mDocumentURI + 11 + + +* pstate EXPR + + Shows the frame state bits (using their symbolic names) of a given frame. + EXPR is an expression that is evaluated, and must be an nsIFrame*. + + (lldb) p this + (nsTextFrame *) $1 = 0x000000011f470b10 + (lldb) p/x mState + (nsFrameState) $2 = 0x0000004080604000 + (lldb) pstate this + TEXT_HAS_NONCOLLAPSED_CHARACTERS | TEXT_END_OF_LINE | TEXT_START_OF_LINE | NS_FRAME_PAINTED_THEBES | NS_FRAME_INDEPENDENT_SELECTION + + +* ptag EXPR + + Shows the DOM tag name of a node. EXPR is an expression that is + evaluated, and can be either an nsINode pointer or a concrete DOM + object. + + (lldb) p this + (nsHTMLDocument *) $0 = 0x0000000116e9d800 + (lldb) ptag this + (PermanentAtomImpl *) $1 = 0x0000000110133ac0 u"#document" + (lldb) p this->GetRootElement() + (mozilla::dom::HTMLSharedElement *) $2 = 0x0000000118429780 + (lldb) ptag $2 + (PermanentAtomImpl *) $3 = 0x0000000110123b80 u"html" + + +Supported type summaries and synthetic children +----------------------------------------------- + +In lldb terminology, type summaries are rules for how to display a value +when using the "expression" command (or its familiar-to-gdb-users "p" alias), +and synthetic children are fake member variables or array elements also +added by custom rules. + +For objects that do have synthetic children defined for them, like nsTArray, +the "expr -R -- EXPR" command can be used to show its actual member variables. + + +* nsAString, nsACString, + nsFixedString, nsFixedCString, + nsAutoString, nsAutoCString + + Strings have a type summary that shows the actual string. + + (lldb) frame info + frame #0: 0x000000010400cfea XUL`nsCSSParser::ParseProperty(this=0x00007fff5fbf5248, aPropID=eCSSProperty_margin_top, aPropValue=0x00007fff5fbf53f8, aSheetURI=0x0000000115ae8c00, aBaseURI=0x0000000115ae8c00, aSheetPrincipal=0x000000010ff9e040, aDeclaration=0x00000001826fd580, aChanged=0x00007fff5fbf5247, aIsImportant=false, aIsSVGMode=false) + 74 at nsCSSParser.cpp:12851 + (lldb) p aPropValue + (const nsAString_internal) $16 = u"-25px" + + (lldb) p this + (nsHTMLDocument *) $18 = 0x0000000115b56000 + (lldb) p mContentType + (nsCString) $19 = { + nsACString_internal = "text/html" + } + +* nscolor + + nscolors (32-bit RGBA colors) have a type summary that shows the color as + one of the CSS 2.1 color keywords, a six digit hex color, an rgba() color, + or the "transparent" keyword. + + (lldb) p this + (nsTextFrame *) $0 = 0x00000001168245e0 + (lldb) p *this->StyleColor() + (const nsStyleColor) $1 = { + mColor = lime + } + (lldb) expr -R -- *this->StyleColor() + (const nsStyleColor) $2 = { + mColor = 4278255360 + } + +* nsIAtom + + Atoms have a type summary that shows the string value inside the atom. + + (lldb) frame info + frame #0: 0x00000001028b8c49 XUL`mozilla::dom::Element::GetBoolAttr(this=0x0000000115ca1c50, aAttr=0x000000011012a640) const + 25 at Element.h:907 + (lldb) p aAttr + (PermanentAtomImpl *) $1 = 0x000000011012a640 u"readonly" + +* nsTArray and friends + + nsTArrays and their auto and fallible varieties have synthetic children + for their elements. This means when displaying them with "expr" (or "p"), + they will be shown like regular arrays, rather than showing the mHdr and + other fields. + + (lldb) frame info + frame #0: 0x00000001043eb8a8 XUL`SVGTextFrame::DoGlyphPositioning(this=0x000000012f3f8778) + 248 at SVGTextFrame.cpp:4940 + (lldb) p charPositions + (nsTArray<nsPoint>) $5 = { + [0] = { + mozilla::gfx::BasePoint<int, nsPoint> = { + x = 0 + y = 816 + } + } + [1] = { + mozilla::gfx::BasePoint<int, nsPoint> = { + x = 426 + y = 816 + } + } + [2] = { + mozilla::gfx::BasePoint<int, nsPoint> = { + x = 906 + y = 816 + } + } + } + (lldb) expr -R -- charPositions + (nsTArray<nsPoint>) $4 = { + nsTArray_Impl<nsPoint, nsTArrayInfallibleAllocator> = { + nsTArray_base<nsTArrayInfallibleAllocator, nsTArray_CopyWithMemutils> = { + mHdr = 0x000000012f3f1b80 + } + } + } + +* nsTextNode, nsTextFragment + + Text nodes have a type summary that shows the nsTextFragment in the + nsTextNode, which itself has a type summary that shows the text + content. + + (lldb) p this + (nsTextFrame *) $14 = 0x000000011811bb10 + (lldb) p mContent + (nsTextNode *) $15 = 0x0000000118130110 "Search or enter address" + diff --git a/python/lldbutils/lldbutils/__init__.py b/python/lldbutils/lldbutils/__init__.py new file mode 100644 index 000000000..f27fa7297 --- /dev/null +++ b/python/lldbutils/lldbutils/__init__.py @@ -0,0 +1,13 @@ +import lldb + +__all__ = ['content', 'general', 'gfx', 'layout', 'utils'] + +def init(): + for name in __all__: + init = None + try: + init = __import__('lldbutils.' + name, globals(), locals(), ['init']).init + except AttributeError: + pass + if init: + init(lldb.debugger) diff --git a/python/lldbutils/lldbutils/content.py b/python/lldbutils/lldbutils/content.py new file mode 100644 index 000000000..93199001b --- /dev/null +++ b/python/lldbutils/lldbutils/content.py @@ -0,0 +1,21 @@ +import lldb +from lldbutils import utils + +def summarize_text_fragment(valobj, internal_dict): + content_union = valobj.GetChildAtIndex(0) + state_union = valobj.GetChildAtIndex(1).GetChildMemberWithName("mState") + length = state_union.GetChildMemberWithName("mLength").GetValueAsUnsigned(0) + if state_union.GetChildMemberWithName("mIs2b").GetValueAsUnsigned(0): + field = "m2b" + else: + field = "m1b" + ptr = content_union.GetChildMemberWithName(field) + return utils.format_string(ptr, length) + +def ptag(debugger, command, result, dict): + """Displays the tag name of a content node.""" + debugger.HandleCommand("expr (" + command + ")->mNodeInfo.mRawPtr->mInner.mName") + +def init(debugger): + debugger.HandleCommand("type summary add nsTextFragment -F lldbutils.content.summarize_text_fragment") + debugger.HandleCommand("command script add -f lldbutils.content.ptag ptag") diff --git a/python/lldbutils/lldbutils/general.py b/python/lldbutils/lldbutils/general.py new file mode 100644 index 000000000..27cf19aab --- /dev/null +++ b/python/lldbutils/lldbutils/general.py @@ -0,0 +1,105 @@ +import lldb +from lldbutils import utils + +def summarize_string(valobj, internal_dict): + data = valobj.GetChildMemberWithName("mData") + length = valobj.GetChildMemberWithName("mLength").GetValueAsUnsigned(0) + return utils.format_string(data, length) + +class TArraySyntheticChildrenProvider: + def __init__(self, valobj, internal_dict): + self.valobj = valobj + self.header = self.valobj.GetChildMemberWithName("mHdr") + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) + self.element_size = self.element_type.GetByteSize() + header_size = self.header.GetType().GetPointeeType().GetByteSize() + self.element_base_addr = self.header.GetValueAsUnsigned(0) + header_size + + def num_children(self): + return self.header.Dereference().GetChildMemberWithName("mLength").GetValueAsUnsigned(0) + + def get_child_index(self, name): + try: + index = int(name) + if index >= self.num_children(): + return None + except: + pass + return None + + def get_child_at_index(self, index): + if index >= self.num_children(): + return None + addr = self.element_base_addr + index * self.element_size + return self.valobj.CreateValueFromAddress("[%d]" % index, addr, self.element_type) + +def prefcnt(debugger, command, result, dict): + """Displays the refcount of an object.""" + # We handled regular nsISupports-like refcounted objects and cycle collected + # objects. + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + obj = frame.EvaluateExpression(command) + if obj.GetError().Fail(): + print "could not evaluate expression" + return + obj = utils.dereference(obj) + field = obj.GetChildMemberWithName("mRefCnt") + if field.GetError().Fail(): + field = obj.GetChildMemberWithName("refCnt") + if field.GetError().Fail(): + print "not a refcounted object" + return + refcnt_type = field.GetType().GetCanonicalType().GetName() + if refcnt_type == "nsAutoRefCnt": + print field.GetChildMemberWithName("mValue").GetValueAsUnsigned(0) + elif refcnt_type == "nsCycleCollectingAutoRefCnt": + print field.GetChildMemberWithName("mRefCntAndFlags").GetValueAsUnsigned(0) >> 2 + elif refcnt_type == "mozilla::ThreadSafeAutoRefCnt": + print field.GetChildMemberWithName("mValue").GetChildMemberWithName("mValue").GetValueAsUnsigned(0) + elif refcnt_type == "int": # non-atomic mozilla::RefCounted object + print field.GetValueAsUnsigned(0) + elif refcnt_type == "mozilla::Atomic<int>": # atomic mozilla::RefCounted object + print field.GetChildMemberWithName("mValue").GetValueAsUnsigned(0) + else: + print "unknown mRefCnt type " + refcnt_type + +# Used to work around http://llvm.org/bugs/show_bug.cgi?id=22211 +def callfunc(debugger, command, result, dict): + """Calls a function for which debugger information is unavailable by getting its address from the symbol table. + The function is assumed to return void.""" + + if '(' not in command: + print 'Usage: callfunc your_function(args)' + return + + command_parts = command.split('(') + funcname = command_parts[0].strip() + args = command_parts[1] + + target = debugger.GetSelectedTarget() + symbols = target.FindFunctions(funcname).symbols + if not symbols: + print 'Could not find a function symbol for a function called "%s"' % funcname + return + + sym = symbols[0] + arg_types = '()' + if sym.name and sym.name.startswith(funcname + '('): + arg_types = sym.name[len(funcname):] + debugger.HandleCommand('print ((void(*)%s)0x%0x)(%s' % (arg_types, sym.addr.GetLoadAddress(target), args)) + +def init(debugger): + debugger.HandleCommand("type summary add nsAString_internal -F lldbutils.general.summarize_string") + debugger.HandleCommand("type summary add nsACString_internal -F lldbutils.general.summarize_string") + debugger.HandleCommand("type summary add nsFixedString -F lldbutils.general.summarize_string") + debugger.HandleCommand("type summary add nsFixedCString -F lldbutils.general.summarize_string") + debugger.HandleCommand("type summary add nsAutoString -F lldbutils.general.summarize_string") + debugger.HandleCommand("type summary add nsAutoCString -F lldbutils.general.summarize_string") + debugger.HandleCommand("type synthetic add -x \"nsTArray<\" -l lldbutils.general.TArraySyntheticChildrenProvider") + debugger.HandleCommand("type synthetic add -x \"AutoTArray<\" -l lldbutils.general.TArraySyntheticChildrenProvider") + debugger.HandleCommand("type synthetic add -x \"FallibleTArray<\" -l lldbutils.general.TArraySyntheticChildrenProvider") + debugger.HandleCommand("command script add -f lldbutils.general.prefcnt -f lldbutils.general.prefcnt prefcnt") + debugger.HandleCommand("command script add -f lldbutils.general.callfunc -f lldbutils.general.callfunc callfunc") diff --git a/python/lldbutils/lldbutils/gfx.py b/python/lldbutils/lldbutils/gfx.py new file mode 100644 index 000000000..1ad9a37a7 --- /dev/null +++ b/python/lldbutils/lldbutils/gfx.py @@ -0,0 +1,130 @@ +import lldb + +def summarize_nscolor(valobj, internal_dict): + colors = { + "#800000": "maroon", + "#ff0000": "red", + "#ffa500": "orange", + "#ffff00": "yellow", + "#808000": "olive", + "#800080": "purple", + "#ff00ff": "fuchsia", + "#ffffff": "white", + "#00ff00": "lime", + "#008000": "green", + "#000080": "navy", + "#0000ff": "blue", + "#00ffff": "aqua", + "#008080": "teal", + "#000000": "black", + "#c0c0c0": "silver", + "#808080": "gray" + } + value = valobj.GetValueAsUnsigned(0) + if value == 0: + return "transparent" + if value & 0xff000000 != 0xff000000: + return "rgba(%d, %d, %d, %f)" % (value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, ((value >> 24) & 0xff) / 255.0) + color = "#%02x%02x%02x" % (value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff) + if color in colors: + return colors[color] + return color + +class RegionSyntheticChildrenProvider: + + def __init__(self, valobj, internal_dict, rect_type = "nsRect"): + self.rect_type = rect_type + self.valobj = valobj + self.pixman_region = self.valobj.GetChildMemberWithName("mImpl") + self.pixman_data = self.pixman_region.GetChildMemberWithName("data") + self.pixman_extents = self.pixman_region.GetChildMemberWithName("extents") + self.num_rects = self.pixman_region_num_rects() + self.box_type = self.pixman_extents.GetType() + self.box_type_size = self.box_type.GetByteSize() + self.box_list_base_ptr = self.pixman_data.GetValueAsUnsigned(0) + self.pixman_data.GetType().GetPointeeType().GetByteSize() + + def pixman_region_num_rects(self): + if self.pixman_data.GetValueAsUnsigned(0): + return self.pixman_data.Dereference().GetChildMemberWithName("numRects").GetValueAsUnsigned(0) + return 1 + + def num_children(self): + return 2 + self.num_rects + + def get_child_index(self, name): + if name == "numRects": + return 0 + if name == "bounds": + return 1 + return None + + def convert_pixman_box_to_rect(self, valobj, name): + x1 = valobj.GetChildMemberWithName("x1").GetValueAsSigned() + x2 = valobj.GetChildMemberWithName("x2").GetValueAsSigned() + y1 = valobj.GetChildMemberWithName("y1").GetValueAsSigned() + y2 = valobj.GetChildMemberWithName("y2").GetValueAsSigned() + return valobj.CreateValueFromExpression(name, + '%s(%d, %d, %d, %d)' % (self.rect_type, x1, y1, x2 - x1, y2 - y1)) + + def get_child_at_index(self, index): + if index == 0: + return self.pixman_data.CreateValueFromExpression('numRects', '(uint32_t)%d' % self.num_rects) + if index == 1: + return self.convert_pixman_box_to_rect(self.pixman_extents, 'bounds') + + rect_index = index - 2 + if rect_index >= self.num_rects: + return None + if self.num_rects == 1: + return self.convert_pixman_box_to_rect(self.pixman_extents, 'bounds') + box_address = self.box_list_base_ptr + rect_index * self.box_type_size + box = self.pixman_data.CreateValueFromAddress('', box_address, self.box_type) + return self.convert_pixman_box_to_rect(box, "[%d]" % rect_index) + +class IntRegionSyntheticChildrenProvider: + def __init__(self, valobj, internal_dict): + wrapped_region = valobj.GetChildMemberWithName("mImpl") + self.wrapped_provider = RegionSyntheticChildrenProvider(wrapped_region, internal_dict, "nsIntRect") + + def num_children(self): + return self.wrapped_provider.num_children() + + def get_child_index(self, name): + return self.wrapped_provider.get_child_index(name) + + def get_child_at_index(self, index): + return self.wrapped_provider.get_child_at_index(index) + +def summarize_rect(valobj, internal_dict): + x = valobj.GetChildMemberWithName("x").GetValue() + y = valobj.GetChildMemberWithName("y").GetValue() + width = valobj.GetChildMemberWithName("width").GetValue() + height = valobj.GetChildMemberWithName("height").GetValue() + return "%s, %s, %s, %s" % (x, y, width, height) + +def rect_is_empty(valobj): + width = valobj.GetChildMemberWithName("width").GetValueAsSigned() + height = valobj.GetChildMemberWithName("height").GetValueAsSigned() + return width <= 0 or height <= 0 + +def summarize_region(valobj, internal_dict): + # This function makes use of the synthetic children generated for ns(Int)Regions. + bounds = valobj.GetChildMemberWithName("bounds") + bounds_summary = summarize_rect(bounds, internal_dict) + num_rects = valobj.GetChildMemberWithName("numRects").GetValueAsUnsigned(0) + if num_rects <= 1: + if rect_is_empty(bounds): + return "empty" + if num_rects == 1: + return "one rect: " + bounds_summary + return str(num_rects) + " rects, bounds: " + bounds_summary + +def init(debugger): + debugger.HandleCommand("type summary add nscolor -v -F lldbutils.gfx.summarize_nscolor") + debugger.HandleCommand("type summary add nsRect -v -F lldbutils.gfx.summarize_rect") + debugger.HandleCommand("type summary add nsIntRect -v -F lldbutils.gfx.summarize_rect") + debugger.HandleCommand("type summary add gfxRect -v -F lldbutils.gfx.summarize_rect") + debugger.HandleCommand("type synthetic add nsRegion -l lldbutils.gfx.RegionSyntheticChildrenProvider") + debugger.HandleCommand("type synthetic add nsIntRegion -l lldbutils.gfx.IntRegionSyntheticChildrenProvider") + debugger.HandleCommand("type summary add nsRegion -v -F lldbutils.gfx.summarize_region") + debugger.HandleCommand("type summary add nsIntRegion -v -F lldbutils.gfx.summarize_region") diff --git a/python/lldbutils/lldbutils/layout.py b/python/lldbutils/lldbutils/layout.py new file mode 100644 index 000000000..a4894699c --- /dev/null +++ b/python/lldbutils/lldbutils/layout.py @@ -0,0 +1,20 @@ +import lldb + +def frametree(debugger, command, result, dict): + """Dumps the frame tree containing the given nsIFrame*.""" + debugger.HandleCommand('expr (' + command + ')->DumpFrameTree()') + +def frametreelimited(debugger, command, result, dict): + """Dumps the subtree of a frame tree rooted at the given nsIFrame*.""" + debugger.HandleCommand('expr (' + command + ')->DumpFrameTreeLimited()') + +def pstate(debugger, command, result, dict): + """Displays a frame's state bits symbolically.""" + debugger.HandleCommand('expr mozilla::PrintFrameState(' + command + ')') + +def init(debugger): + debugger.HandleCommand('command script add -f lldbutils.layout.frametree frametree') + debugger.HandleCommand('command script add -f lldbutils.layout.frametreelimited frametreelimited') + debugger.HandleCommand('command alias ft frametree') + debugger.HandleCommand('command alias ftl frametreelimited') + debugger.HandleCommand('command script add -f lldbutils.layout.pstate pstate'); diff --git a/python/lldbutils/lldbutils/utils.py b/python/lldbutils/lldbutils/utils.py new file mode 100644 index 000000000..4e038f630 --- /dev/null +++ b/python/lldbutils/lldbutils/utils.py @@ -0,0 +1,70 @@ +def format_char(c): + if c == 0: + return "\\0" + elif c == 0x07: + return "\\a" + elif c == 0x08: + return "\\b" + elif c == 0x0c: + return "\\f" + elif c == 0x0a: + return "\\n" + elif c == 0x0d: + return "\\r" + elif c == 0x09: + return "\\t" + elif c == 0x0b: + return "\\v" + elif c == 0x5c: + return "\\" + elif c == 0x22: + return "\\\"" + elif c == 0x27: + return "\\'" + elif c < 0x20 or c >= 0x80 and c <= 0xff: + return "\\x%02x" % c + elif c >= 0x0100: + return "\\u%04x" % c + else: + return chr(c) + +# Take an SBValue that is either a char* or char16_t* and formats it like lldb +# would when printing it. +def format_string(lldb_value, length=100): + ptr = lldb_value.GetValueAsUnsigned(0) + char_type = lldb_value.GetType().GetPointeeType() + if char_type.GetByteSize() == 1: + s = "\"" + size = 1 + mask = 0xff + elif char_type.GetByteSize() == 2: + s = "u\"" + size = 2 + mask = 0xffff + else: + return "(cannot format string with char type %s)" % char_type.GetName() + i = 0 + terminated = False + while i < length: + c = lldb_value.CreateValueFromAddress("x", ptr + i * size, char_type).GetValueAsUnsigned(0) & mask + if c == 0: + terminated = True + break + s += format_char(c) + i = i + 1 + s += "\"" + if not terminated and i != length: + s += "..." + return s + +# Dereferences a raw pointer, nsCOMPtr, RefPtr, nsAutoPtr, already_AddRefed or +# mozilla::RefPtr; otherwise returns the value unchanged. +def dereference(lldb_value): + if lldb_value.TypeIsPointerType(): + return lldb_value.Dereference() + name = lldb_value.GetType().GetUnqualifiedType().GetName() + if name.startswith("nsCOMPtr<") or name.startswith("RefPtr<") or name.startswith("nsAutoPtr<") or name.startswith("already_AddRefed<"): + return lldb_value.GetChildMemberWithName("mRawPtr") + if name.startswith("mozilla::RefPtr<"): + return lldb_value.GetChildMemberWithName("ptr") + return lldb_value |