1#!/usr/bin/env python 2#coding: utf-8 3# 4# Usage: run `command script import -r misc/lldb_cruby.py` on LLDB 5# 6# Test: misc/test_lldb_cruby.rb 7# 8 9import lldb 10import commands 11import os 12import shlex 13 14def lldb_init(debugger): 15 target = debugger.GetSelectedTarget() 16 global SIZEOF_VALUE 17 SIZEOF_VALUE = target.FindFirstType("VALUE").GetByteSize() 18 19 value_types = [] 20 g = globals() 21 for enum in target.FindFirstGlobalVariable('ruby_dummy_gdb_enums'): 22 enum = enum.GetType() 23 members = enum.GetEnumMembers() 24 for i in xrange(0, members.GetSize()): 25 member = members.GetTypeEnumMemberAtIndex(i) 26 name = member.GetName() 27 value = member.GetValueAsUnsigned() 28 g[name] = value 29 30 if name.startswith('RUBY_T_'): 31 value_types.append(name) 32 g['value_types'] = value_types 33 34def string2cstr(rstring): 35 """Returns the pointer to the C-string in the given String object""" 36 flags = rstring.GetValueForExpressionPath(".basic->flags").unsigned 37 if flags & RUBY_T_MASK != RUBY_T_STRING: 38 raise TypeError("not a string") 39 if flags & RUBY_FL_USER1: 40 cptr = int(rstring.GetValueForExpressionPath(".as.heap.ptr").value, 0) 41 clen = int(rstring.GetValueForExpressionPath(".as.heap.len").value, 0) 42 else: 43 cptr = int(rstring.GetValueForExpressionPath(".as.ary").value, 0) 44 clen = (flags & RSTRING_EMBED_LEN_MASK) >> RSTRING_EMBED_LEN_SHIFT 45 return cptr, clen 46 47def output_string(ctx, rstring): 48 cptr, clen = string2cstr(rstring) 49 expr = 'printf("%%.*s", (size_t)%d, (const char*)%d)' % (clen, cptr) 50 ctx.frame.EvaluateExpression(expr) 51 52def fixnum_p(x): 53 return x & RUBY_FIXNUM_FLAG != 0 54 55def flonum_p(x): 56 return (x&RUBY_FLONUM_MASK) == RUBY_FLONUM_FLAG 57 58def static_sym_p(x): 59 return (x&~(~0<<RUBY_SPECIAL_SHIFT)) == RUBY_SYMBOL_FLAG 60 61def append_command_output(debugger, command, result): 62 output1 = result.GetOutput() 63 debugger.GetCommandInterpreter().HandleCommand(command, result) 64 output2 = result.GetOutput() 65 result.Clear() 66 result.write(output1) 67 result.write(output2) 68 69def lldb_rp(debugger, command, result, internal_dict): 70 if not ('RUBY_Qfalse' in globals()): 71 lldb_init(debugger) 72 73 target = debugger.GetSelectedTarget() 74 process = target.GetProcess() 75 thread = process.GetSelectedThread() 76 frame = thread.GetSelectedFrame() 77 if frame.IsValid(): 78 val = frame.EvaluateExpression(command) 79 else: 80 val = target.EvaluateExpression(command) 81 error = val.GetError() 82 if error.Fail(): 83 print >> result, error 84 return 85 lldb_inspect(debugger, target, result, val) 86 87def lldb_inspect(debugger, target, result, val): 88 num = val.GetValueAsSigned() 89 if num == RUBY_Qfalse: 90 print >> result, 'false' 91 elif num == RUBY_Qtrue: 92 print >> result, 'true' 93 elif num == RUBY_Qnil: 94 print >> result, 'nil' 95 elif num == RUBY_Qundef: 96 print >> result, 'undef' 97 elif fixnum_p(num): 98 print >> result, num >> 1 99 elif flonum_p(num): 100 append_command_output(debugger, "print rb_float_value(%0#x)" % val.GetValueAsUnsigned(), result) 101 elif static_sym_p(num): 102 if num < 128: 103 print >> result, "T_SYMBOL: %c" % num 104 else: 105 print >> result, "T_SYMBOL: (%x)" % num 106 elif num & RUBY_IMMEDIATE_MASK: 107 print >> result, 'immediate(%x)' % num 108 else: 109 tRBasic = target.FindFirstType("struct RBasic").GetPointerType() 110 val = val.Cast(tRBasic) 111 flags = val.GetValueForExpressionPath("->flags").GetValueAsUnsigned() 112 if (flags & RUBY_FL_PROMOTED) == RUBY_FL_PROMOTED: 113 print >> result, "[PROMOTED] " 114 flType = flags & RUBY_T_MASK 115 if flType == RUBY_T_NONE: 116 print >> result, 'T_NONE: %s' % val.Dereference() 117 elif flType == RUBY_T_NIL: 118 print >> result, 'T_NIL: %s' % val.Dereference() 119 elif flType == RUBY_T_OBJECT: 120 tRObject = target.FindFirstType("struct RObject").GetPointerType() 121 val = val.Cast(tRObject) 122 print >> result, 'T_OBJECT: %s' % val.Dereference() 123 elif flType == RUBY_T_CLASS or flType == RUBY_T_MODULE or flType == RUBY_T_ICLASS: 124 tRClass = target.FindFirstType("struct RClass").GetPointerType() 125 val = val.Cast(tRClass) 126 print >> result, 'T_%s: %s' % ('CLASS' if flType == RUBY_T_CLASS else 'MODULE' if flType == RUBY_T_MODULE else 'ICLASS', val.Dereference()) 127 elif flType == RUBY_T_STRING: 128 tRString = target.FindFirstType("struct RString").GetPointerType() 129 val = val.Cast(tRString) 130 if flags & RSTRING_NOEMBED: 131 print >> result, val.GetValueForExpressionPath("->as.heap") 132 else: 133 print >> result, val.GetValueForExpressionPath("->as.ary") 134 elif flType == RUBY_T_SYMBOL: 135 tRSymbol = target.FindFirstType("struct RSymbol").GetPointerType() 136 print >> result, val.Cast(tRSymbol).Dereference() 137 elif flType == RUBY_T_ARRAY: 138 tRArray = target.FindFirstType("struct RArray").GetPointerType() 139 val = val.Cast(tRArray) 140 if flags & RUBY_FL_USER1: 141 len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3)) 142 ptr = val.GetValueForExpressionPath("->as.ary") 143 else: 144 len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned() 145 ptr = val.GetValueForExpressionPath("->as.heap.ptr") 146 #print >> result, val.GetValueForExpressionPath("->as.heap") 147 result.write("T_ARRAY: len=%d" % len) 148 if flags & RUBY_FL_USER1: 149 result.write(" (embed)") 150 elif flags & RUBY_FL_USER2: 151 shared = val.GetValueForExpressionPath("->as.heap.aux.shared").GetValueAsUnsigned() 152 result.write(" (shared) shared=%016x") 153 else: 154 capa = val.GetValueForExpressionPath("->as.heap.aux.capa").GetValueAsSigned() 155 result.write(" (ownership) capa=%d" % capa) 156 if len == 0: 157 result.write(" {(empty)}") 158 else: 159 result.write("\n") 160 append_command_output(debugger, "expression -Z %d -fx -- (const VALUE*)%0#x" % (len, ptr.GetValueAsUnsigned()), result) 161 elif flType == RUBY_T_HASH: 162 append_command_output(debugger, "p *(struct RHash *) %0#x" % val.GetValueAsUnsigned(), result) 163 elif flType == RUBY_T_BIGNUM: 164 tRBignum = target.FindFirstType("struct RBignum").GetPointerType() 165 val = val.Cast(tRBignum) 166 if flags & RUBY_FL_USER2: 167 len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4|RUBY_FL_USER5)) >> (RUBY_FL_USHIFT+3)) 168 print >> result, "T_BIGNUM: len=%d (embed)" % len 169 append_command_output(debugger, "print ((struct RBignum *) %0#x)->as.ary" % val.GetValueAsUnsigned(), result) 170 else: 171 len = val.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned() 172 print >> result, "T_BIGNUM: len=%d" % len 173 print >> result, val.Dereference() 174 append_command_output(debugger, "expression -Z %x -fx -- (const BDIGIT*)((struct RBignum*)%d)->as.heap.digits" % (len, val.GetValueAsUnsigned()), result) 175 # append_command_output(debugger, "x ((struct RBignum *) %0#x)->as.heap.digits / %d" % (val.GetValueAsUnsigned(), len), result) 176 elif flType == RUBY_T_FLOAT: 177 tRFloat = target.FindFirstType("struct RFloat").GetPointerType() 178 val = val.Cast(tRFloat) 179 append_command_output(debugger, "p *(double *)%0#x" % val.GetValueForExpressionPath("->float_value").GetAddress(), result) 180 elif flType == RUBY_T_RATIONAL: 181 tRRational = target.FindFirstType("struct RRational").GetPointerType() 182 val = val.Cast(tRRational) 183 lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->num")) 184 output = result.GetOutput() 185 result.Clear() 186 result.write("(Rational) " + output.rstrip() + " / ") 187 lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->den")) 188 elif flType == RUBY_T_COMPLEX: 189 tRComplex = target.FindFirstType("struct RComplex").GetPointerType() 190 val = val.Cast(tRComplex) 191 lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->real")) 192 real = result.GetOutput().rstrip() 193 result.Clear() 194 lldb_inspect(debugger, target, result, val.GetValueForExpressionPath("->imag")) 195 imag = result.GetOutput().rstrip() 196 result.Clear() 197 if not imag.startswith("-"): 198 imag = "+" + imag 199 print >> result, "(Complex) " + real + imag + "i" 200 elif flType == RUBY_T_DATA: 201 tRTypedData = target.FindFirstType("struct RTypedData").GetPointerType() 202 val = val.Cast(tRTypedData) 203 flag = val.GetValueForExpressionPath("->typed_flag") 204 if flag.GetValueAsUnsigned() == 1: 205 print >> result, "T_DATA: %s" % val.GetValueForExpressionPath("->type->wrap_struct_name") 206 append_command_output(debugger, "p *(struct RTypedData *) %0#x" % val.GetValueAsUnsigned(), result) 207 else: 208 print >> result, "T_DATA:" 209 append_command_output(debugger, "p *(struct RData *) %0#x" % val.GetValueAsUnsigned(), result) 210 else: 211 print >> result, "Not-handled type %0#x" % flType 212 print >> result, val 213 214def count_objects(debugger, command, ctx, result, internal_dict): 215 objspace = ctx.frame.EvaluateExpression("ruby_current_vm->objspace") 216 num_pages = objspace.GetValueForExpressionPath(".heap_pages.allocated_pages").unsigned 217 218 counts = {} 219 total = 0 220 for t in range(0x00, RUBY_T_MASK+1): 221 counts[t] = 0 222 223 for i in range(0, num_pages): 224 print "\rcounting... %d/%d" % (i, num_pages), 225 page = objspace.GetValueForExpressionPath('.heap_pages.sorted[%d]' % i) 226 p = page.GetChildMemberWithName('start') 227 num_slots = page.GetChildMemberWithName('total_slots').unsigned 228 for j in range(0, num_slots): 229 obj = p.GetValueForExpressionPath('[%d]' % j) 230 flags = obj.GetValueForExpressionPath('.as.basic.flags').unsigned 231 obj_type = flags & RUBY_T_MASK 232 counts[obj_type] += 1 233 total += num_slots 234 235 print "\rTOTAL: %d, FREE: %d" % (total, counts[0x00]) 236 for sym in value_types: 237 print "%s: %d" % (sym, counts[globals()[sym]]) 238 239def stack_dump_raw(debugger, command, ctx, result, internal_dict): 240 ctx.frame.EvaluateExpression("rb_vmdebug_stack_dump_raw_current()") 241 242def dump_node(debugger, command, ctx, result, internal_dict): 243 args = shlex.split(command) 244 if not args: 245 return 246 node = args[0] 247 248 dump = ctx.frame.EvaluateExpression("(struct RString*)rb_parser_dump_tree((NODE*)(%s), 0)" % node) 249 output_string(ctx, dump) 250 251def __lldb_init_module(debugger, internal_dict): 252 debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp") 253 debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects") 254 debugger.HandleCommand("command script add -f lldb_cruby.stack_dump_raw SDR") 255 debugger.HandleCommand("command script add -f lldb_cruby.dump_node dump_node") 256 lldb_init(debugger) 257 print "lldb scripts for ruby has been installed." 258