mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	Mostly recover a Ruby stack trace from a core file
Update the lldb script so it can mostly recover a Ruby stack trace from a core file. It's still missing line numbers and dealing with CFUNCs, but you use it like this: ``` (lldb) rbbt ec rb_control_frame_t TYPE 0x7f6fd6555fa0 EVAL ./bootstraptest/runner.rb error!! 0x7f6fd6555f68 METHOD ./bootstraptest/runner.rb main 0x7f6fd6555f30 METHOD ./bootstraptest/runner.rb in_temporary_working_directory 0x7f6fd6555ef8 METHOD /home/aaron/git/ruby/lib/tmpdir.rb mktmpdir 0x7f6fd6555ec0 BLOCK ./bootstraptest/runner.rb block in in_temporary_working_directory 0x7f6fd6555e88 CFUNC 0x7f6fd6555e50 BLOCK ./bootstraptest/runner.rb block (2 levels) in in_temporary_working_directory 0x7f6fd6555e18 BLOCK ./bootstraptest/runner.rb block in main 0x7f6fd6555de0 METHOD ./bootstraptest/runner.rb exec_test 0x7f6fd6555da8 CFUNC 0x7f6fd6555d70 BLOCK ./bootstraptest/runner.rb block in exec_test 0x7f6fd6555d38 CFUNC 0x7f6fd6555d00 TOP /home/aaron/git/ruby/bootstraptest/test_insns.rb error!! 0x7f6fd6555cc8 CFUNC 0x7f6fd6555c90 BLOCK /home/aaron/git/ruby/bootstraptest/test_insns.rb block in <top (required)> 0x7f6fd6555c58 METHOD ./bootstraptest/runner.rb assert_equal 0x7f6fd6555c20 METHOD ./bootstraptest/runner.rb assert_check 0x7f6fd6555be8 METHOD ./bootstraptest/runner.rb show_progress 0x7f6fd6555bb0 METHOD ./bootstraptest/runner.rb with_stderr 0x7f6fd6555b78 BLOCK ./bootstraptest/runner.rb block in show_progress 0x7f6fd6555b40 BLOCK ./bootstraptest/runner.rb block in assert_check 0x7f6fd6555b08 METHOD ./bootstraptest/runner.rb get_result_string 0x7f6fd6555ad0 METHOD ./bootstraptest/runner.rb make_srcfile 0x7f6fd6555a98 CFUNC 0x7f6fd6555a60 BLOCK ./bootstraptest/runner.rb block in make_srcfile ``` Getting the main execution context is difficult (it is stored in a thread local) so for now you must supply an ec and this will make a backtrace
This commit is contained in:
		
							parent
							
								
									2e8b5968e1
								
							
						
					
					
						commit
						8a06af5f88
					
				
					 1 changed files with 163 additions and 0 deletions
				
			
		|  | @ -14,6 +14,149 @@ import shlex | |||
| HEAP_PAGE_ALIGN_LOG = 14 | ||||
| HEAP_PAGE_ALIGN_MASK = (~(~0 << HEAP_PAGE_ALIGN_LOG)) | ||||
| 
 | ||||
| class BackTrace: | ||||
|     VM_FRAME_MAGIC_METHOD = 0x11110001 | ||||
|     VM_FRAME_MAGIC_BLOCK = 0x22220001 | ||||
|     VM_FRAME_MAGIC_CLASS = 0x33330001 | ||||
|     VM_FRAME_MAGIC_TOP = 0x44440001 | ||||
|     VM_FRAME_MAGIC_CFUNC = 0x55550001 | ||||
|     VM_FRAME_MAGIC_IFUNC = 0x66660001 | ||||
|     VM_FRAME_MAGIC_EVAL = 0x77770001 | ||||
|     VM_FRAME_MAGIC_RESCUE = 0x78880001 | ||||
|     VM_FRAME_MAGIC_DUMMY = 0x79990001 | ||||
| 
 | ||||
|     VM_FRAME_MAGIC_MASK = 0x7fff0001 | ||||
| 
 | ||||
|     VM_FRAME_MAGIC_NAME = { | ||||
|             VM_FRAME_MAGIC_TOP: "TOP", | ||||
|             VM_FRAME_MAGIC_METHOD: "METHOD", | ||||
|             VM_FRAME_MAGIC_CLASS: "CLASS", | ||||
|             VM_FRAME_MAGIC_BLOCK: "BLOCK", | ||||
|             VM_FRAME_MAGIC_CFUNC: "CFUNC", | ||||
|             VM_FRAME_MAGIC_IFUNC: "IFUNC", | ||||
|             VM_FRAME_MAGIC_EVAL: "EVAL", | ||||
|             VM_FRAME_MAGIC_RESCUE: "RESCUE", | ||||
|             0: "-----" | ||||
|     } | ||||
| 
 | ||||
