1 ##===-- lldbutil.py ------------------------------------------*- Python -*-===##
3 ## The LLVM Compiler Infrastructure
5 ## This file is distributed under the University of Illinois Open Source
6 ## License. See LICENSE.TXT for details.
8 ##===----------------------------------------------------------------------===##
11 This LLDB module contains miscellaneous utilities.
12 Some of the test suite takes advantage of the utility functions defined here.
13 They can also be useful for general purpose lldb scripting.
20 # ===================================================
21 # Utilities for locating/checking executable programs
22 # ===================================================
25 """Returns True if fpath is an executable."""
26 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
29 """Returns the full path to a program; None otherwise."""
30 fpath, fname = os.path.split(program)
35 for path in os.environ["PATH"].split(os.pathsep):
36 exe_file = os.path.join(path, program)
41 # ===================================================
42 # Disassembly for an SBFunction or an SBSymbol object
43 # ===================================================
45 def disassemble(target, function_or_symbol):
46 """Disassemble the function or symbol given a target.
48 It returns the disassembly content in a string object.
50 buf = StringIO.StringIO()
51 insts = function_or_symbol.GetInstructions(target)
56 # ==========================================================
57 # Integer (byte size 1, 2, 4, and 8) to bytearray conversion
58 # ==========================================================
60 def int_to_bytearray(val, bytesize):
61 """Utility function to convert an integer into a bytearray.
63 It returns the bytearray in the little endian format. It is easy to get the
64 big endian format, just do ba.reverse() on the returned object.
69 return bytearray([val])
71 # Little endian followed by a format character.
82 packed = struct.pack(fmt, val)
83 return bytearray(map(ord, packed))
85 def bytearray_to_int(bytes, bytesize):
86 """Utility function to convert a bytearray into an integer.
88 It interprets the bytearray in the little endian format. For a big endian
89 bytearray, just do ba.reverse() on the object before passing it in.
96 # Little endian followed by a format character.
107 unpacked = struct.unpack(fmt, str(bytes))
111 # ==============================================================
112 # Get the description of an lldb object or None if not available
113 # ==============================================================
114 def get_description(obj, option=None):
115 """Calls lldb_obj.GetDescription() and returns a string, or None.
117 For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra
118 option can be passed in to describe the detailed level of description
120 o lldb.eDescriptionLevelBrief
121 o lldb.eDescriptionLevelFull
122 o lldb.eDescriptionLevelVerbose
124 method = getattr(obj, 'GetDescription')
127 tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint)
128 if isinstance(obj, tuple):
130 option = lldb.eDescriptionLevelBrief
132 stream = lldb.SBStream()
134 success = method(stream)
136 success = method(stream, option)
139 return stream.GetData()
142 # =================================================
143 # Convert some enum value to its string counterpart
144 # =================================================
146 def state_type_to_str(enum):
147 """Returns the stateType string given an enum."""
148 if enum == lldb.eStateInvalid:
150 elif enum == lldb.eStateUnloaded:
152 elif enum == lldb.eStateConnected:
154 elif enum == lldb.eStateAttaching:
156 elif enum == lldb.eStateLaunching:
158 elif enum == lldb.eStateStopped:
160 elif enum == lldb.eStateRunning:
162 elif enum == lldb.eStateStepping:
164 elif enum == lldb.eStateCrashed:
166 elif enum == lldb.eStateDetached:
168 elif enum == lldb.eStateExited:
170 elif enum == lldb.eStateSuspended:
173 raise Exception("Unknown StateType enum")
175 def stop_reason_to_str(enum):
176 """Returns the stopReason string given an enum."""
177 if enum == lldb.eStopReasonInvalid:
179 elif enum == lldb.eStopReasonNone:
181 elif enum == lldb.eStopReasonTrace:
183 elif enum == lldb.eStopReasonBreakpoint:
185 elif enum == lldb.eStopReasonWatchpoint:
187 elif enum == lldb.eStopReasonSignal:
189 elif enum == lldb.eStopReasonException:
191 elif enum == lldb.eStopReasonPlanComplete:
192 return "plancomplete"
193 elif enum == lldb.eStopReasonThreadExiting:
194 return "threadexiting"
196 raise Exception("Unknown StopReason enum")
198 def symbol_type_to_str(enum):
199 """Returns the symbolType string given an enum."""
200 if enum == lldb.eSymbolTypeInvalid:
202 elif enum == lldb.eSymbolTypeAbsolute:
204 elif enum == lldb.eSymbolTypeCode:
206 elif enum == lldb.eSymbolTypeData:
208 elif enum == lldb.eSymbolTypeTrampoline:
210 elif enum == lldb.eSymbolTypeRuntime:
212 elif enum == lldb.eSymbolTypeException:
214 elif enum == lldb.eSymbolTypeSourceFile:
216 elif enum == lldb.eSymbolTypeHeaderFile:
218 elif enum == lldb.eSymbolTypeObjectFile:
220 elif enum == lldb.eSymbolTypeCommonBlock:
222 elif enum == lldb.eSymbolTypeBlock:
224 elif enum == lldb.eSymbolTypeLocal:
226 elif enum == lldb.eSymbolTypeParam:
228 elif enum == lldb.eSymbolTypeVariable:
230 elif enum == lldb.eSymbolTypeVariableType:
231 return "variabletype"
232 elif enum == lldb.eSymbolTypeLineEntry:
234 elif enum == lldb.eSymbolTypeLineHeader:
236 elif enum == lldb.eSymbolTypeScopeBegin:
238 elif enum == lldb.eSymbolTypeScopeEnd:
240 elif enum == lldb.eSymbolTypeAdditional:
242 elif enum == lldb.eSymbolTypeCompiler:
244 elif enum == lldb.eSymbolTypeInstrumentation:
245 return "instrumentation"
246 elif enum == lldb.eSymbolTypeUndefined:
249 def value_type_to_str(enum):
250 """Returns the valueType string given an enum."""
251 if enum == lldb.eValueTypeInvalid:
253 elif enum == lldb.eValueTypeVariableGlobal:
254 return "global_variable"
255 elif enum == lldb.eValueTypeVariableStatic:
256 return "static_variable"
257 elif enum == lldb.eValueTypeVariableArgument:
258 return "argument_variable"
259 elif enum == lldb.eValueTypeVariableLocal:
260 return "local_variable"
261 elif enum == lldb.eValueTypeRegister:
263 elif enum == lldb.eValueTypeRegisterSet:
264 return "register_set"
265 elif enum == lldb.eValueTypeConstResult:
266 return "constant_result"
268 raise Exception("Unknown ValueType enum")
271 # ==================================================
272 # Get stopped threads due to each stop reason.
273 # ==================================================
275 def sort_stopped_threads(process,
276 breakpoint_threads = None,
277 crashed_threads = None,
278 watchpoint_threads = None,
279 signal_threads = None,
280 exiting_threads = None,
281 other_threads = None):
282 """ Fills array *_threads with threads stopped for the corresponding stop
285 for lst in [breakpoint_threads,
293 for thread in process:
295 for (reason, list) in [(lldb.eStopReasonBreakpoint, breakpoint_threads),
296 (lldb.eStopReasonException, crashed_threads),
297 (lldb.eStopReasonWatchpoint, watchpoint_threads),
298 (lldb.eStopReasonSignal, signal_threads),
299 (lldb.eStopReasonThreadExiting, exiting_threads),
300 (None, other_threads)]:
301 if not dispatched and list is not None:
302 if thread.GetStopReason() == reason or reason is None:
306 # ==================================================
307 # Utility functions for setting breakpoints
308 # ==================================================
310 def run_break_set_by_file_and_line (test, file_name, line_number, extra_options = None, num_expected_locations = 1, loc_exact=False, module_name=None):
311 """Set a breakpoint by file and line, returning the breakpoint number.
313 If extra_options is not None, then we append it to the breakpoint set command.
315 If num_expected_locations is -1 we check that we got AT LEAST one location, otherwise we check that num_expected_locations equals the number of locations.
317 If loc_exact is true, we check that there is one location, and that location must be at the input file and line number."""
319 if file_name == None:
320 command = 'breakpoint set -l %d'%(line_number)
322 command = 'breakpoint set -f "%s" -l %d'%(file_name, line_number)
325 command += " --shlib '%s'" % (module_name)
328 command += " " + extra_options
330 break_results = run_break_set_command (test, command)
332 if num_expected_locations == 1 and loc_exact:
333 check_breakpoint_result (test, break_results, num_locations=num_expected_locations, file_name = file_name, line_number = line_number, module_name=module_name)
335 check_breakpoint_result (test, break_results, num_locations = num_expected_locations)
337 return get_bpno_from_match (break_results)
339 def run_break_set_by_symbol (test, symbol, extra_options = None, num_expected_locations = -1, sym_exact = False, module_name=None):
340 """Set a breakpoint by symbol name. Common options are the same as run_break_set_by_file_and_line.
342 If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match."""
343 command = 'breakpoint set -n "%s"'%(symbol)
346 command += " --shlib '%s'" % (module_name)
349 command += " " + extra_options
351 break_results = run_break_set_command (test, command)
353 if num_expected_locations == 1 and sym_exact:
354 check_breakpoint_result (test, break_results, num_locations = num_expected_locations, symbol_name = symbol, module_name=module_name)
356 check_breakpoint_result (test, break_results, num_locations = num_expected_locations)
358 return get_bpno_from_match (break_results)
360 def run_break_set_by_selector (test, selector, extra_options = None, num_expected_locations = -1, module_name=None):
361 """Set a breakpoint by selector. Common options are the same as run_break_set_by_file_and_line."""
363 command = 'breakpoint set -S "%s"' % (selector)
366 command += ' --shlib "%s"' % (module_name)
369 command += " " + extra_options
371 break_results = run_break_set_command (test, command)
373 if num_expected_locations == 1:
374 check_breakpoint_result (test, break_results, num_locations = num_expected_locations, symbol_name = selector, symbol_match_exact=False, module_name=module_name)
376 check_breakpoint_result (test, break_results, num_locations = num_expected_locations)
378 return get_bpno_from_match (break_results)
380 def run_break_set_by_regexp (test, regexp, extra_options=None, num_expected_locations=-1):
381 """Set a breakpoint by regular expression match on symbol name. Common options are the same as run_break_set_by_file_and_line."""
383 command = 'breakpoint set -r "%s"'%(regexp)
385 command += " " + extra_options
387 break_results = run_break_set_command (test, command)
389 check_breakpoint_result (test, break_results, num_locations=num_expected_locations)
391 return get_bpno_from_match (break_results)
393 def run_break_set_by_source_regexp (test, regexp, extra_options=None, num_expected_locations=-1):
394 """Set a breakpoint by source regular expression. Common options are the same as run_break_set_by_file_and_line."""
395 command = 'breakpoint set -p "%s"'%(regexp)
397 command += " " + extra_options
399 break_results = run_break_set_command (test, command)
401 check_breakpoint_result (test, break_results, num_locations=num_expected_locations)
403 return get_bpno_from_match (break_results)
405 def run_break_set_command (test, command):
406 """Run the command passed in - it must be some break set variant - and analyze the result.
407 Returns a dictionary of information gleaned from the command-line results.
408 Will assert if the breakpoint setting fails altogether.
410 Dictionary will contain:
411 bpno - breakpoint of the newly created breakpoint, -1 on error.
412 num_locations - number of locations set for the breakpoint.
414 If there is only one location, the dictionary MAY contain:
415 file - source file name
416 line_no - source line number
418 inline_symbol - inlined symbol name
419 offset - offset from the original symbol
421 address - address at which the breakpoint was set."""
423 patterns = [r"^Breakpoint (?P<bpno>[0-9]+): (?P<num_locations>[0-9]+) locations\.$",
424 r"^Breakpoint (?P<bpno>[0-9]+): (?P<num_locations>no) locations \(pending\)\.",
425 r"^Breakpoint (?P<bpno>[0-9]+): where = (?P<module>.*)`(?P<symbol>[+\-]{0,1}[^+]+)( \+ (?P<offset>[0-9]+)){0,1}( \[inlined\] (?P<inline_symbol>.*)){0,1} at (?P<file>[^:]+):(?P<line_no>[0-9]+), address = (?P<address>0x[0-9a-fA-F]+)$",
426 r"^Breakpoint (?P<bpno>[0-9]+): where = (?P<module>.*)`(?P<symbol>.*)( \+ (?P<offset>[0-9]+)){0,1}, address = (?P<address>0x[0-9a-fA-F]+)$"]
427 match_object = test.match (command, patterns)
428 break_results = match_object.groupdict()
430 # We always insert the breakpoint number, setting it to -1 if we couldn't find it
431 # Also, make sure it gets stored as an integer.
432 if not 'bpno' in break_results:
433 break_results['bpno'] = -1
435 break_results['bpno'] = int(break_results['bpno'])
437 # We always insert the number of locations
438 # If ONE location is set for the breakpoint, then the output doesn't mention locations, but it has to be 1...
439 # We also make sure it is an integer.
441 if not 'num_locations' in break_results:
444 num_locations = break_results['num_locations']
445 if num_locations == 'no':
448 num_locations = int(break_results['num_locations'])
450 break_results['num_locations'] = num_locations
452 if 'line_no' in break_results:
453 break_results['line_no'] = int(break_results['line_no'])
457 def get_bpno_from_match (break_results):
458 return int (break_results['bpno'])
460 def check_breakpoint_result (test, break_results, file_name=None, line_number=-1, symbol_name=None, symbol_match_exact=True, module_name=None, offset=-1, num_locations=-1):
462 out_num_locations = break_results['num_locations']
464 if num_locations == -1:
465 test.assertTrue (out_num_locations > 0, "Expecting one or more locations, got none.")
467 test.assertTrue (num_locations == out_num_locations, "Expecting %d locations, got %d."%(num_locations, out_num_locations))
471 if 'file' in break_results:
472 out_file_name = break_results['file']
473 test.assertTrue (file_name == out_file_name, "Breakpoint file name '%s' doesn't match resultant name '%s'."%(file_name, out_file_name))
475 if line_number != -1:
477 if 'line_no' in break_results:
478 out_line_number = break_results['line_no']
480 test.assertTrue (line_number == out_line_number, "Breakpoint line number %s doesn't match resultant line %s."%(line_number, out_line_number))
484 # Look first for the inlined symbol name, otherwise use the symbol name:
485 if 'inline_symbol' in break_results and break_results['inline_symbol']:
486 out_symbol_name = break_results['inline_symbol']
487 elif 'symbol' in break_results:
488 out_symbol_name = break_results['symbol']
490 if symbol_match_exact:
491 test.assertTrue(symbol_name == out_symbol_name, "Symbol name '%s' doesn't match resultant symbol '%s'."%(symbol_name, out_symbol_name))
493 test.assertTrue(out_symbol_name.find(symbol_name) != -1, "Symbol name '%s' isn't in resultant symbol '%s'."%(symbol_name, out_symbol_name))
496 out_nodule_name = None
497 if 'module' in break_results:
498 out_module_name = break_results['module']
500 test.assertTrue (module_name.find(out_module_name) != -1, "Symbol module name '%s' isn't in expected module name '%s'."%(out_module_name, module_name))
502 # ==================================================
503 # Utility functions related to Threads and Processes
504 # ==================================================
506 def get_stopped_threads(process, reason):
507 """Returns the thread(s) with the specified stop reason in a list.
509 The list can be empty if no such thread exists.
513 if t.GetStopReason() == reason:
517 def get_stopped_thread(process, reason):
518 """A convenience function which returns the first thread with the given stop
523 1. Get the stopped thread due to a breakpoint condition
526 from lldbutil import get_stopped_thread
527 thread = get_stopped_thread(process, lldb.eStopReasonPlanComplete)
528 self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition")
531 2. Get the thread stopped due to a breakpoint
534 from lldbutil import get_stopped_thread
535 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)
536 self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint")
540 threads = get_stopped_threads(process, reason)
541 if len(threads) == 0:
545 def get_threads_stopped_at_breakpoint (process, bkpt):
546 """ For a stopped process returns the thread stopped at the breakpoint passed in bkpt"""
550 stopped_threads = get_stopped_threads (process, lldb.eStopReasonBreakpoint)
552 if len(stopped_threads) == 0:
555 for thread in stopped_threads:
556 # Make sure we've hit our breakpoint...
557 break_id = thread.GetStopReasonDataAtIndex (0)
558 if break_id == bkpt.GetID():
559 threads.append(thread)
563 def continue_to_breakpoint (process, bkpt):
564 """ Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None"""
566 if process.GetState() != lldb.eStateStopped:
569 return get_threads_stopped_at_breakpoint (process, bkpt)
571 def get_caller_symbol(thread):
573 Returns the symbol name for the call site of the leaf function.
575 depth = thread.GetNumFrames()
578 caller = thread.GetFrameAtIndex(1).GetSymbol()
580 return caller.GetName()
585 def get_function_names(thread):
587 Returns a sequence of function names from the stack frames of this thread.
590 return thread.GetFrameAtIndex(i).GetFunctionName()
592 return map(GetFuncName, range(thread.GetNumFrames()))
595 def get_symbol_names(thread):
597 Returns a sequence of symbols for this thread.
600 return thread.GetFrameAtIndex(i).GetSymbol().GetName()
602 return map(GetSymbol, range(thread.GetNumFrames()))
605 def get_pc_addresses(thread):
607 Returns a sequence of pc addresses for this thread.
610 return thread.GetFrameAtIndex(i).GetPCAddress()
612 return map(GetPCAddress, range(thread.GetNumFrames()))
615 def get_filenames(thread):
617 Returns a sequence of file names from the stack frames of this thread.
620 return thread.GetFrameAtIndex(i).GetLineEntry().GetFileSpec().GetFilename()
622 return map(GetFilename, range(thread.GetNumFrames()))
625 def get_line_numbers(thread):
627 Returns a sequence of line numbers from the stack frames of this thread.
629 def GetLineNumber(i):
630 return thread.GetFrameAtIndex(i).GetLineEntry().GetLine()
632 return map(GetLineNumber, range(thread.GetNumFrames()))
635 def get_module_names(thread):
637 Returns a sequence of module names from the stack frames of this thread.
639 def GetModuleName(i):
640 return thread.GetFrameAtIndex(i).GetModule().GetFileSpec().GetFilename()
642 return map(GetModuleName, range(thread.GetNumFrames()))
645 def get_stack_frames(thread):
647 Returns a sequence of stack frames for this thread.
649 def GetStackFrame(i):
650 return thread.GetFrameAtIndex(i)
652 return map(GetStackFrame, range(thread.GetNumFrames()))
655 def print_stacktrace(thread, string_buffer = False):
656 """Prints a simple stack trace of this thread."""
658 output = StringIO.StringIO() if string_buffer else sys.stdout
659 target = thread.GetProcess().GetTarget()
661 depth = thread.GetNumFrames()
663 mods = get_module_names(thread)
664 funcs = get_function_names(thread)
665 symbols = get_symbol_names(thread)
666 files = get_filenames(thread)
667 lines = get_line_numbers(thread)
668 addrs = get_pc_addresses(thread)
670 if thread.GetStopReason() != lldb.eStopReasonInvalid:
671 desc = "stop reason=" + stop_reason_to_str(thread.GetStopReason())
674 print >> output, "Stack trace for thread id={0:#x} name={1} queue={2} ".format(
675 thread.GetThreadID(), thread.GetName(), thread.GetQueueName()) + desc
677 for i in range(depth):
678 frame = thread.GetFrameAtIndex(i)
679 function = frame.GetFunction()
681 load_addr = addrs[i].GetLoadAddress(target)
683 file_addr = addrs[i].GetFileAddress()
684 start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress()
685 symbol_offset = file_addr - start_addr
686 print >> output, " frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}".format(
687 num=i, addr=load_addr, mod=mods[i], symbol=symbols[i], offset=symbol_offset)
689 print >> output, " frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}".format(
690 num=i, addr=load_addr, mod=mods[i],
691 func='%s [inlined]' % funcs[i] if frame.IsInlined() else funcs[i],
692 file=files[i], line=lines[i],
693 args=get_args_as_string(frame, showFuncName=False) if not frame.IsInlined() else '()')
696 return output.getvalue()
699 def print_stacktraces(process, string_buffer = False):
700 """Prints the stack traces of all the threads."""
702 output = StringIO.StringIO() if string_buffer else sys.stdout
704 print >> output, "Stack traces for " + str(process)
706 for thread in process:
707 print >> output, print_stacktrace(thread, string_buffer=True)
710 return output.getvalue()
712 # ===================================
713 # Utility functions related to Frames
714 # ===================================
716 def get_parent_frame(frame):
718 Returns the parent frame of the input frame object; None if not available.
720 thread = frame.GetThread()
725 if f.GetFrameID() == frame.GetFrameID():
728 # If we reach here, no parent has been found, return None.
731 def get_args_as_string(frame, showFuncName=True):
733 Returns the args of the input frame object as a string.
738 # in_scope_only => True
739 vars = frame.GetVariables(True, False, False, True) # type of SBValueList
740 args = [] # list of strings
742 args.append("(%s)%s=%s" % (var.GetTypeName(),
745 if frame.GetFunction():
746 name = frame.GetFunction().GetName()
747 elif frame.GetSymbol():
748 name = frame.GetSymbol().GetName()
752 return "%s(%s)" % (name, ", ".join(args))
754 return "(%s)" % (", ".join(args))
756 def print_registers(frame, string_buffer = False):
757 """Prints all the register sets of the frame."""
759 output = StringIO.StringIO() if string_buffer else sys.stdout
761 print >> output, "Register sets for " + str(frame)
763 registerSet = frame.GetRegisters() # Return type of SBValueList.
764 print >> output, "Frame registers (size of register set = %d):" % registerSet.GetSize()
765 for value in registerSet:
766 #print >> output, value
767 print >> output, "%s (number of children = %d):" % (value.GetName(), value.GetNumChildren())
769 print >> output, "Name: %s, Value: %s" % (child.GetName(), child.GetValue())
772 return output.getvalue()
774 def get_registers(frame, kind):
775 """Returns the registers given the frame and the kind of registers desired.
777 Returns None if there's no such kind.
779 registerSet = frame.GetRegisters() # Return type of SBValueList.
780 for value in registerSet:
781 if kind.lower() in value.GetName().lower():
787 """Returns the general purpose registers of the frame as an SBValue.
789 The returned SBValue object is iterable. An example:
791 from lldbutil import get_GPRs
792 regs = get_GPRs(frame)
794 print "%s => %s" % (reg.GetName(), reg.GetValue())
797 return get_registers(frame, "general purpose")
800 """Returns the floating point registers of the frame as an SBValue.
802 The returned SBValue object is iterable. An example:
804 from lldbutil import get_FPRs
805 regs = get_FPRs(frame)
807 print "%s => %s" % (reg.GetName(), reg.GetValue())
810 return get_registers(frame, "floating point")
813 """Returns the exception state registers of the frame as an SBValue.
815 The returned SBValue object is iterable. An example:
817 from lldbutil import get_ESRs
818 regs = get_ESRs(frame)
820 print "%s => %s" % (reg.GetName(), reg.GetValue())
823 return get_registers(frame, "exception state")
825 # ======================================
826 # Utility classes/functions for SBValues
827 # ======================================
829 class BasicFormatter(object):
830 """The basic formatter inspects the value object and prints the value."""
831 def format(self, value, buffer=None, indent=0):
833 output = StringIO.StringIO()
836 # If there is a summary, it suffices.
837 val = value.GetSummary()
838 # Otherwise, get the value.
840 val = value.GetValue()
841 if val == None and value.GetNumChildren() > 0:
842 val = "%s (location)" % value.GetLocation()
843 print >> output, "{indentation}({type}) {name} = {value}".format(
844 indentation = ' ' * indent,
845 type = value.GetTypeName(),
846 name = value.GetName(),
848 return output.getvalue()
850 class ChildVisitingFormatter(BasicFormatter):
851 """The child visiting formatter prints the value and its immediate children.
853 The constructor takes a keyword arg: indent_child, which defaults to 2.
855 def __init__(self, indent_child=2):
856 """Default indentation of 2 SPC's for the children."""
857 self.cindent = indent_child
858 def format(self, value, buffer=None):
860 output = StringIO.StringIO()
864 BasicFormatter.format(self, value, buffer=output)
866 BasicFormatter.format(self, child, buffer=output, indent=self.cindent)
868 return output.getvalue()
870 class RecursiveDecentFormatter(BasicFormatter):
871 """The recursive decent formatter prints the value and the decendents.
873 The constructor takes two keyword args: indent_level, which defaults to 0,
874 and indent_child, which defaults to 2. The current indentation level is
875 determined by indent_level, while the immediate children has an additional
876 indentation by inden_child.
878 def __init__(self, indent_level=0, indent_child=2):
879 self.lindent = indent_level
880 self.cindent = indent_child
881 def format(self, value, buffer=None):
883 output = StringIO.StringIO()
887 BasicFormatter.format(self, value, buffer=output, indent=self.lindent)
888 new_indent = self.lindent + self.cindent
890 if child.GetSummary() != None:
891 BasicFormatter.format(self, child, buffer=output, indent=new_indent)
893 if child.GetNumChildren() > 0:
894 rdf = RecursiveDecentFormatter(indent_level=new_indent)
895 rdf.format(child, buffer=output)
897 BasicFormatter.format(self, child, buffer=output, indent=new_indent)
899 return output.getvalue()