diff options
Diffstat (limited to 'ext/objspace')
-rw-r--r-- | ext/objspace/depend | 86 | ||||
-rw-r--r-- | ext/objspace/lib/objspace.rb | 94 | ||||
-rw-r--r-- | ext/objspace/object_tracing.c | 167 | ||||
-rw-r--r-- | ext/objspace/objspace.c | 268 | ||||
-rw-r--r-- | ext/objspace/objspace_dump.c | 312 |
5 files changed, 554 insertions, 373 deletions
diff --git a/ext/objspace/depend b/ext/objspace/depend index c4da8031cc..a02168b06a 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -2,10 +2,12 @@ object_tracing.o: $(RUBY_EXTCONF_H) object_tracing.o: $(arch_hdrdir)/ruby/config.h object_tracing.o: $(hdrdir)/ruby/assert.h +object_tracing.o: $(hdrdir)/ruby/atomic.h object_tracing.o: $(hdrdir)/ruby/backward.h object_tracing.o: $(hdrdir)/ruby/backward/2/assume.h object_tracing.o: $(hdrdir)/ruby/backward/2/attributes.h object_tracing.o: $(hdrdir)/ruby/backward/2/bool.h +object_tracing.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h object_tracing.o: $(hdrdir)/ruby/backward/2/inttypes.h object_tracing.o: $(hdrdir)/ruby/backward/2/limits.h object_tracing.o: $(hdrdir)/ruby/backward/2/long_long.h @@ -13,6 +15,7 @@ object_tracing.o: $(hdrdir)/ruby/backward/2/stdalign.h object_tracing.o: $(hdrdir)/ruby/backward/2/stdarg.h object_tracing.o: $(hdrdir)/ruby/debug.h object_tracing.o: $(hdrdir)/ruby/defines.h +object_tracing.o: $(hdrdir)/ruby/encoding.h object_tracing.o: $(hdrdir)/ruby/intern.h object_tracing.o: $(hdrdir)/ruby/internal/abi.h object_tracing.o: $(hdrdir)/ruby/internal/anyargs.h @@ -52,6 +55,7 @@ object_tracing.o: $(hdrdir)/ruby/internal/attr/noexcept.h object_tracing.o: $(hdrdir)/ruby/internal/attr/noinline.h object_tracing.o: $(hdrdir)/ruby/internal/attr/nonnull.h object_tracing.o: $(hdrdir)/ruby/internal/attr/noreturn.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/packed_struct.h object_tracing.o: $(hdrdir)/ruby/internal/attr/pure.h object_tracing.o: $(hdrdir)/ruby/internal/attr/restrict.h object_tracing.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h @@ -84,6 +88,15 @@ object_tracing.o: $(hdrdir)/ruby/internal/core/rtypeddata.h object_tracing.o: $(hdrdir)/ruby/internal/ctype.h object_tracing.o: $(hdrdir)/ruby/internal/dllexport.h object_tracing.o: $(hdrdir)/ruby/internal/dosish.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/coderange.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/ctype.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/encoding.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/pathname.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/re.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/string.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/symbol.h +object_tracing.o: $(hdrdir)/ruby/internal/encoding/transcode.h object_tracing.o: $(hdrdir)/ruby/internal/error.h object_tracing.o: $(hdrdir)/ruby/internal/eval.h object_tracing.o: $(hdrdir)/ruby/internal/event.h @@ -111,7 +124,6 @@ object_tracing.o: $(hdrdir)/ruby/internal/intern/enumerator.h object_tracing.o: $(hdrdir)/ruby/internal/intern/error.h object_tracing.o: $(hdrdir)/ruby/internal/intern/eval.h object_tracing.o: $(hdrdir)/ruby/internal/intern/file.h -object_tracing.o: $(hdrdir)/ruby/internal/intern/gc.h object_tracing.o: $(hdrdir)/ruby/internal/intern/hash.h object_tracing.o: $(hdrdir)/ruby/internal/intern/io.h object_tracing.o: $(hdrdir)/ruby/internal/intern/load.h @@ -142,12 +154,12 @@ object_tracing.o: $(hdrdir)/ruby/internal/memory.h object_tracing.o: $(hdrdir)/ruby/internal/method.h object_tracing.o: $(hdrdir)/ruby/internal/module.h object_tracing.o: $(hdrdir)/ruby/internal/newobj.h -object_tracing.o: $(hdrdir)/ruby/internal/rgengc.h object_tracing.o: $(hdrdir)/ruby/internal/scan_args.h object_tracing.o: $(hdrdir)/ruby/internal/special_consts.h object_tracing.o: $(hdrdir)/ruby/internal/static_assert.h object_tracing.o: $(hdrdir)/ruby/internal/stdalign.h object_tracing.o: $(hdrdir)/ruby/internal/stdbool.h +object_tracing.o: $(hdrdir)/ruby/internal/stdckdint.h object_tracing.o: $(hdrdir)/ruby/internal/symbol.h object_tracing.o: $(hdrdir)/ruby/internal/value.h object_tracing.o: $(hdrdir)/ruby/internal/value_type.h @@ -155,15 +167,42 @@ object_tracing.o: $(hdrdir)/ruby/internal/variable.h object_tracing.o: $(hdrdir)/ruby/internal/warning_push.h object_tracing.o: $(hdrdir)/ruby/internal/xmalloc.h object_tracing.o: $(hdrdir)/ruby/missing.h +object_tracing.o: $(hdrdir)/ruby/onigmo.h +object_tracing.o: $(hdrdir)/ruby/oniguruma.h object_tracing.o: $(hdrdir)/ruby/ruby.h object_tracing.o: $(hdrdir)/ruby/st.h object_tracing.o: $(hdrdir)/ruby/subst.h +object_tracing.o: $(hdrdir)/ruby/thread_native.h +object_tracing.o: $(top_srcdir)/ccan/check_type/check_type.h +object_tracing.o: $(top_srcdir)/ccan/container_of/container_of.h +object_tracing.o: $(top_srcdir)/ccan/list/list.h +object_tracing.o: $(top_srcdir)/ccan/str/str.h object_tracing.o: $(top_srcdir)/internal.h +object_tracing.o: $(top_srcdir)/internal/array.h +object_tracing.o: $(top_srcdir)/internal/basic_operators.h +object_tracing.o: $(top_srcdir)/internal/compilers.h +object_tracing.o: $(top_srcdir)/internal/gc.h +object_tracing.o: $(top_srcdir)/internal/imemo.h +object_tracing.o: $(top_srcdir)/internal/sanitizers.h +object_tracing.o: $(top_srcdir)/internal/serial.h +object_tracing.o: $(top_srcdir)/internal/static_assert.h +object_tracing.o: $(top_srcdir)/internal/vm.h +object_tracing.o: $(top_srcdir)/internal/warnings.h +object_tracing.o: $(top_srcdir)/method.h +object_tracing.o: $(top_srcdir)/node.h +object_tracing.o: $(top_srcdir)/ruby_assert.h +object_tracing.o: $(top_srcdir)/ruby_atomic.h +object_tracing.o: $(top_srcdir)/rubyparser.h +object_tracing.o: $(top_srcdir)/thread_pthread.h +object_tracing.o: $(top_srcdir)/vm_core.h +object_tracing.o: $(top_srcdir)/vm_opts.h object_tracing.o: object_tracing.c object_tracing.o: objspace.h +object_tracing.o: {$(VPATH)}id.h objspace.o: $(RUBY_EXTCONF_H) objspace.o: $(arch_hdrdir)/ruby/config.h objspace.o: $(hdrdir)/ruby/assert.h +objspace.o: $(hdrdir)/ruby/atomic.h objspace.o: $(hdrdir)/ruby/backward.h objspace.o: $(hdrdir)/ruby/backward/2/assume.h objspace.o: $(hdrdir)/ruby/backward/2/attributes.h @@ -215,6 +254,7 @@ objspace.o: $(hdrdir)/ruby/internal/attr/noexcept.h objspace.o: $(hdrdir)/ruby/internal/attr/noinline.h objspace.o: $(hdrdir)/ruby/internal/attr/nonnull.h objspace.o: $(hdrdir)/ruby/internal/attr/noreturn.h +objspace.o: $(hdrdir)/ruby/internal/attr/packed_struct.h objspace.o: $(hdrdir)/ruby/internal/attr/pure.h objspace.o: $(hdrdir)/ruby/internal/attr/restrict.h objspace.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h @@ -284,7 +324,6 @@ objspace.o: $(hdrdir)/ruby/internal/intern/enumerator.h objspace.o: $(hdrdir)/ruby/internal/intern/error.h objspace.o: $(hdrdir)/ruby/internal/intern/eval.h objspace.o: $(hdrdir)/ruby/internal/intern/file.h -objspace.o: $(hdrdir)/ruby/internal/intern/gc.h objspace.o: $(hdrdir)/ruby/internal/intern/hash.h objspace.o: $(hdrdir)/ruby/internal/intern/io.h objspace.o: $(hdrdir)/ruby/internal/intern/load.h @@ -315,12 +354,12 @@ objspace.o: $(hdrdir)/ruby/internal/memory.h objspace.o: $(hdrdir)/ruby/internal/method.h objspace.o: $(hdrdir)/ruby/internal/module.h objspace.o: $(hdrdir)/ruby/internal/newobj.h -objspace.o: $(hdrdir)/ruby/internal/rgengc.h objspace.o: $(hdrdir)/ruby/internal/scan_args.h objspace.o: $(hdrdir)/ruby/internal/special_consts.h objspace.o: $(hdrdir)/ruby/internal/static_assert.h objspace.o: $(hdrdir)/ruby/internal/stdalign.h objspace.o: $(hdrdir)/ruby/internal/stdbool.h +objspace.o: $(hdrdir)/ruby/internal/stdckdint.h objspace.o: $(hdrdir)/ruby/internal/symbol.h objspace.o: $(hdrdir)/ruby/internal/value.h objspace.o: $(hdrdir)/ruby/internal/value_type.h @@ -336,10 +375,17 @@ objspace.o: $(hdrdir)/ruby/regex.h objspace.o: $(hdrdir)/ruby/ruby.h objspace.o: $(hdrdir)/ruby/st.h objspace.o: $(hdrdir)/ruby/subst.h -objspace.o: $(top_srcdir)/gc.h +objspace.o: $(hdrdir)/ruby/thread_native.h +objspace.o: $(top_srcdir)/ccan/check_type/check_type.h +objspace.o: $(top_srcdir)/ccan/container_of/container_of.h +objspace.o: $(top_srcdir)/ccan/list/list.h +objspace.o: $(top_srcdir)/ccan/str/str.h +objspace.o: $(top_srcdir)/constant.h +objspace.o: $(top_srcdir)/debug_counter.h objspace.o: $(top_srcdir)/id_table.h objspace.o: $(top_srcdir)/internal.h objspace.o: $(top_srcdir)/internal/array.h +objspace.o: $(top_srcdir)/internal/basic_operators.h objspace.o: $(top_srcdir)/internal/class.h objspace.o: $(top_srcdir)/internal/compilers.h objspace.o: $(top_srcdir)/internal/gc.h @@ -348,9 +394,21 @@ objspace.o: $(top_srcdir)/internal/imemo.h objspace.o: $(top_srcdir)/internal/sanitizers.h objspace.o: $(top_srcdir)/internal/serial.h objspace.o: $(top_srcdir)/internal/static_assert.h +objspace.o: $(top_srcdir)/internal/variable.h +objspace.o: $(top_srcdir)/internal/vm.h objspace.o: $(top_srcdir)/internal/warnings.h +objspace.o: $(top_srcdir)/method.h objspace.o: $(top_srcdir)/node.h +objspace.o: $(top_srcdir)/ruby_assert.h +objspace.o: $(top_srcdir)/ruby_atomic.h +objspace.o: $(top_srcdir)/rubyparser.h +objspace.o: $(top_srcdir)/shape.h objspace.o: $(top_srcdir)/symbol.h +objspace.o: $(top_srcdir)/thread_pthread.h +objspace.o: $(top_srcdir)/vm_core.h +objspace.o: $(top_srcdir)/vm_debug.h +objspace.o: $(top_srcdir)/vm_opts.h +objspace.o: $(top_srcdir)/vm_sync.h objspace.o: objspace.c objspace.o: {$(VPATH)}id.h objspace_dump.o: $(RUBY_EXTCONF_H) @@ -409,6 +467,7 @@ objspace_dump.o: $(hdrdir)/ruby/internal/attr/noexcept.h objspace_dump.o: $(hdrdir)/ruby/internal/attr/noinline.h objspace_dump.o: $(hdrdir)/ruby/internal/attr/nonnull.h objspace_dump.o: $(hdrdir)/ruby/internal/attr/noreturn.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/packed_struct.h objspace_dump.o: $(hdrdir)/ruby/internal/attr/pure.h objspace_dump.o: $(hdrdir)/ruby/internal/attr/restrict.h objspace_dump.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h @@ -477,7 +536,6 @@ objspace_dump.o: $(hdrdir)/ruby/internal/intern/enumerator.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/error.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/eval.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/file.h -objspace_dump.o: $(hdrdir)/ruby/internal/intern/gc.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/hash.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/io.h objspace_dump.o: $(hdrdir)/ruby/internal/intern/load.h @@ -508,12 +566,12 @@ objspace_dump.o: $(hdrdir)/ruby/internal/memory.h objspace_dump.o: $(hdrdir)/ruby/internal/method.h objspace_dump.o: $(hdrdir)/ruby/internal/module.h objspace_dump.o: $(hdrdir)/ruby/internal/newobj.h -objspace_dump.o: $(hdrdir)/ruby/internal/rgengc.h objspace_dump.o: $(hdrdir)/ruby/internal/scan_args.h objspace_dump.o: $(hdrdir)/ruby/internal/special_consts.h objspace_dump.o: $(hdrdir)/ruby/internal/static_assert.h objspace_dump.o: $(hdrdir)/ruby/internal/stdalign.h objspace_dump.o: $(hdrdir)/ruby/internal/stdbool.h +objspace_dump.o: $(hdrdir)/ruby/internal/stdckdint.h objspace_dump.o: $(hdrdir)/ruby/internal/symbol.h objspace_dump.o: $(hdrdir)/ruby/internal/value.h objspace_dump.o: $(hdrdir)/ruby/internal/value_type.h @@ -533,26 +591,38 @@ objspace_dump.o: $(top_srcdir)/ccan/check_type/check_type.h objspace_dump.o: $(top_srcdir)/ccan/container_of/container_of.h objspace_dump.o: $(top_srcdir)/ccan/list/list.h objspace_dump.o: $(top_srcdir)/ccan/str/str.h -objspace_dump.o: $(top_srcdir)/gc.h +objspace_dump.o: $(top_srcdir)/constant.h +objspace_dump.o: $(top_srcdir)/debug_counter.h +objspace_dump.o: $(top_srcdir)/id_table.h objspace_dump.o: $(top_srcdir)/internal.h objspace_dump.o: $(top_srcdir)/internal/array.h +objspace_dump.o: $(top_srcdir)/internal/basic_operators.h +objspace_dump.o: $(top_srcdir)/internal/class.h objspace_dump.o: $(top_srcdir)/internal/compilers.h objspace_dump.o: $(top_srcdir)/internal/gc.h objspace_dump.o: $(top_srcdir)/internal/hash.h objspace_dump.o: $(top_srcdir)/internal/imemo.h +objspace_dump.o: $(top_srcdir)/internal/io.h objspace_dump.o: $(top_srcdir)/internal/sanitizers.h objspace_dump.o: $(top_srcdir)/internal/serial.h objspace_dump.o: $(top_srcdir)/internal/static_assert.h objspace_dump.o: $(top_srcdir)/internal/string.h +objspace_dump.o: $(top_srcdir)/internal/variable.h objspace_dump.o: $(top_srcdir)/internal/vm.h objspace_dump.o: $(top_srcdir)/internal/warnings.h objspace_dump.o: $(top_srcdir)/method.h objspace_dump.o: $(top_srcdir)/node.h objspace_dump.o: $(top_srcdir)/ruby_assert.h objspace_dump.o: $(top_srcdir)/ruby_atomic.h +objspace_dump.o: $(top_srcdir)/rubyparser.h +objspace_dump.o: $(top_srcdir)/shape.h +objspace_dump.o: $(top_srcdir)/symbol.h objspace_dump.o: $(top_srcdir)/thread_pthread.h +objspace_dump.o: $(top_srcdir)/vm_callinfo.h objspace_dump.o: $(top_srcdir)/vm_core.h +objspace_dump.o: $(top_srcdir)/vm_debug.h objspace_dump.o: $(top_srcdir)/vm_opts.h +objspace_dump.o: $(top_srcdir)/vm_sync.h objspace_dump.o: objspace.h objspace_dump.o: objspace_dump.c objspace_dump.o: {$(VPATH)}id.h diff --git a/ext/objspace/lib/objspace.rb b/ext/objspace/lib/objspace.rb index 0298b0646c..47873f5112 100644 --- a/ext/objspace/lib/objspace.rb +++ b/ext/objspace/lib/objspace.rb @@ -6,17 +6,21 @@ module ObjectSpace class << self private :_dump private :_dump_all + private :_dump_shapes end module_function - # call-seq: - # ObjectSpace.dump(obj[, output: :string]) # => "{ ... }" - # ObjectSpace.dump(obj, output: :file) # => #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json> - # ObjectSpace.dump(obj, output: :stdout) # => nil - # # Dump the contents of a ruby object as JSON. # + # _output_ can be one of: +:stdout+, +:file+, +:string+, or IO object. + # + # * +:file+ means dumping to a tempfile and returning corresponding File object; + # * +:stdout+ means printing the dump and returning +nil+; + # * +:string+ means returning a string with the dump; + # * if an instance of IO object is provided, the output goes there, and the object + # is returned. + # # This method is only expected to work with C Ruby. # This is an experimental method and is subject to change. # In particular, the function signature and output format are @@ -42,38 +46,78 @@ module ObjectSpace end - # call-seq: - # ObjectSpace.dump_all([output: :file]) # => #<File:/tmp/rubyheap20131125-88469-laoj3v.json> - # ObjectSpace.dump_all(output: :stdout) # => nil - # ObjectSpace.dump_all(output: :string) # => "{...}\n{...}\n..." - # ObjectSpace.dump_all(output: - # File.open('heap.json','w')) # => #<File:heap.json> - # ObjectSpace.dump_all(output: :string, - # since: 42) # => "{...}\n{...}\n..." + # Dump the contents of the ruby heap as JSON. + # + # _output_ argument is the same as for #dump. + # + # _full_ must be a boolean. If true, all heap slots are dumped including the empty ones (+T_NONE+). # - # Dump the contents of the ruby heap as JSON. + # _since_ must be a non-negative integer or +nil+. # - # _since_ must be a non-negative integer or +nil+. + # If _since_ is a positive integer, only objects of that generation and + # newer generations are dumped. The current generation can be accessed using + # GC::count. Objects that were allocated without object allocation tracing enabled + # are ignored. See ::trace_object_allocations for more information and + # examples. # - # If _since_ is a positive integer, only objects of that generation and - # newer generations are dumped. The current generation can be accessed using - # GC::count. + # If _since_ is omitted or is +nil+, all objects are dumped. # - # Objects that were allocated without object allocation tracing enabled - # are ignored. See ::trace_object_allocations for more information and - # examples. + # _shapes_ must be a boolean or a non-negative integer. # - # If _since_ is omitted or is +nil+, all objects are dumped. + # If _shapes_ is a positive integer, only shapes newer than the provided + # shape id are dumped. The current shape_id can be accessed using <tt>RubyVM.stat(:next_shape_id)</tt>. + # + # If _shapes_ is +false+, no shapes are dumped. + # + # To only dump objects allocated past a certain point you can combine _since_ and _shapes_: + # ObjectSpace.trace_object_allocations + # GC.start + # gc_generation = GC.count + # shape_generation = RubyVM.stat(:next_shape_id) + # call_method_to_instrument + # ObjectSpace.dump_all(since: gc_generation, shapes: shape_generation) + # + # This method is only expected to work with C Ruby. + # This is an experimental method and is subject to change. + # In particular, the function signature and output format are + # not guaranteed to be compatible in future versions of ruby. + def dump_all(output: :file, full: false, since: nil, shapes: true) + out = case output + when :file, nil + require 'tempfile' + Tempfile.create(%w(rubyheap .json)) + when :stdout + STDOUT + when :string + +'' + when IO + output + else + raise ArgumentError, "wrong output option: #{output.inspect}" + end + + shapes = 0 if shapes == true + ret = _dump_all(out, full, since, shapes) + return nil if output == :stdout + ret + end + + # Dump the contents of the ruby shape tree as JSON. + # + # _output_ argument is the same as for #dump. + # + # If _since_ is a positive integer, only shapes newer than the provided + # shape id are dumped. The current shape_id can be accessed using <tt>RubyVM.stat(:next_shape_id)</tt>. # # This method is only expected to work with C Ruby. # This is an experimental method and is subject to change. # In particular, the function signature and output format are # not guaranteed to be compatible in future versions of ruby. - def dump_all(output: :file, full: false, since: nil) + def dump_shapes(output: :file, since: 0) out = case output when :file, nil require 'tempfile' - Tempfile.create(%w(rubyheap .json)) + Tempfile.create(%w(rubyshapes .json)) when :stdout STDOUT when :string @@ -84,7 +128,7 @@ module ObjectSpace raise ArgumentError, "wrong output option: #{output.inspect}" end - ret = _dump_all(out, full, since) + ret = _dump_shapes(out, since) return nil if output == :stdout ret end diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c index 66d6baa491..c1c93c51f5 100644 --- a/ext/objspace/object_tracing.c +++ b/ext/objspace/object_tracing.c @@ -14,6 +14,7 @@ **********************************************************************/ #include "internal.h" +#include "internal/gc.h" #include "ruby/debug.h" #include "objspace.h" @@ -31,24 +32,24 @@ static const char * make_unique_str(st_table *tbl, const char *str, long len) { if (!str) { - return NULL; + return NULL; } else { - st_data_t n; - char *result; - - if (st_lookup(tbl, (st_data_t)str, &n)) { - st_insert(tbl, (st_data_t)str, n+1); - st_get_key(tbl, (st_data_t)str, &n); - result = (char *)n; - } - else { - result = (char *)ruby_xmalloc(len+1); - strncpy(result, str, len); - result[len] = 0; - st_add_direct(tbl, (st_data_t)result, 1); - } - return result; + st_data_t n; + char *result; + + if (st_lookup(tbl, (st_data_t)str, &n)) { + st_insert(tbl, (st_data_t)str, n+1); + st_get_key(tbl, (st_data_t)str, &n); + result = (char *)n; + } + else { + result = (char *)ruby_xmalloc(len+1); + strncpy(result, str, len); + result[len] = 0; + st_add_direct(tbl, (st_data_t)result, 1); + } + return result; } } @@ -56,17 +57,17 @@ static void delete_unique_str(st_table *tbl, const char *str) { if (str) { - st_data_t n; - - st_lookup(tbl, (st_data_t)str, &n); - if (n == 1) { - n = (st_data_t)str; - st_delete(tbl, &n, 0); - ruby_xfree((char *)n); - } - else { - st_insert(tbl, (st_data_t)str, n-1); - } + st_data_t n; + + st_lookup(tbl, (st_data_t)str, &n); + if (n == 1) { + n = (st_data_t)str; + st_delete(tbl, &n, 0); + ruby_xfree((char *)n); + } + else { + st_insert(tbl, (st_data_t)str, n-1); + } } } @@ -87,18 +88,18 @@ newobj_i(VALUE tpval, void *data) st_data_t v; if (st_lookup(arg->object_table, (st_data_t)obj, &v)) { - info = (struct allocation_info *)v; - if (arg->keep_remains) { - if (info->living) { - /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */ - } - } - /* reuse info */ - delete_unique_str(arg->str_table, info->path); - delete_unique_str(arg->str_table, info->class_path); + info = (struct allocation_info *)v; + if (arg->keep_remains) { + if (info->living) { + /* do nothing. there is possibility to keep living if FREEOBJ events while suppressing tracing */ + } + } + /* reuse info */ + delete_unique_str(arg->str_table, info->path); + delete_unique_str(arg->str_table, info->class_path); } else { - info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info)); + info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info)); } info->living = 1; info->flags = RBASIC(obj)->flags; @@ -121,20 +122,26 @@ freeobj_i(VALUE tpval, void *data) st_data_t v; struct allocation_info *info; + /* Modifying the st table can cause allocations, which can trigger GC. + * Since freeobj_i is called during GC, it must not trigger another GC. */ + VALUE gc_disabled = rb_gc_disable_no_rest(); + if (arg->keep_remains) { - if (st_lookup(arg->object_table, obj, &v)) { - info = (struct allocation_info *)v; - info->living = 0; - } + if (st_lookup(arg->object_table, obj, &v)) { + info = (struct allocation_info *)v; + info->living = 0; + } } else { - if (st_delete(arg->object_table, &obj, &v)) { - info = (struct allocation_info *)v; - delete_unique_str(arg->str_table, info->path); - delete_unique_str(arg->str_table, info->class_path); - ruby_xfree(info); - } + if (st_delete(arg->object_table, &obj, &v)) { + info = (struct allocation_info *)v; + delete_unique_str(arg->str_table, info->path); + delete_unique_str(arg->str_table, info->class_path); + ruby_xfree(info); + } } + + if (gc_disabled == Qfalse) rb_gc_enable(); } static int @@ -236,12 +243,12 @@ get_traceobj_arg(void) VALUE obj = TypedData_Make_Struct(rb_cObject, struct traceobj_arg, &allocation_info_tracer_type, tmp_trace_arg); traceobj_arg = obj; rb_gc_register_mark_object(traceobj_arg); - tmp_trace_arg->running = 0; - tmp_trace_arg->keep_remains = tmp_keep_remains; - tmp_trace_arg->newobj_trace = 0; - tmp_trace_arg->freeobj_trace = 0; - tmp_trace_arg->object_table = st_init_numtable(); - tmp_trace_arg->str_table = st_init_strtable(); + tmp_trace_arg->running = 0; + tmp_trace_arg->keep_remains = tmp_keep_remains; + tmp_trace_arg->newobj_trace = 0; + tmp_trace_arg->freeobj_trace = 0; + tmp_trace_arg->object_table = st_init_numtable(); + tmp_trace_arg->str_table = st_init_strtable(); } return tmp_trace_arg; } @@ -258,15 +265,15 @@ trace_object_allocations_start(VALUE self) struct traceobj_arg *arg = get_traceobj_arg(); if (arg->running++ > 0) { - /* do nothing */ + /* do nothing */ } else { - if (arg->newobj_trace == 0) { - arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg); - arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg); - } - rb_tracepoint_enable(arg->newobj_trace); - rb_tracepoint_enable(arg->freeobj_trace); + if (arg->newobj_trace == 0) { + arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg); + arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg); + } + rb_tracepoint_enable(arg->newobj_trace); + rb_tracepoint_enable(arg->freeobj_trace); } return Qnil; @@ -287,7 +294,7 @@ trace_object_allocations_stop(VALUE self) struct traceobj_arg *arg = get_traceobj_arg(); if (arg->running > 0) { - arg->running--; + arg->running--; } if (arg->running == 0) { @@ -374,8 +381,8 @@ object_allocations_reporter_i(st_data_t key, st_data_t val, st_data_t ptr) else fprintf(out, "C: %p", (void *)info->klass); fprintf(out, "@%s:%lu", info->path ? info->path : "", info->line); if (!NIL_P(info->mid)) { - VALUE m = rb_sym2str(info->mid); - fprintf(out, " (%s)", RSTRING_PTR(m)); + VALUE m = rb_sym2str(info->mid); + fprintf(out, " (%s)", RSTRING_PTR(m)); } fprintf(out, ")\n"); @@ -387,7 +394,7 @@ object_allocations_reporter(FILE *out, void *ptr) { fprintf(out, "== object_allocations_reporter: START\n"); if (tmp_trace_arg) { - st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out); + st_foreach(tmp_trace_arg->object_table, object_allocations_reporter_i, (st_data_t)out); } fprintf(out, "== object_allocations_reporter: END\n"); } @@ -397,8 +404,8 @@ trace_object_allocations_debug_start(VALUE self) { tmp_keep_remains = 1; if (object_allocations_reporter_registered == 0) { - object_allocations_reporter_registered = 1; - rb_bug_reporter_add(object_allocations_reporter, 0); + object_allocations_reporter_registered = 1; + rb_bug_reporter_add(object_allocations_reporter, 0); } return trace_object_allocations_start(self); @@ -408,10 +415,10 @@ static struct allocation_info * lookup_allocation_info(VALUE obj) { if (tmp_trace_arg) { - st_data_t info; - if (st_lookup(tmp_trace_arg->object_table, obj, &info)) { - return (struct allocation_info *)info; - } + st_data_t info; + if (st_lookup(tmp_trace_arg->object_table, obj, &info)) { + return (struct allocation_info *)info; + } } return NULL; } @@ -435,10 +442,10 @@ allocation_sourcefile(VALUE self, VALUE obj) struct allocation_info *info = lookup_allocation_info(obj); if (info && info->path) { - return rb_str_new2(info->path); + return rb_str_new2(info->path); } else { - return Qnil; + return Qnil; } } @@ -455,10 +462,10 @@ allocation_sourceline(VALUE self, VALUE obj) struct allocation_info *info = lookup_allocation_info(obj); if (info) { - return INT2FIX(info->line); + return INT2FIX(info->line); } else { - return Qnil; + return Qnil; } } @@ -486,10 +493,10 @@ allocation_class_path(VALUE self, VALUE obj) struct allocation_info *info = lookup_allocation_info(obj); if (info && info->class_path) { - return rb_str_new2(info->class_path); + return rb_str_new2(info->class_path); } else { - return Qnil; + return Qnil; } } @@ -518,10 +525,10 @@ allocation_method_id(VALUE self, VALUE obj) { struct allocation_info *info = lookup_allocation_info(obj); if (info) { - return info->mid; + return info->mid; } else { - return Qnil; + return Qnil; } } @@ -550,10 +557,10 @@ allocation_generation(VALUE self, VALUE obj) { struct allocation_info *info = lookup_allocation_info(obj); if (info) { - return SIZET2NUM(info->generation); + return SIZET2NUM(info->generation); } else { - return Qnil; + return Qnil; } } diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 9cc66bcfe8..24d7bd419f 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -12,14 +12,13 @@ **********************************************************************/ -#include "gc.h" #include "internal.h" #include "internal/class.h" #include "internal/compilers.h" +#include "internal/gc.h" #include "internal/hash.h" #include "internal/imemo.h" #include "internal/sanitizers.h" -#include "node.h" #include "ruby/io.h" #include "ruby/re.h" #include "ruby/st.h" @@ -62,9 +61,9 @@ total_i(VALUE v, void *ptr) struct total_data *data = (struct total_data *)ptr; if (!rb_objspace_internal_object_p(v)) { - if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) { - data->total += rb_obj_memsize_of(v); - } + if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) { + data->total += rb_obj_memsize_of(v); + } } } @@ -140,7 +139,7 @@ memsize_of_all_m(int argc, VALUE *argv, VALUE self) struct total_data data = {0, 0}; if (argc > 0) { - rb_scan_args(argc, argv, "01", &data.klass); + rb_scan_args(argc, argv, "01", &data.klass); } each_object_with_flags(total_i, &data); @@ -170,8 +169,7 @@ setup_hash(int argc, VALUE *argv) hash = rb_hash_new(); } else if (!RHASH_EMPTY_P(hash)) { - /* WB: no new reference */ - st_foreach(RHASH_TBL_RAW(hash), set_zero_i, hash); + rb_hash_foreach(hash, set_zero_i, (st_data_t)hash); } return hash; @@ -190,33 +188,33 @@ type2sym(enum ruby_value_type i) VALUE type; switch (i) { #define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break; - CASE_TYPE(T_NONE); - CASE_TYPE(T_OBJECT); - CASE_TYPE(T_CLASS); - CASE_TYPE(T_MODULE); - CASE_TYPE(T_FLOAT); - CASE_TYPE(T_STRING); - CASE_TYPE(T_REGEXP); - CASE_TYPE(T_ARRAY); - CASE_TYPE(T_HASH); - CASE_TYPE(T_STRUCT); - CASE_TYPE(T_BIGNUM); - CASE_TYPE(T_FILE); - CASE_TYPE(T_DATA); - CASE_TYPE(T_MATCH); - CASE_TYPE(T_COMPLEX); - CASE_TYPE(T_RATIONAL); - CASE_TYPE(T_NIL); - CASE_TYPE(T_TRUE); - CASE_TYPE(T_FALSE); - CASE_TYPE(T_SYMBOL); - CASE_TYPE(T_FIXNUM); - CASE_TYPE(T_UNDEF); - CASE_TYPE(T_IMEMO); - CASE_TYPE(T_NODE); - CASE_TYPE(T_ICLASS); + CASE_TYPE(T_NONE); + CASE_TYPE(T_OBJECT); + CASE_TYPE(T_CLASS); + CASE_TYPE(T_MODULE); + CASE_TYPE(T_FLOAT); + CASE_TYPE(T_STRING); + CASE_TYPE(T_REGEXP); + CASE_TYPE(T_ARRAY); + CASE_TYPE(T_HASH); + CASE_TYPE(T_STRUCT); + CASE_TYPE(T_BIGNUM); + CASE_TYPE(T_FILE); + CASE_TYPE(T_DATA); + CASE_TYPE(T_MATCH); + CASE_TYPE(T_COMPLEX); + CASE_TYPE(T_RATIONAL); + CASE_TYPE(T_NIL); + CASE_TYPE(T_TRUE); + CASE_TYPE(T_FALSE); + CASE_TYPE(T_SYMBOL); + CASE_TYPE(T_FIXNUM); + CASE_TYPE(T_UNDEF); + CASE_TYPE(T_IMEMO); + CASE_TYPE(T_NODE); + CASE_TYPE(T_ICLASS); CASE_TYPE(T_MOVED); - CASE_TYPE(T_ZOMBIE); + CASE_TYPE(T_ZOMBIE); #undef CASE_TYPE default: rb_bug("type2sym: unknown type (%d)", i); } @@ -255,17 +253,17 @@ count_objects_size(int argc, VALUE *argv, VALUE os) VALUE hash = setup_hash(argc, argv); for (i = 0; i <= T_MASK; i++) { - counts[i] = 0; + counts[i] = 0; } each_object_with_flags(cos_i, &counts[0]); for (i = 0; i <= T_MASK; i++) { - if (counts[i]) { - VALUE type = type2sym(i); - total += counts[i]; - rb_hash_aset(hash, type, SIZET2NUM(counts[i])); - } + if (counts[i]) { + VALUE type = type2sym(i); + total += counts[i]; + rb_hash_aset(hash, type, SIZET2NUM(counts[i])); + } } rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total)); return hash; @@ -337,17 +335,6 @@ count_symbols(int argc, VALUE *argv, VALUE os) return hash; } -static void -cn_i(VALUE v, void *n) -{ - size_t *nodes = (size_t *)n; - - if (BUILTIN_TYPE(v) == T_NODE) { - size_t s = nd_type((NODE *)v); - nodes[s]++; - } -} - /* * call-seq: * ObjectSpace.count_nodes([result_hash]) -> hash @@ -374,134 +361,7 @@ cn_i(VALUE v, void *n) static VALUE count_nodes(int argc, VALUE *argv, VALUE os) { - size_t nodes[NODE_LAST+1]; - enum node_type i; - VALUE hash = setup_hash(argc, argv); - - for (i = 0; i <= NODE_LAST; i++) { - nodes[i] = 0; - } - - each_object_with_flags(cn_i, &nodes[0]); - - for (i=0; i<NODE_LAST; i++) { - if (nodes[i] != 0) { - VALUE node; - switch (i) { -#define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); goto set - COUNT_NODE(NODE_SCOPE); - COUNT_NODE(NODE_BLOCK); - COUNT_NODE(NODE_IF); - COUNT_NODE(NODE_UNLESS); - COUNT_NODE(NODE_CASE); - COUNT_NODE(NODE_CASE2); - COUNT_NODE(NODE_CASE3); - COUNT_NODE(NODE_WHEN); - COUNT_NODE(NODE_IN); - COUNT_NODE(NODE_WHILE); - COUNT_NODE(NODE_UNTIL); - COUNT_NODE(NODE_ITER); - COUNT_NODE(NODE_FOR); - COUNT_NODE(NODE_FOR_MASGN); - COUNT_NODE(NODE_BREAK); - COUNT_NODE(NODE_NEXT); - COUNT_NODE(NODE_REDO); - COUNT_NODE(NODE_RETRY); - COUNT_NODE(NODE_BEGIN); - COUNT_NODE(NODE_RESCUE); - COUNT_NODE(NODE_RESBODY); - COUNT_NODE(NODE_ENSURE); - COUNT_NODE(NODE_AND); - COUNT_NODE(NODE_OR); - COUNT_NODE(NODE_MASGN); - COUNT_NODE(NODE_LASGN); - COUNT_NODE(NODE_DASGN); - COUNT_NODE(NODE_GASGN); - COUNT_NODE(NODE_IASGN); - COUNT_NODE(NODE_CDECL); - COUNT_NODE(NODE_CVASGN); - COUNT_NODE(NODE_OP_ASGN1); - COUNT_NODE(NODE_OP_ASGN2); - COUNT_NODE(NODE_OP_ASGN_AND); - COUNT_NODE(NODE_OP_ASGN_OR); - COUNT_NODE(NODE_OP_CDECL); - COUNT_NODE(NODE_CALL); - COUNT_NODE(NODE_OPCALL); - COUNT_NODE(NODE_FCALL); - COUNT_NODE(NODE_VCALL); - COUNT_NODE(NODE_QCALL); - COUNT_NODE(NODE_SUPER); - COUNT_NODE(NODE_ZSUPER); - COUNT_NODE(NODE_LIST); - COUNT_NODE(NODE_ZLIST); - COUNT_NODE(NODE_VALUES); - COUNT_NODE(NODE_HASH); - COUNT_NODE(NODE_RETURN); - COUNT_NODE(NODE_YIELD); - COUNT_NODE(NODE_LVAR); - COUNT_NODE(NODE_DVAR); - COUNT_NODE(NODE_GVAR); - COUNT_NODE(NODE_IVAR); - COUNT_NODE(NODE_CONST); - COUNT_NODE(NODE_CVAR); - COUNT_NODE(NODE_NTH_REF); - COUNT_NODE(NODE_BACK_REF); - COUNT_NODE(NODE_MATCH); - COUNT_NODE(NODE_MATCH2); - COUNT_NODE(NODE_MATCH3); - COUNT_NODE(NODE_LIT); - COUNT_NODE(NODE_STR); - COUNT_NODE(NODE_DSTR); - COUNT_NODE(NODE_XSTR); - COUNT_NODE(NODE_DXSTR); - COUNT_NODE(NODE_EVSTR); - COUNT_NODE(NODE_DREGX); - COUNT_NODE(NODE_ONCE); - COUNT_NODE(NODE_ARGS); - COUNT_NODE(NODE_ARGS_AUX); - COUNT_NODE(NODE_OPT_ARG); - COUNT_NODE(NODE_KW_ARG); - COUNT_NODE(NODE_POSTARG); - COUNT_NODE(NODE_ARGSCAT); - COUNT_NODE(NODE_ARGSPUSH); - COUNT_NODE(NODE_SPLAT); - COUNT_NODE(NODE_BLOCK_PASS); - COUNT_NODE(NODE_DEFN); - COUNT_NODE(NODE_DEFS); - COUNT_NODE(NODE_ALIAS); - COUNT_NODE(NODE_VALIAS); - COUNT_NODE(NODE_UNDEF); - COUNT_NODE(NODE_CLASS); - COUNT_NODE(NODE_MODULE); - COUNT_NODE(NODE_SCLASS); - COUNT_NODE(NODE_COLON2); - COUNT_NODE(NODE_COLON3); - COUNT_NODE(NODE_DOT2); - COUNT_NODE(NODE_DOT3); - COUNT_NODE(NODE_FLIP2); - COUNT_NODE(NODE_FLIP3); - COUNT_NODE(NODE_SELF); - COUNT_NODE(NODE_NIL); - COUNT_NODE(NODE_TRUE); - COUNT_NODE(NODE_FALSE); - COUNT_NODE(NODE_ERRINFO); - COUNT_NODE(NODE_DEFINED); - COUNT_NODE(NODE_POSTEXE); - COUNT_NODE(NODE_DSYM); - COUNT_NODE(NODE_ATTRASGN); - COUNT_NODE(NODE_LAMBDA); - COUNT_NODE(NODE_ARYPTN); - COUNT_NODE(NODE_FNDPTN); - COUNT_NODE(NODE_HSHPTN); -#undef COUNT_NODE - case NODE_LAST: break; - } - UNREACHABLE; - set: - rb_hash_aset(hash, node, SIZET2NUM(nodes[i])); - } - } - return hash; + return setup_hash(argc, argv); } static void @@ -763,7 +623,7 @@ collect_values(st_data_t key, st_data_t value, st_data_t data) * * With this method, you can find memory leaks. * - * This method is only expected to work except with C Ruby. + * This method is only expected to work with C Ruby. * * Example: * ObjectSpace.reachable_objects_from(['a', 'b', 'c']) @@ -817,26 +677,26 @@ reachable_object_from_root_i(const char *category, VALUE obj, void *ptr) VALUE category_objects; if (category == data->last_category) { - category_str = data->last_category_str; - category_objects = data->last_category_objects; + category_str = data->last_category_str; + category_objects = data->last_category_objects; } else { - data->last_category = category; - category_str = data->last_category_str = rb_str_new2(category); - category_objects = data->last_category_objects = rb_ident_hash_new(); - if (!NIL_P(rb_hash_lookup(data->categories, category_str))) { - rb_bug("reachable_object_from_root_i: category should insert at once"); - } - rb_hash_aset(data->categories, category_str, category_objects); + data->last_category = category; + category_str = data->last_category_str = rb_str_new2(category); + category_objects = data->last_category_objects = rb_ident_hash_new(); + if (!NIL_P(rb_hash_lookup(data->categories, category_str))) { + rb_bug("reachable_object_from_root_i: category should insert at once"); + } + rb_hash_aset(data->categories, category_str, category_objects); } if (rb_objspace_markable_object_p(obj) && - obj != data->categories && - obj != data->last_category_objects) { - if (rb_objspace_internal_object_p(obj)) { - obj = iow_newobj(obj); - } - rb_hash_aset(category_objects, obj, obj); + obj != data->categories && + obj != data->last_category_objects) { + if (rb_objspace_internal_object_p(obj)) { + obj = iow_newobj(obj); + } + rb_hash_aset(category_objects, obj, obj); } } @@ -872,14 +732,14 @@ static VALUE wrap_klass_iow(VALUE klass) { if (!RTEST(klass)) { - return Qnil; + return Qnil; } else if (RB_TYPE_P(klass, T_ICLASS) || CLASS_OF(klass) == Qfalse /* hidden object */) { - return iow_newobj(klass); + return iow_newobj(klass); } else { - return klass; + return klass; } } @@ -898,7 +758,7 @@ objspace_internal_class_of(VALUE self, VALUE obj) VALUE klass; if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { - obj = (VALUE)DATA_PTR(obj); + obj = (VALUE)DATA_PTR(obj); } if (RB_TYPE_P(obj, T_IMEMO)) { @@ -925,17 +785,17 @@ objspace_internal_super_of(VALUE self, VALUE obj) VALUE super; if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { - obj = (VALUE)DATA_PTR(obj); + obj = (VALUE)DATA_PTR(obj); } switch (OBJ_BUILTIN_TYPE(obj)) { case T_MODULE: case T_CLASS: case T_ICLASS: - super = RCLASS_SUPER(obj); - break; + super = RCLASS_SUPER(obj); + break; default: - rb_raise(rb_eArgError, "class or module is expected"); + rb_raise(rb_eArgError, "class or module is expected"); } return wrap_klass_iow(super); @@ -953,7 +813,7 @@ void Init_objspace_dump(VALUE rb_mObjSpace); * * You need to <code>require 'objspace'</code> to use this extension module. * - * Generally, you *SHOULD NOT* use this library if you do not know + * Generally, you *SHOULD* *NOT* use this library if you do not know * about the MRI implementation. Mainly, this library is for (memory) * profiler developers and MRI developers who need to know about MRI * memory usage. diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index b570acbd95..bb479b91c5 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -12,16 +12,23 @@ **********************************************************************/ -#include "gc.h" +#include "id_table.h" #include "internal.h" +#include "internal/array.h" +#include "internal/class.h" +#include "internal/gc.h" #include "internal/hash.h" +#include "internal/io.h" #include "internal/string.h" #include "internal/sanitizers.h" +#include "symbol.h" +#include "shape.h" #include "node.h" #include "objspace.h" #include "ruby/debug.h" #include "ruby/util.h" #include "ruby/io.h" +#include "vm_callinfo.h" #include "vm_core.h" RUBY_EXTERN const char ruby_hexdigits[]; @@ -41,6 +48,7 @@ struct dump_config { unsigned int full_heap: 1; unsigned int partial_dump; size_t since; + size_t shapes_since; unsigned long buffer_len; char buffer[BUFFER_CAPACITY]; }; @@ -76,7 +84,8 @@ buffer_ensure_capa(struct dump_config *dc, unsigned long requested) } } -static void buffer_append(struct dump_config *dc, const char *cstr, unsigned long len) +static void +buffer_append(struct dump_config *dc, const char *cstr, unsigned long len) { if (LIKELY(len > 0)) { buffer_ensure_capa(dc, len); @@ -142,10 +151,10 @@ dump_append_sizet(struct dump_config *dc, const size_t number) } static void -dump_append_c(struct dump_config *dc, char c) +dump_append_c(struct dump_config *dc, unsigned char c) { if (c <= 0x1f) { - const unsigned int width = (sizeof(c) * CHAR_BIT / 4) + 5; + const unsigned int width = rb_strlen_lit("\\u0000") + 1; buffer_ensure_capa(dc, width); unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "\\u00%02x", c); RUBY_ASSERT(required <= width); @@ -159,11 +168,9 @@ dump_append_c(struct dump_config *dc, char c) } static void -dump_append_ref(struct dump_config *dc, VALUE ref) +dump_append_ptr(struct dump_config *dc, VALUE ref) { - RUBY_ASSERT(ref > 0); - - char buffer[((sizeof(VALUE) * CHAR_BIT + 3) / 4) + 4]; + char buffer[roomof(sizeof(VALUE) * CHAR_BIT, 4) + rb_strlen_lit("\"0x\"")]; char *buffer_start, *buffer_end; buffer_start = buffer_end = &buffer[sizeof(buffer)]; @@ -179,6 +186,14 @@ dump_append_ref(struct dump_config *dc, VALUE ref) } static void +dump_append_ref(struct dump_config *dc, VALUE ref) +{ + RUBY_ASSERT(ref > 0); + dump_append_ptr(dc, ref); +} + + +static void dump_append_string_value(struct dump_config *dc, VALUE obj) { long i; @@ -235,32 +250,32 @@ obj_type(VALUE obj) { switch (BUILTIN_TYPE(obj)) { #define CASE_TYPE(type) case T_##type: return #type - CASE_TYPE(NONE); - CASE_TYPE(NIL); - CASE_TYPE(OBJECT); - CASE_TYPE(CLASS); - CASE_TYPE(ICLASS); - CASE_TYPE(MODULE); - CASE_TYPE(FLOAT); - CASE_TYPE(STRING); - CASE_TYPE(REGEXP); - CASE_TYPE(ARRAY); - CASE_TYPE(HASH); - CASE_TYPE(STRUCT); - CASE_TYPE(BIGNUM); - CASE_TYPE(FILE); - CASE_TYPE(FIXNUM); - CASE_TYPE(TRUE); - CASE_TYPE(FALSE); - CASE_TYPE(DATA); - CASE_TYPE(MATCH); - CASE_TYPE(SYMBOL); - CASE_TYPE(RATIONAL); - CASE_TYPE(COMPLEX); - CASE_TYPE(IMEMO); - CASE_TYPE(UNDEF); - CASE_TYPE(NODE); - CASE_TYPE(ZOMBIE); + CASE_TYPE(NONE); + CASE_TYPE(NIL); + CASE_TYPE(OBJECT); + CASE_TYPE(CLASS); + CASE_TYPE(ICLASS); + CASE_TYPE(MODULE); + CASE_TYPE(FLOAT); + CASE_TYPE(STRING); + CASE_TYPE(REGEXP); + CASE_TYPE(ARRAY); + CASE_TYPE(HASH); + CASE_TYPE(STRUCT); + CASE_TYPE(BIGNUM); + CASE_TYPE(FILE); + CASE_TYPE(FIXNUM); + CASE_TYPE(TRUE); + CASE_TYPE(FALSE); + CASE_TYPE(DATA); + CASE_TYPE(MATCH); + CASE_TYPE(SYMBOL); + CASE_TYPE(RATIONAL); + CASE_TYPE(COMPLEX); + CASE_TYPE(IMEMO); + CASE_TYPE(UNDEF); + CASE_TYPE(NODE); + CASE_TYPE(ZOMBIE); #undef CASE_TYPE default: break; } @@ -313,6 +328,17 @@ reachable_object_i(VALUE ref, void *data) dc->cur_obj_references++; } +static bool +dump_string_ascii_only(const char *str, long size) +{ + for (long i = 0; i < size; i++) { + if (str[i] & 0x80) { + return false; + } + } + return true; +} + static void dump_append_string_content(struct dump_config *dc, VALUE obj) { @@ -323,12 +349,35 @@ dump_append_string_content(struct dump_config *dc, VALUE obj) dump_append_sizet(dc, rb_str_capacity(obj)); } - if (is_ascii_string(obj)) { - dump_append(dc, ", \"value\":"); - dump_append_string_value(dc, obj); + if (RSTRING_LEN(obj) && rb_enc_asciicompat(rb_enc_from_index(ENCODING_GET(obj)))) { + int cr = ENC_CODERANGE(obj); + if (cr == RUBY_ENC_CODERANGE_UNKNOWN) { + if (dump_string_ascii_only(RSTRING_PTR(obj), RSTRING_LEN(obj))) { + cr = RUBY_ENC_CODERANGE_7BIT; + } + } + if (cr == RUBY_ENC_CODERANGE_7BIT) { + dump_append(dc, ", \"value\":"); + dump_append_string_value(dc, obj); + } + } +} + +static inline void +dump_append_id(struct dump_config *dc, ID id) +{ + VALUE str = rb_sym2str(ID2SYM(id)); + if (RTEST(str)) { + dump_append_string_value(dc, str); + } + else { + dump_append(dc, "\"ID_INTERNAL("); + dump_append_sizet(dc, rb_id_to_serial(id)); + dump_append(dc, ")\""); } } + static void dump_object(VALUE obj, struct dump_config *dc) { @@ -337,6 +386,7 @@ dump_object(VALUE obj, struct dump_config *dc) rb_io_t *fptr; ID flags[RB_OBJ_GC_FLAGS_MAX]; size_t n, i; + ID mid; if (SPECIAL_CONST_P(obj)) { dump_append_special_const(dc, obj); @@ -345,7 +395,11 @@ dump_object(VALUE obj, struct dump_config *dc) dc->cur_obj = obj; dc->cur_obj_references = 0; - dc->cur_obj_klass = BUILTIN_TYPE(obj) == T_NODE ? 0 : RBASIC_CLASS(obj); + if (BUILTIN_TYPE(obj) == T_NODE || BUILTIN_TYPE(obj) == T_IMEMO) { + dc->cur_obj_klass = 0; + } else { + dc->cur_obj_klass = RBASIC_CLASS(obj); + } if (dc->partial_dump && (!ainfo || ainfo->generation < dc->since)) { return; @@ -361,6 +415,10 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, obj_type(obj)); dump_append(dc, "\""); + size_t shape_id = rb_shape_get_shape_id(obj); + dump_append(dc, ", \"shape_id\":"); + dump_append_sizet(dc, shape_id); + dump_append(dc, ", \"slot_size\":"); dump_append_sizet(dc, dc->cur_page_slot_size); @@ -380,6 +438,33 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, ", \"imemo_type\":\""); dump_append(dc, rb_imemo_name(imemo_type(obj))); dump_append(dc, "\""); + + switch (imemo_type(obj)) { + case imemo_callinfo: + mid = vm_ci_mid((const struct rb_callinfo *)obj); + if (mid != 0) { + dump_append(dc, ", \"mid\":"); + dump_append_id(dc, mid); + } + break; + + case imemo_callcache: + mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id; + if (mid != 0) { + dump_append(dc, ", \"called_id\":"); + dump_append_id(dc, mid); + + VALUE klass = ((const struct rb_callcache *)obj)->klass; + if (klass != 0) { + dump_append(dc, ", \"receiver_class\":"); + dump_append_ref(dc, klass); + } + } + break; + + default: + break; + } break; case T_SYMBOL: @@ -389,10 +474,10 @@ dump_object(VALUE obj, struct dump_config *dc) case T_STRING: if (STR_EMBED_P(obj)) dump_append(dc, ", \"embedded\":true"); - if (is_broken_string(obj)) - dump_append(dc, ", \"broken\":true"); if (FL_TEST(obj, RSTRING_FSTR)) dump_append(dc, ", \"fstring\":true"); + if (CHILLED_STRING_P(obj)) + dump_append(dc, ", \"chilled\":true"); if (STR_SHARED_P(obj)) dump_append(dc, ", \"shared\":true"); else @@ -403,6 +488,27 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, rb_enc_name(rb_enc_from_index(ENCODING_GET(obj)))); dump_append(dc, "\""); } + + dump_append(dc, ", \"coderange\":\""); + switch (RB_ENC_CODERANGE(obj)) { + case RUBY_ENC_CODERANGE_UNKNOWN: + dump_append(dc, "unknown"); + break; + case RUBY_ENC_CODERANGE_7BIT: + dump_append(dc, "7bit"); + break; + case RUBY_ENC_CODERANGE_VALID: + dump_append(dc, "valid"); + break; + case RUBY_ENC_CODERANGE_BROKEN: + dump_append(dc, "broken"); + break; + } + dump_append(dc, "\""); + + if (RB_ENC_CODERANGE(obj) == RUBY_ENC_CODERANGE_BROKEN) + dump_append(dc, ", \"broken\":true"); + break; case T_HASH: @@ -417,7 +523,7 @@ dump_object(VALUE obj, struct dump_config *dc) case T_ARRAY: dump_append(dc, ", \"length\":"); dump_append_ld(dc, RARRAY_LEN(obj)); - if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED)) + if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_SHARED_FLAG)) dump_append(dc, ", \"shared\":true"); if (FL_TEST(obj, RARRAY_EMBED_FLAG)) dump_append(dc, ", \"embedded\":true"); @@ -431,6 +537,9 @@ dump_object(VALUE obj, struct dump_config *dc) break; case T_CLASS: + dump_append(dc, ", \"variation_count\":"); + dump_append_d(dc, RCLASS_EXT(obj)->variation_count); + case T_MODULE: if (rb_class_get_superclass(obj)) { dump_append(dc, ", \"superclass\":"); @@ -443,7 +552,8 @@ dump_object(VALUE obj, struct dump_config *dc) dump_append(dc, ", \"name\":\""); dump_append(dc, RSTRING_PTR(mod_name)); dump_append(dc, "\""); - } else { + } + else { VALUE real_mod_name = rb_mod_name(rb_class_real(obj)); if (RTEST(real_mod_name)) { dump_append(dc, ", \"real_class_name\":\""); @@ -452,7 +562,7 @@ dump_object(VALUE obj, struct dump_config *dc) } } - if (FL_TEST(obj, FL_SINGLETON)) { + if (RCLASS_SINGLETON_P(obj)) { dump_append(dc, ", \"singleton\":true"); } } @@ -473,8 +583,15 @@ dump_object(VALUE obj, struct dump_config *dc) break; case T_OBJECT: + if (FL_TEST(obj, ROBJECT_EMBED)) { + dump_append(dc, ", \"embedded\":true"); + } + dump_append(dc, ", \"ivars\":"); - dump_append_lu(dc, ROBJECT_NUMIV(obj)); + dump_append_lu(dc, ROBJECT_IV_COUNT(obj)); + if (rb_shape_obj_too_complex(obj)) { + dump_append(dc, ", \"too_complex_shape\":true"); + } break; case T_FILE: @@ -486,8 +603,8 @@ dump_object(VALUE obj, struct dump_config *dc) break; case T_ZOMBIE: - dump_append(dc, "}\n"); - return; + dump_append(dc, "}\n"); + return; default: break; @@ -545,8 +662,8 @@ heap_i(void *vstart, void *vend, size_t stride, void *data) asan_unpoison_object(v, false); dc->cur_page_slot_size = stride; - if (dc->full_heap || RBASIC(v)->flags) - dump_object(v, dc); + if (dc->full_heap || RBASIC(v)->flags) + dump_object(v, dc); if (ptr) { asan_poison_object(v); @@ -578,7 +695,7 @@ root_obj_i(const char *category, VALUE obj, void *data) } static void -dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since) +dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE shapes) { dc->full_heap = 0; @@ -587,7 +704,8 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since) if (TYPE(output) == T_STRING) { dc->stream = Qfalse; dc->string = output; - } else { + } + else { dc->stream = output; dc->string = Qfalse; } @@ -599,9 +717,12 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since) if (RTEST(since)) { dc->partial_dump = 1; dc->since = NUM2SIZET(since); - } else { + } + else { dc->partial_dump = 0; } + + dc->shapes_since = RTEST(shapes) ? NUM2SIZET(shapes) : 0; } static VALUE @@ -611,12 +732,14 @@ dump_result(struct dump_config *dc) if (dc->string) { return dc->string; - } else { + } + else { rb_io_flush(dc->stream); return dc->stream; } } +/* :nodoc: */ static VALUE objspace_dump(VALUE os, VALUE obj, VALUE output) { @@ -625,18 +748,77 @@ objspace_dump(VALUE os, VALUE obj, VALUE output) dc.cur_page_slot_size = rb_gc_obj_slot_size(obj); } - dump_output(&dc, output, Qnil, Qnil); + dump_output(&dc, output, Qnil, Qnil, Qnil); dump_object(obj, &dc); return dump_result(&dc); } +static void +shape_i(rb_shape_t *shape, void *data) +{ + struct dump_config *dc = (struct dump_config *)data; + + size_t shape_id = rb_shape_id(shape); + if (shape_id < dc->shapes_since) { + return; + } + + dump_append(dc, "{\"address\":"); + dump_append_ref(dc, (VALUE)shape); + + dump_append(dc, ", \"type\":\"SHAPE\", \"id\":"); + dump_append_sizet(dc, shape_id); + + if (shape->type != SHAPE_ROOT) { + dump_append(dc, ", \"parent_id\":"); + dump_append_lu(dc, shape->parent_id); + } + + dump_append(dc, ", \"depth\":"); + dump_append_sizet(dc, rb_shape_depth(shape)); + + dump_append(dc, ", \"shape_type\":"); + switch((enum shape_type)shape->type) { + case SHAPE_ROOT: + dump_append(dc, "\"ROOT\""); + break; + case SHAPE_IVAR: + dump_append(dc, "\"IVAR\""); + + dump_append(dc, ",\"edge_name\":"); + dump_append_id(dc, shape->edge_name); + + break; + case SHAPE_FROZEN: + dump_append(dc, "\"FROZEN\""); + break; + case SHAPE_T_OBJECT: + dump_append(dc, "\"T_OBJECT\""); + break; + case SHAPE_OBJ_TOO_COMPLEX: + dump_append(dc, "\"OBJ_TOO_COMPLEX\""); + break; + default: + rb_bug("[objspace] unexpected shape type"); + } + + dump_append(dc, ", \"edges\":"); + dump_append_sizet(dc, rb_shape_edges_count(shape)); + + dump_append(dc, ", \"memsize\":"); + dump_append_sizet(dc, rb_shape_memsize(shape)); + + dump_append(dc, "}\n"); +} + +/* :nodoc: */ static VALUE -objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since) +objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes) { struct dump_config dc = {0,}; - dump_output(&dc, output, full, since); + dump_output(&dc, output, full, since, shapes); if (!dc.partial_dump || dc.since == 0) { /* dump roots */ @@ -644,12 +826,29 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since) if (dc.roots) dump_append(&dc, "]}\n"); } + if (RTEST(shapes)) { + rb_shape_each_shape(shape_i, &dc); + } + /* dump all objects */ rb_objspace_each_objects(heap_i, &dc); return dump_result(&dc); } +/* :nodoc: */ +static VALUE +objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes) +{ + struct dump_config dc = {0,}; + dump_output(&dc, output, Qfalse, Qnil, shapes); + + if (RTEST(shapes)) { + rb_shape_each_shape(shape_i, &dc); + } + return dump_result(&dc); +} + void Init_objspace_dump(VALUE rb_mObjSpace) { @@ -659,7 +858,8 @@ Init_objspace_dump(VALUE rb_mObjSpace) #endif rb_define_module_function(rb_mObjSpace, "_dump", objspace_dump, 2); - rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 3); + rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 4); + rb_define_module_function(rb_mObjSpace, "_dump_shapes", objspace_dump_shapes, 2); /* force create static IDs */ rb_obj_gc_flags(rb_mObjSpace, 0, 0); |