|     def __init__(self, debugger, command, result, internal_dict): | ||||
|         self.debugger = debugger | ||||
|         self.command = command | ||||
|         self.result = result | ||||
| 
 | ||||
|         self.target = debugger.GetSelectedTarget() | ||||
|         self.process = self.target.GetProcess() | ||||
|         self.thread = self.process.GetSelectedThread() | ||||
|         self.frame = self.thread.GetSelectedFrame() | ||||
|         self.tRString = self.target.FindFirstType("struct RString").GetPointerType() | ||||
|         self.tRArray = self.target.FindFirstType("struct RArray").GetPointerType() | ||||
| 
 | ||||
|         rb_cft_len = len("rb_control_frame_t") | ||||
|         method_type_length = sorted(map(len, self.VM_FRAME_MAGIC_NAME.values()), reverse=True)[0] | ||||
|         # cfp address, method type, function name | ||||
|         self.fmt = "%%-%ds %%-%ds %%s" % (rb_cft_len, method_type_length) | ||||
| 
 | ||||
|     def vm_frame_magic(self, cfp): | ||||
|         ep = cfp.GetValueForExpressionPath("->ep") | ||||
|         frame_type = ep.GetChildAtIndex(0).GetValueAsUnsigned() & self.VM_FRAME_MAGIC_MASK | ||||
|         return self.VM_FRAME_MAGIC_NAME.get(frame_type, "(none)") | ||||
| 
 | ||||
|     def rb_iseq_path_str(self, iseq): | ||||
|         tRBasic = self.target.FindFirstType("struct RBasic").GetPointerType() | ||||
| 
 | ||||
|         pathobj = iseq.GetValueForExpressionPath("->body->location.pathobj") | ||||
|         pathobj = pathobj.Cast(tRBasic) | ||||
|         flags = pathobj.GetValueForExpressionPath("->flags").GetValueAsUnsigned() | ||||
|         flType = flags & RUBY_T_MASK | ||||
| 
 | ||||
|         if flType == RUBY_T_ARRAY: | ||||
|             pathobj = pathobj.Cast(self.tRArray) | ||||
| 
 | ||||
|             if flags & RUBY_FL_USER1: | ||||
|                 len = ((flags & (RUBY_FL_USER3|RUBY_FL_USER4)) >> (RUBY_FL_USHIFT+3)) | ||||
|                 ptr = pathobj.GetValueForExpressionPath("->as.ary") | ||||
|             else: | ||||
|                 len = pathobj.GetValueForExpressionPath("->as.heap.len").GetValueAsSigned() | ||||
|                 ptr = pathobj.GetValueForExpressionPath("->as.heap.ptr") | ||||
| 
 | ||||
|             pathobj = ptr.GetChildAtIndex(0) | ||||
| 
 | ||||
|         pathobj = pathobj.Cast(self.tRString) | ||||
|         ptr, len = string2cstr(pathobj) | ||||
|         err = lldb.SBError() | ||||
|         path = self.target.process.ReadMemory(ptr, len, err) | ||||
|         if err.Success(): | ||||
|             return path.decode("utf-8") | ||||
|         else: | ||||
|             return "unknown" | ||||
| 
 | ||||
|     def dump_iseq_frame(self, cfp, iseq): | ||||
|         m = self.vm_frame_magic(cfp) | ||||
| 
 | ||||
|         if iseq.GetValueAsUnsigned(): | ||||
|             iseq_label = iseq.GetValueForExpressionPath("->body->location.label") | ||||
|             path = self.rb_iseq_path_str(iseq) | ||||
|             ptr, len = string2cstr(iseq_label.Cast(self.tRString)) | ||||
| 
 | ||||
|             err = lldb.SBError() | ||||
|             iseq_name = self.target.process.ReadMemory(ptr, len, err) | ||||
|             if err.Success(): | ||||
|                 iseq_name = iseq_name.decode("utf-8") | ||||
|             else: | ||||
|                 iseq_name = "error!!" | ||||
| 
 | ||||
|         else: | ||||
|             print("No iseq", file=self.result) | ||||
| 
 | ||||
|         print(self.fmt % (("%0#12x" % cfp.GetAddress().GetLoadAddress(self.target)), m, "%s %s" % (path, iseq_name)), file=self.result) | ||||
| 
 | ||||
|     def dump_cfunc_frame(self, cfp): | ||||
|         print(self.fmt % ("%0#12x" % (cfp.GetAddress().GetLoadAddress(self.target)), "CFUNC", ""), file=self.result) | ||||
| 
 | ||||
