[前][次][番号順一覧][スレッド一覧]

ruby-changes:63350

From: Aaron <ko1@a...>
Date: Thu, 15 Oct 2020 08:44:33 +0900 (JST)
Subject: [ruby-changes:63350] 8a06af5f88 (master): Mostly recover a Ruby stack trace from a core file

https://git.ruby-lang.org/ruby.git/commit/?id=8a06af5f88

From 8a06af5f88e07bf3723f06611d69466944079439 Mon Sep 17 00:00:00 2001
From: Aaron Patterson <tenderlove@r...>
Date: Wed, 14 Oct 2020 16:41:07 -0700
Subject: 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

diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py
index ff66fe4..0756127 100755
--- a/misc/lldb_cruby.py
+++ b/misc/lldb_cruby.py
@@ -14,6 +14,149 @@ import shlex https://github.com/ruby/ruby/blob/trunk/misc/lldb_cruby.py#L14
 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): https://github.com/ruby/ruby/blob/trunk/misc/lldb_cruby.py#L503
     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): https://github.com/ruby/ruby/blob/trunk/misc/lldb_cruby.py#L529
     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.")
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]