diff options
Diffstat (limited to 'misc')
| -rw-r--r-- | misc/.vscode/settings.json | 8 | ||||
| -rwxr-xr-x | misc/expand_tabs.rb | 34 | ||||
| -rwxr-xr-x | misc/jit_perf.py (renamed from misc/yjit_perf.py) | 8 | ||||
| -rw-r--r--[-rwxr-xr-x] | misc/lldb_cruby.py | 22 | ||||
| -rw-r--r-- | misc/lldb_rb/commands/heap_page_command.py | 5 | ||||
| -rw-r--r-- | misc/lldb_rb/commands/print_flags_command.py | 6 | ||||
| -rw-r--r-- | misc/lldb_rb/lldb_interface.py | 11 | ||||
| -rw-r--r-- | misc/lldb_rb/rb_heap_structs.py | 19 | ||||
| -rw-r--r-- | misc/lldb_rb/utils.py | 35 | ||||
| -rw-r--r-- | misc/tsan_suppressions.txt | 109 |
10 files changed, 175 insertions, 82 deletions
diff --git a/misc/.vscode/settings.json b/misc/.vscode/settings.json index 7b1a38c536..a2e4e1ec69 100644 --- a/misc/.vscode/settings.json +++ b/misc/.vscode/settings.json @@ -2,7 +2,9 @@ "rust-analyzer.cargo.features": [ "disasm", ], - "rust-analyzer.cargo.unsetTest": [ - "yjit", - ], + "rust-analyzer.cfg.setTest": false, + // rust-analyzer bundled in the VSCode extension may only support Rust newer than 1.85.0. + // To avoid warnings, install rust-analyzer with `rustup component add rust-analyzer` and + // use `~/.cargo/bin/rust-analyzer` with the following config. + "rust-analyzer.server.path": "rust-analyzer", } diff --git a/misc/expand_tabs.rb b/misc/expand_tabs.rb index a94eea5046..d26568eefc 100755 --- a/misc/expand_tabs.rb +++ b/misc/expand_tabs.rb @@ -59,53 +59,31 @@ class Git end DEFAULT_GEM_LIBS = %w[ - abbrev - base64 - benchmark bundler - cmath - csv - debug delegate did_you_mean - drb english erb + error_highlight fileutils find forwardable - getoptlong ipaddr - irb - logger - mutex_m net-http net-protocol - observer open3 open-uri optparse ostruct pp prettyprint - prime - pstore - rdoc - readline - reline + prism resolv - resolv-replace - rexml - rinda - rss rubygems - scanf securerandom - set shellwords singleton tempfile - thwait time timeout tmpdir @@ -117,27 +95,19 @@ DEFAULT_GEM_LIBS = %w[ ] DEFAULT_GEM_EXTS = %w[ - bigdecimal - cgi date digest etc fcntl - fiddle io-console io-nonblock io-wait json - nkf openssl pathname psych - racc - readline-ext stringio strscan - syslog - win32ole zlib ] diff --git a/misc/yjit_perf.py b/misc/jit_perf.py index 61434e5eb4..bc0f961b20 100755 --- a/misc/yjit_perf.py +++ b/misc/jit_perf.py @@ -22,10 +22,10 @@ def categorize_symbol(dso, symbol): return '[sha256]' elif symbol.startswith('[JIT] gen_send'): return '[JIT send]' - elif symbol.startswith('[JIT]'): + elif symbol.startswith('[JIT]') or symbol.startswith('ZJIT: ') or dso.startswith('perf-'): return '[JIT code]' - elif '::' in symbol or symbol.startswith('yjit::') or symbol.startswith('_ZN4yjit'): - return '[YJIT compile]' + elif '::' in symbol or symbol.startswith('_ZN4yjit') or symbol.startswith('_ZN4zjit'): + return '[JIT compile]' elif symbol.startswith('rb_vm_') or symbol.startswith('vm_') or symbol in { "rb_call0", "callable_method_entry_or_negative", "invoke_block_from_c_bh", "rb_funcallv_scope", "setup_parameters_complex", "rb_yield"}: @@ -108,7 +108,7 @@ if __name__ == "__main__" and len(sys.argv) == 2: row = line.split(maxsplit=6) period = row[3] # "1212775" - symbol, dso = row[6].split(" (") # "[JIT] getlocal_WC_0+0x0", "/tmp/perf-78207.map)\n" + symbol, dso = row[6].rsplit(" (", 1) # "[JIT] getlocal_WC_0+0x0", "/tmp/perf-78207.map)\n" symbol = symbol.split("+")[0] # "[JIT] getlocal_WC_0" dso = dso.split(")")[0] # "/tmp/perf-78207.map" diff --git a/misc/lldb_cruby.py b/misc/lldb_cruby.py index 0707bb42d3..b3d4fb509a 100755..100644 --- a/misc/lldb_cruby.py +++ b/misc/lldb_cruby.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python #coding: utf-8 # # Usage: run `command script import -r misc/lldb_cruby.py` on LLDB @@ -14,6 +13,7 @@ import sys import shlex import platform import glob +import math from lldb_rb.constants import * @@ -67,7 +67,7 @@ class BackTrace: return self.VM_FRAME_MAGIC_NAME.get(frame_type, "(none)") def rb_iseq_path_str(self, iseq): - tRBasic = self.target.FindFirstType("struct RBasic").GetPointerType() + tRBasic = self.target.FindFirstType("::RBasic").GetPointerType() pathobj = iseq.GetValueForExpressionPath("->body->location.pathobj") pathobj = pathobj.Cast(tRBasic) @@ -269,8 +269,7 @@ def lldb_inspect(debugger, target, result, val): elif num & RUBY_IMMEDIATE_MASK: print('immediate(%x)' % num, file=result) else: - tRBasic = target.FindFirstType("struct RBasic").GetPointerType() - tRValue = target.FindFirstType("struct RVALUE") + tRBasic = target.FindFirstType("::RBasic").GetPointerType() val = val.Cast(tRBasic) flags = val.GetValueForExpressionPath("->flags").GetValueAsUnsigned() @@ -524,10 +523,11 @@ def rb_backtrace(debugger, command, result, internal_dict): bt.print_bt(val) def dump_bits(target, result, page, object_address, end = "\n"): - tRValue = target.FindFirstType("struct RVALUE") + slot_size = page.GetChildMemberWithName("heap").GetChildMemberWithName("slot_size").unsigned + byte_size = 40 ** math.floor(math.log(slot_size, 40)) tUintPtr = target.FindFirstType("uintptr_t") # bits_t - num_in_page = (object_address & HEAP_PAGE_ALIGN_MASK) // tRValue.GetByteSize(); + num_in_page = (object_address & HEAP_PAGE_ALIGN_MASK) // byte_size; bits_bitlength = tUintPtr.GetByteSize() * 8 bitmap_index = num_in_page // bits_bitlength bitmap_offset = num_in_page & (bits_bitlength - 1) @@ -549,8 +549,7 @@ class HeapPageIter: self.num_slots = page.GetChildMemberWithName('total_slots').unsigned self.slot_size = page.GetChildMemberWithName('heap').GetChildMemberWithName('slot_size').unsigned self.counter = 0 - self.tRBasic = target.FindFirstType("struct RBasic") - self.tRValue = target.FindFirstType("struct RVALUE") + self.tRBasic = target.FindFirstType("::RBasic") def is_valid(self): heap_page_header_size = self.target.FindFirstType("struct heap_page_header").GetByteSize() @@ -582,14 +581,13 @@ def dump_page_internal(page, target, process, thread, frame, result, debugger, h freelist = [] fl_start = page.GetChildMemberWithName('freelist').GetValueAsUnsigned() - tRVALUE = target.FindFirstType("struct RVALUE") + free_slot = target.FindFirstType("struct free_slot") while fl_start > 0: freelist.append(fl_start) obj_addr = lldb.SBAddress(fl_start, target) - obj = target.CreateValueFromAddress("object", obj_addr, tRVALUE) - fl_start = obj.GetChildMemberWithName("as").GetChildMemberWithName("free").GetChildMemberWithName("next").GetValueAsUnsigned() - + obj = target.CreateValueFromAddress("object", obj_addr, free_slot) + fl_start = obj.GetChildMemberWithName("next").GetValueAsUnsigned() page_iter = HeapPageIter(page, target) if page_iter.is_valid(): diff --git a/misc/lldb_rb/commands/heap_page_command.py b/misc/lldb_rb/commands/heap_page_command.py index b56a3eae4e..2eed3c3bee 100644 --- a/misc/lldb_rb/commands/heap_page_command.py +++ b/misc/lldb_rb/commands/heap_page_command.py @@ -8,14 +8,15 @@ class HeapPageCommand(RbBaseCommand): help_string = "prints out 'struct heap_page' for a VALUE pointer in the page" def call(self, debugger, command, exe_ctx, result): + self.result = result self.t_heap_page_body = self.target.FindFirstType("struct heap_page_body") self.t_heap_page_ptr = self.target.FindFirstType("struct heap_page").GetPointerType() page = self._get_page(self.frame.EvaluateExpression(command)) page.Cast(self.t_heap_page_ptr) - self._append_expression(debugger, "(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result) - self._append_expression(debugger, "*(struct heap_page *) %0#x" % page.GetValueAsUnsigned(), result) + self._append_expression("(struct heap_page *) %0#x" % page.GetValueAsUnsigned()) + self._append_expression("*(struct heap_page *) %0#x" % page.GetValueAsUnsigned()) def _get_page(self, val): addr = val.GetValueAsUnsigned() diff --git a/misc/lldb_rb/commands/print_flags_command.py b/misc/lldb_rb/commands/print_flags_command.py index 00da4834bf..bc494ae01a 100644 --- a/misc/lldb_rb/commands/print_flags_command.py +++ b/misc/lldb_rb/commands/print_flags_command.py @@ -11,13 +11,13 @@ class PrintFlagsCommand(RbBaseCommand): # call is where our command logic will be implemented def call(self, debugger, command, exe_ctx, result): - rclass_t = self.target.FindFirstType("struct RBasic") + rclass_t = self.target.FindFirstType("::RBasic") rcass_ptr = self.target.EvaluateExpression(command).Cast(rclass_t.GetPointerType()) obj_flags = rcass_ptr.GetValueForExpressionPath("->flags").GetValueAsUnsigned() flags = [ - "RUBY_FL_WB_PROTECTED", "RUBY_FL_PROMOTED0", "RUBY_FL_PROMOTED1", "RUBY_FL_FINALIZE", - "RUBY_FL_SHAREABLE", "RUBY_FL_EXIVAR", "RUBY_FL_FREEZE", + "RUBY_FL_WB_PROTECTED", "RUBY_FL_PROMOTED", "RUBY_FL_FINALIZE", + "RUBY_FL_SHAREABLE", "RUBY_FL_FREEZE", "RUBY_FL_USER0", "RUBY_FL_USER1", "RUBY_FL_USER2", "RUBY_FL_USER3", "RUBY_FL_USER4", "RUBY_FL_USER5", "RUBY_FL_USER6", "RUBY_FL_USER7", "RUBY_FL_USER8", "RUBY_FL_USER9", "RUBY_FL_USER10", "RUBY_FL_USER11", "RUBY_FL_USER12", "RUBY_FL_USER13", "RUBY_FL_USER14", diff --git a/misc/lldb_rb/lldb_interface.py b/misc/lldb_rb/lldb_interface.py index 785a54b3e3..25930b2e16 100644 --- a/misc/lldb_rb/lldb_interface.py +++ b/misc/lldb_rb/lldb_interface.py @@ -5,3 +5,14 @@ class LLDBInterface: self.process = self.target.GetProcess() self.thread = self.process.GetSelectedThread() self.frame = self.thread.GetSelectedFrame() + + def _append_command_output(self, command): + output1 = self.result.GetOutput() + self.debugger.GetCommandInterpreter().HandleCommand(command, self.result) + output2 = self.result.GetOutput() + self.result.Clear() + self.result.write(output1) + self.result.write(output2) + + def _append_expression(self, expression): + self._append_command_output("expression " + expression) diff --git a/misc/lldb_rb/rb_heap_structs.py b/misc/lldb_rb/rb_heap_structs.py index 86b38dbbbd..798b838080 100644 --- a/misc/lldb_rb/rb_heap_structs.py +++ b/misc/lldb_rb/rb_heap_structs.py @@ -1,4 +1,5 @@ import lldb +import math from lldb_rb.lldb_interface import LLDBInterface from lldb_rb.constants import * @@ -50,8 +51,7 @@ class RbObject(LLDBInterface): self.flUser9 = self.ruby_globals["RUBY_FL_USER9"] self.flUshift = self.ruby_globals["RUBY_FL_USHIFT"] - self.tRBasic = self.target.FindFirstType("struct RBasic").GetPointerType() - self.tRValue = self.target.FindFirstType("struct RVALUE") + self.tRBasic = self.target.FindFirstType("::RBasic").GetPointerType() self.val = ptr.Cast(self.tRBasic) self.page = HeapPage(self.debugger, self.val) @@ -70,10 +70,12 @@ class RbObject(LLDBInterface): return ' ' def dump_bits(self, result, end = "\n"): - tRValue = self.target.FindFirstType("struct RVALUE") tUintPtr = self.target.FindFirstType("uintptr_t") # bits_t - num_in_page = (self.val.GetValueAsUnsigned() & HEAP_PAGE_ALIGN_MASK) // tRValue.GetByteSize(); + slot_size = self.page.to_heap_page_struct().GetChildMemberWithName("heap").GetChildMemberWithName("slot_size").unsigned + byte_size = 40 ** math.floor(math.log(slot_size, 40)) + + num_in_page = (self.val.GetValueAsUnsigned() & HEAP_PAGE_ALIGN_MASK) // byte_size; bits_bitlength = tUintPtr.GetByteSize() * 8 bitmap_index = num_in_page // bits_bitlength bitmap_offset = num_in_page & (bits_bitlength - 1) @@ -109,7 +111,14 @@ class RbObject(LLDBInterface): return False def as_type(self, type_name): - return self.val.Cast(self.tRValue.GetPointerType()).GetValueForExpressionPath("->as."+type_name) + if type_name == "array": + tRarray = self.target.FindFirstType("struct RArray") + return self.val.Cast(tRarray.GetPointerType()) + elif type_name == "bignum": + tRbignum = self.target.FindFirstType("struct RBignum") + return self.val.Cast(tRbignum.GetPointerType()) + else: + print("as_type is not implemented for:", type_name) def ary_ptr(self): rval = self.as_type("array") diff --git a/misc/lldb_rb/utils.py b/misc/lldb_rb/utils.py index a6bbd385cd..a2bcedc328 100644 --- a/misc/lldb_rb/utils.py +++ b/misc/lldb_rb/utils.py @@ -8,17 +8,6 @@ class RbInspector(LLDBInterface): self.result = result self.ruby_globals = ruby_globals - def _append_command_output(self, command): - output1 = self.result.GetOutput() - self.debugger.GetCommandInterpreter().HandleCommand(command, self.result) - output2 = self.result.GetOutput() - self.result.Clear() - self.result.write(output1) - self.result.write(output2) - - def _append_expression(self, expression): - self._append_command_output("expression " + expression) - def string2cstr(self, rstring): """Returns the pointer to the C-string in the given String object""" if rstring.TypeIsPointerType(): @@ -247,16 +236,20 @@ class RbInspector(LLDBInterface): elif rval.is_type("RUBY_T_DATA"): tRTypedData = self.target.FindFirstType("struct RTypedData").GetPointerType() val = val.Cast(tRTypedData) - flag = val.GetValueForExpressionPath("->typed_flag") - if flag.GetValueAsUnsigned() == 1: - print("T_DATA: %s" % - val.GetValueForExpressionPath("->type->wrap_struct_name"), - file=self.result) - self._append_expression("*(struct RTypedData *) %0#x" % val.GetValueAsUnsigned()) - else: - print("T_DATA:", file=self.result) - self._append_expression("*(struct RData *) %0#x" % val.GetValueAsUnsigned()) + type = val.GetValueForExpressionPath("->type").GetValueAsUnsigned() + embed = (type & 1) + if embed: + flaginfo += "[EMBED] " + type = self.frame.EvaluateExpression("(rb_data_type_t *)%0#x" % (type & ~1)) + print("T_DATA: %s%s" % + (flaginfo, type.GetValueForExpressionPath("->wrap_struct_name")), + file=self.result) + print("%s", type.Dereference(), file=self.result) + ptr = val.GetValueForExpressionPath("->data") + if embed: + ptr = ptr.AddressOf() + self._append_expression("(void *)%0#x" % ptr.GetValueAsUnsigned()) elif rval.is_type("RUBY_T_IMEMO"): imemo_type = ((rval.flags >> self.ruby_globals["RUBY_FL_USHIFT"]) @@ -290,7 +283,7 @@ class RbInspector(LLDBInterface): # if val.GetType() != tRNode: does not work for unknown reason - if val.GetType().GetPointeeType().name != "NODE": + if val.GetType().GetPointeeType().GetCanonicalType().name != "RNode": return False rbNodeTypeMask = self.ruby_globals["RUBY_NODE_TYPEMASK"] diff --git a/misc/tsan_suppressions.txt b/misc/tsan_suppressions.txt new file mode 100644 index 0000000000..5492500e7f --- /dev/null +++ b/misc/tsan_suppressions.txt @@ -0,0 +1,109 @@ +# TSan: ThreadSanitizer +# https://github.com/google/sanitizers/wiki/threadsanitizersuppressions +# +# This file describes a number of places where TSAN detects problems in CRuby. +# Many of these indicate bugs. Others are benign (ex. data races that can be +# replaced with relaxed atomic loads) +# +# Usage: +# Configure with: +# ./configure cflags='-fsanitize=thread' CC=clang +# Build and run with: +# TSAN_OPTIONS="suppressions=$(pwd)/misc/tsan_suppressions.txt:die_after_fork=0" +# +# Other useful TSAN_OPTIONS: +# * halt_on_error=1 +# * strip_path_prefix=$(pwd)/ + +# Namespaces +race_top:push_subclass_entry_to_list + +# sub_nounderflow includes non-atomic read, possibly other issue +race:objspace_malloc_increase_body + +# Signals and ubf +race:unregister_ubf_list + +# It's already crashing. We're doing our best +signal:rb_vm_bugreport +race:check_reserved_signal_ + +race_top:rb_check_deadlock + +# vm->ractor.sched.grq_cnt++ +race_top:ractor_sched_enq +race_top:ractor_sched_deq + +# Race between vm_remove_ractor writing ractor count and +# native_thread_check_and_create_shared reading it during thread creation. +# The write happens when a ractor thread exits, the read happens when +# checking if new shared threads need to be created. +race:vm_remove_ractor + +# th->sched.finished at end of co_start +race_top:rb_thread_sched_mark_zombies + +# Races against timer thread setting th->sched.waiting_reason.flags +race_top:thread_sched_wait_events + +# At thread start +race_top:rb_ractor_set_current_ec_ + +# TSan reports a lock-order-inversion between thread_sched_lock_ and this lock. +# It's unclear if that can cause a deadlock since the lock is on self +deadlock:ractor_lock_self + +# TSan reports a deadlock when reacquiring the this lock after a barrier, but +# we know the other threads have been stopped +deadlock:rb_ractor_sched_barrier_start + +# RVALUE_AGE_SET manipulates flag bits on objects which may be accessed in Ractors +race_top:RVALUE_AGE_SET + +# Inline caches and call cache updates +# Multiple threads can race when updating shared call caches during method lookups +# and argument forwarding. These races involve reading/writing cd->cc fields. +race_top:vm_cc_call_set +race_top:vm_cc_class_check +race_top:vm_search_cc +race_top:vm_search_method_slowpath0 +race_top:rb_vm_opt_getconstant_path +race_top:vm_ic_attr_index_set +race:vm_ic_update +race:vm_caller_setup_fwd_args + +# Race in shape_get_next where multiple threads simultaneously access and modify +# RCLASS_MAX_IV_COUNT and RCLASS_VARIATION_COUNT fields in class objects. +# One thread reads the field while another thread calls RCLASS_SET_MAX_IV_COUNT. +# This happens during instance variable shape transitions in multi-threaded code. +race:shape_get_next + +# Non-atomic reads/writes +race:gccct_method_search + +# Ignore exit for now +race:rb_ec_finalize +race:rb_ec_cleanup + +# TSan doesn't work well post-fork, this raises errors when creating the new +# timer thread +race:after_fork_ruby + +# Sets objspace->flags.dont_incremental while writebarrier may be running +race_top:objspace_each_exec +race_top:objspace_each_objects_ensure + +# Non-atomic lazy initialized static variable +race_top:rbimpl_intern_const + +# Setting def->aliased bitfield non-atomically +race_top:method_definition_addref + +# Switching to setting up tracing. Likely other ractors should be stopped for this. +race_top:encoded_iseq_trace_instrument +race:rb_iseq_trace_set_all +race:rb_tracepoint_enable + +# GC enable/disable flag modifications race with object allocation flag reads +race_top:rb_gc_impl_gc_disable +race_top:rb_gc_impl_gc_enable |