|     def print_bt(self, ec): | ||||
|         tRbExecutionContext_t = self.target.FindFirstType("rb_execution_context_t") | ||||
|         ec = ec.Cast(tRbExecutionContext_t.GetPointerType()) | ||||
|         vm_stack = ec.GetValueForExpressionPath("->vm_stack") | ||||
|         vm_stack_size = ec.GetValueForExpressionPath("->vm_stack_size") | ||||
| 
 | ||||
|         last_cfp_frame = ec.GetValueForExpressionPath("->cfp") | ||||
|         cfp_type_p = last_cfp_frame.GetType() | ||||
| 
 | ||||
|         stack_top = vm_stack.GetValueAsUnsigned() + ( | ||||
|                 vm_stack_size.GetValueAsUnsigned() * vm_stack.GetType().GetByteSize()) | ||||
| 
 | ||||
|         cfp_frame_size = cfp_type_p.GetPointeeType().GetByteSize() | ||||
| 
 | ||||
|         start_cfp = stack_top | ||||
|         # Skip dummy frames | ||||
|         start_cfp -= cfp_frame_size | ||||
|         start_cfp -= cfp_frame_size | ||||
| 
 | ||||
|         last_cfp = last_cfp_frame.GetValueAsUnsigned() | ||||
| 
 | ||||
|         size = ((start_cfp - last_cfp) / cfp_frame_size) + 1 | ||||
| 
 | ||||
|         print(self.fmt % ("rb_control_frame_t", "TYPE", ""), file=self.result) | ||||
| 
 | ||||
|         curr_addr = start_cfp | ||||
| 
 | ||||
|         while curr_addr >= last_cfp: | ||||
|             cfp = self.target.CreateValueFromAddress("cfp", lldb.SBAddress(curr_addr, self.target), cfp_type_p.GetPointeeType()) | ||||
|             ep = cfp.GetValueForExpressionPath("->ep") | ||||
|             iseq = cfp.GetValueForExpressionPath("->iseq") | ||||
| 
 | ||||
|             frame_type = ep.GetChildAtIndex(0).GetValueAsUnsigned() & self.VM_FRAME_MAGIC_MASK | ||||
| 
 | ||||
|             if iseq.GetValueAsUnsigned(): | ||||
|                 pc = cfp.GetValueForExpressionPath("->pc") | ||||
|                 if pc.GetValueAsUnsigned(): | ||||
|                     self.dump_iseq_frame(cfp, iseq) | ||||
|             else: | ||||
|                 if frame_type == self.VM_FRAME_MAGIC_CFUNC: | ||||
|                     self.dump_cfunc_frame(cfp) | ||||
| 
 | ||||
|             curr_addr -= cfp_frame_size | ||||
| 
 | ||||
| def lldb_init(debugger): | ||||
|     target = debugger.GetSelectedTarget() | ||||
|     global SIZEOF_VALUE | ||||
|  | @ -360,6 +503,25 @@ def dump_node(debugger, command, ctx, result, internal_dict): | |||
|     dump = ctx.frame.EvaluateExpression("(struct RString*)rb_parser_dump_tree((NODE*)(%s), 0)" % node) | ||||
|     output_string(ctx, result, dump) | ||||
| 
 | ||||
| def rb_backtrace(debugger, command, result, internal_dict): | ||||
|     bt = BackTrace(debugger, command, result, internal_dict) | ||||
|     frame = bt.frame | ||||
| 
 | ||||
|     if command: | ||||
|         if frame.IsValid(): | ||||
|             val = frame.EvaluateExpression(command) | ||||
|         else: | ||||
|             val = target.EvaluateExpression(command) | ||||
| 
 | ||||
|         error = val.GetError() | ||||
|         if error.Fail(): | ||||
|             print >> result, error | ||||
|             return | ||||
|     else: | ||||
|         print("Need an EC for now") | ||||
| 
 | ||||
|     bt.print_bt(val) | ||||
| 
 | ||||
| def __lldb_init_module(debugger, internal_dict): | ||||
|     debugger.HandleCommand("command script add -f lldb_cruby.lldb_rp rp") | ||||
|     debugger.HandleCommand("command script add -f lldb_cruby.count_objects rb_count_objects") | ||||
|  | @ -367,5 +529,6 @@ def __lldb_init_module(debugger, internal_dict): | |||
|     debugger.HandleCommand("command script add -f lldb_cruby.dump_node dump_node") | ||||
|     debugger.HandleCommand("command script add -f lldb_cruby.heap_page heap_page") | ||||
|     debugger.HandleCommand("command script add -f lldb_cruby.heap_page_body heap_page_body") | ||||
|     debugger.HandleCommand("command script add -f lldb_cruby.rb_backtrace rbbt") | ||||
|     lldb_init(debugger) | ||||
|     print("lldb scripts for ruby has been installed.") | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Aaron Patterson
						Aaron Patterson