diff options
Diffstat (limited to 'ext/objspace')
| -rw-r--r-- | ext/objspace/depend | 576 | ||||
| -rw-r--r-- | ext/objspace/lib/objspace.rb | 135 | ||||
| -rw-r--r-- | ext/objspace/lib/objspace/trace.rb | 45 | ||||
| -rw-r--r-- | ext/objspace/object_tracing.c | 281 | ||||
| -rw-r--r-- | ext/objspace/objspace.c | 725 | ||||
| -rw-r--r-- | ext/objspace/objspace_dump.c | 984 |
6 files changed, 1918 insertions, 828 deletions
diff --git a/ext/objspace/depend b/ext/objspace/depend index 84e944f228..d9dfc0c42b 100644 --- a/ext/objspace/depend +++ b/ext/objspace/depend @@ -1,31 +1,376 @@ # AUTOGENERATED DEPENDENCIES START object_tracing.o: $(RUBY_EXTCONF_H) object_tracing.o: $(arch_hdrdir)/ruby/config.h -object_tracing.o: $(hdrdir)/ruby.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 +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/io.h +object_tracing.o: $(hdrdir)/ruby/internal/abi.h +object_tracing.o: $(hdrdir)/ruby/internal/anyargs.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/char.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/double.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/int.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/long.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/short.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +object_tracing.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +object_tracing.o: $(hdrdir)/ruby/internal/assume.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/artificial.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/cold.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/const.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/constexpr.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/deprecated.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/error.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/forceinline.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/format.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/noalias.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +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 +object_tracing.o: $(hdrdir)/ruby/internal/attr/warning.h +object_tracing.o: $(hdrdir)/ruby/internal/attr/weakref.h +object_tracing.o: $(hdrdir)/ruby/internal/cast.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +object_tracing.o: $(hdrdir)/ruby/internal/compiler_since.h +object_tracing.o: $(hdrdir)/ruby/internal/config.h +object_tracing.o: $(hdrdir)/ruby/internal/constant_p.h +object_tracing.o: $(hdrdir)/ruby/internal/core.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rarray.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rbasic.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rbignum.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rclass.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rdata.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rfile.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rhash.h +object_tracing.o: $(hdrdir)/ruby/internal/core/robject.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rregexp.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rstring.h +object_tracing.o: $(hdrdir)/ruby/internal/core/rstruct.h +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 +object_tracing.o: $(hdrdir)/ruby/internal/fl_type.h +object_tracing.o: $(hdrdir)/ruby/internal/gc.h +object_tracing.o: $(hdrdir)/ruby/internal/glob.h +object_tracing.o: $(hdrdir)/ruby/internal/globals.h +object_tracing.o: $(hdrdir)/ruby/internal/has/attribute.h +object_tracing.o: $(hdrdir)/ruby/internal/has/builtin.h +object_tracing.o: $(hdrdir)/ruby/internal/has/c_attribute.h +object_tracing.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +object_tracing.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +object_tracing.o: $(hdrdir)/ruby/internal/has/extension.h +object_tracing.o: $(hdrdir)/ruby/internal/has/feature.h +object_tracing.o: $(hdrdir)/ruby/internal/has/warning.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/array.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/bignum.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/class.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/compar.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/complex.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/cont.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/dir.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/enum.h +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/hash.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/io.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/load.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/marshal.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/numeric.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/object.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/parse.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/proc.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/process.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/random.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/range.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/rational.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/re.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/ruby.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/select.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/set.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/signal.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/sprintf.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/string.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/struct.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/thread.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/time.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/variable.h +object_tracing.o: $(hdrdir)/ruby/internal/intern/vm.h +object_tracing.o: $(hdrdir)/ruby/internal/interpreter.h +object_tracing.o: $(hdrdir)/ruby/internal/iterator.h +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/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 +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)/id_table.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/box.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/set_table.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.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 +objspace.o: $(hdrdir)/ruby/backward/2/bool.h +objspace.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +objspace.o: $(hdrdir)/ruby/backward/2/inttypes.h +objspace.o: $(hdrdir)/ruby/backward/2/limits.h +objspace.o: $(hdrdir)/ruby/backward/2/long_long.h +objspace.o: $(hdrdir)/ruby/backward/2/stdalign.h +objspace.o: $(hdrdir)/ruby/backward/2/stdarg.h objspace.o: $(hdrdir)/ruby/defines.h objspace.o: $(hdrdir)/ruby/encoding.h objspace.o: $(hdrdir)/ruby/intern.h +objspace.o: $(hdrdir)/ruby/internal/abi.h +objspace.o: $(hdrdir)/ruby/internal/anyargs.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/char.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/double.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/int.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/long.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/short.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +objspace.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +objspace.o: $(hdrdir)/ruby/internal/assume.h +objspace.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +objspace.o: $(hdrdir)/ruby/internal/attr/artificial.h +objspace.o: $(hdrdir)/ruby/internal/attr/cold.h +objspace.o: $(hdrdir)/ruby/internal/attr/const.h +objspace.o: $(hdrdir)/ruby/internal/attr/constexpr.h +objspace.o: $(hdrdir)/ruby/internal/attr/deprecated.h +objspace.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +objspace.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +objspace.o: $(hdrdir)/ruby/internal/attr/error.h +objspace.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +objspace.o: $(hdrdir)/ruby/internal/attr/forceinline.h +objspace.o: $(hdrdir)/ruby/internal/attr/format.h +objspace.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +objspace.o: $(hdrdir)/ruby/internal/attr/noalias.h +objspace.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +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 +objspace.o: $(hdrdir)/ruby/internal/attr/warning.h +objspace.o: $(hdrdir)/ruby/internal/attr/weakref.h +objspace.o: $(hdrdir)/ruby/internal/cast.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +objspace.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +objspace.o: $(hdrdir)/ruby/internal/compiler_since.h +objspace.o: $(hdrdir)/ruby/internal/config.h +objspace.o: $(hdrdir)/ruby/internal/constant_p.h +objspace.o: $(hdrdir)/ruby/internal/core.h +objspace.o: $(hdrdir)/ruby/internal/core/rarray.h +objspace.o: $(hdrdir)/ruby/internal/core/rbasic.h +objspace.o: $(hdrdir)/ruby/internal/core/rbignum.h +objspace.o: $(hdrdir)/ruby/internal/core/rclass.h +objspace.o: $(hdrdir)/ruby/internal/core/rdata.h +objspace.o: $(hdrdir)/ruby/internal/core/rfile.h +objspace.o: $(hdrdir)/ruby/internal/core/rhash.h +objspace.o: $(hdrdir)/ruby/internal/core/rmatch.h +objspace.o: $(hdrdir)/ruby/internal/core/robject.h +objspace.o: $(hdrdir)/ruby/internal/core/rregexp.h +objspace.o: $(hdrdir)/ruby/internal/core/rstring.h +objspace.o: $(hdrdir)/ruby/internal/core/rstruct.h +objspace.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +objspace.o: $(hdrdir)/ruby/internal/ctype.h +objspace.o: $(hdrdir)/ruby/internal/dllexport.h +objspace.o: $(hdrdir)/ruby/internal/dosish.h +objspace.o: $(hdrdir)/ruby/internal/encoding/coderange.h +objspace.o: $(hdrdir)/ruby/internal/encoding/ctype.h +objspace.o: $(hdrdir)/ruby/internal/encoding/encoding.h +objspace.o: $(hdrdir)/ruby/internal/encoding/pathname.h +objspace.o: $(hdrdir)/ruby/internal/encoding/re.h +objspace.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +objspace.o: $(hdrdir)/ruby/internal/encoding/string.h +objspace.o: $(hdrdir)/ruby/internal/encoding/symbol.h +objspace.o: $(hdrdir)/ruby/internal/encoding/transcode.h +objspace.o: $(hdrdir)/ruby/internal/error.h +objspace.o: $(hdrdir)/ruby/internal/eval.h +objspace.o: $(hdrdir)/ruby/internal/event.h +objspace.o: $(hdrdir)/ruby/internal/fl_type.h +objspace.o: $(hdrdir)/ruby/internal/gc.h +objspace.o: $(hdrdir)/ruby/internal/glob.h +objspace.o: $(hdrdir)/ruby/internal/globals.h +objspace.o: $(hdrdir)/ruby/internal/has/attribute.h +objspace.o: $(hdrdir)/ruby/internal/has/builtin.h +objspace.o: $(hdrdir)/ruby/internal/has/c_attribute.h +objspace.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +objspace.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +objspace.o: $(hdrdir)/ruby/internal/has/extension.h +objspace.o: $(hdrdir)/ruby/internal/has/feature.h +objspace.o: $(hdrdir)/ruby/internal/has/warning.h +objspace.o: $(hdrdir)/ruby/internal/intern/array.h +objspace.o: $(hdrdir)/ruby/internal/intern/bignum.h +objspace.o: $(hdrdir)/ruby/internal/intern/class.h +objspace.o: $(hdrdir)/ruby/internal/intern/compar.h +objspace.o: $(hdrdir)/ruby/internal/intern/complex.h +objspace.o: $(hdrdir)/ruby/internal/intern/cont.h +objspace.o: $(hdrdir)/ruby/internal/intern/dir.h +objspace.o: $(hdrdir)/ruby/internal/intern/enum.h +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/hash.h +objspace.o: $(hdrdir)/ruby/internal/intern/io.h +objspace.o: $(hdrdir)/ruby/internal/intern/load.h +objspace.o: $(hdrdir)/ruby/internal/intern/marshal.h +objspace.o: $(hdrdir)/ruby/internal/intern/numeric.h +objspace.o: $(hdrdir)/ruby/internal/intern/object.h +objspace.o: $(hdrdir)/ruby/internal/intern/parse.h +objspace.o: $(hdrdir)/ruby/internal/intern/proc.h +objspace.o: $(hdrdir)/ruby/internal/intern/process.h +objspace.o: $(hdrdir)/ruby/internal/intern/random.h +objspace.o: $(hdrdir)/ruby/internal/intern/range.h +objspace.o: $(hdrdir)/ruby/internal/intern/rational.h +objspace.o: $(hdrdir)/ruby/internal/intern/re.h +objspace.o: $(hdrdir)/ruby/internal/intern/ruby.h +objspace.o: $(hdrdir)/ruby/internal/intern/select.h +objspace.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +objspace.o: $(hdrdir)/ruby/internal/intern/set.h +objspace.o: $(hdrdir)/ruby/internal/intern/signal.h +objspace.o: $(hdrdir)/ruby/internal/intern/sprintf.h +objspace.o: $(hdrdir)/ruby/internal/intern/string.h +objspace.o: $(hdrdir)/ruby/internal/intern/struct.h +objspace.o: $(hdrdir)/ruby/internal/intern/thread.h +objspace.o: $(hdrdir)/ruby/internal/intern/time.h +objspace.o: $(hdrdir)/ruby/internal/intern/variable.h +objspace.o: $(hdrdir)/ruby/internal/intern/vm.h +objspace.o: $(hdrdir)/ruby/internal/interpreter.h +objspace.o: $(hdrdir)/ruby/internal/iterator.h +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/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 +objspace.o: $(hdrdir)/ruby/internal/variable.h +objspace.o: $(hdrdir)/ruby/internal/warning_push.h +objspace.o: $(hdrdir)/ruby/internal/xmalloc.h objspace.o: $(hdrdir)/ruby/io.h objspace.o: $(hdrdir)/ruby/missing.h objspace.o: $(hdrdir)/ruby/onigmo.h @@ -35,21 +380,213 @@ 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/box.h +objspace.o: $(top_srcdir)/internal/class.h +objspace.o: $(top_srcdir)/internal/compilers.h +objspace.o: $(top_srcdir)/internal/gc.h +objspace.o: $(top_srcdir)/internal/hash.h +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/set_table.h +objspace.o: $(top_srcdir)/internal/static_assert.h +objspace.o: $(top_srcdir)/internal/struct.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) objspace_dump.o: $(arch_hdrdir)/ruby/config.h -objspace_dump.o: $(hdrdir)/ruby.h objspace_dump.o: $(hdrdir)/ruby/assert.h +objspace_dump.o: $(hdrdir)/ruby/atomic.h objspace_dump.o: $(hdrdir)/ruby/backward.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/assume.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/attributes.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/bool.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/inttypes.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/limits.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/long_long.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/stdalign.h +objspace_dump.o: $(hdrdir)/ruby/backward/2/stdarg.h objspace_dump.o: $(hdrdir)/ruby/debug.h objspace_dump.o: $(hdrdir)/ruby/defines.h objspace_dump.o: $(hdrdir)/ruby/encoding.h objspace_dump.o: $(hdrdir)/ruby/intern.h +objspace_dump.o: $(hdrdir)/ruby/internal/abi.h +objspace_dump.o: $(hdrdir)/ruby/internal/anyargs.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/char.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/double.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/int.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/long.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/short.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +objspace_dump.o: $(hdrdir)/ruby/internal/assume.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/artificial.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/cold.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/const.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/constexpr.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/deprecated.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/error.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/forceinline.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/format.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/noalias.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +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 +objspace_dump.o: $(hdrdir)/ruby/internal/attr/warning.h +objspace_dump.o: $(hdrdir)/ruby/internal/attr/weakref.h +objspace_dump.o: $(hdrdir)/ruby/internal/cast.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +objspace_dump.o: $(hdrdir)/ruby/internal/compiler_since.h +objspace_dump.o: $(hdrdir)/ruby/internal/config.h +objspace_dump.o: $(hdrdir)/ruby/internal/constant_p.h +objspace_dump.o: $(hdrdir)/ruby/internal/core.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rarray.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rbasic.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rbignum.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rclass.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rdata.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rfile.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rhash.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/robject.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rregexp.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rstring.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rstruct.h +objspace_dump.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +objspace_dump.o: $(hdrdir)/ruby/internal/ctype.h +objspace_dump.o: $(hdrdir)/ruby/internal/dllexport.h +objspace_dump.o: $(hdrdir)/ruby/internal/dosish.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/coderange.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/ctype.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/encoding.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/pathname.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/re.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/string.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/symbol.h +objspace_dump.o: $(hdrdir)/ruby/internal/encoding/transcode.h +objspace_dump.o: $(hdrdir)/ruby/internal/error.h +objspace_dump.o: $(hdrdir)/ruby/internal/eval.h +objspace_dump.o: $(hdrdir)/ruby/internal/event.h +objspace_dump.o: $(hdrdir)/ruby/internal/fl_type.h +objspace_dump.o: $(hdrdir)/ruby/internal/gc.h +objspace_dump.o: $(hdrdir)/ruby/internal/glob.h +objspace_dump.o: $(hdrdir)/ruby/internal/globals.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/attribute.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/builtin.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/c_attribute.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/extension.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/feature.h +objspace_dump.o: $(hdrdir)/ruby/internal/has/warning.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/array.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/bignum.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/class.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/compar.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/complex.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/cont.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/dir.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/enum.h +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/hash.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/io.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/load.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/marshal.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/numeric.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/object.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/parse.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/proc.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/process.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/random.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/range.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/rational.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/re.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/ruby.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/select.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/set.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/signal.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/sprintf.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/string.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/struct.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/thread.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/time.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/variable.h +objspace_dump.o: $(hdrdir)/ruby/internal/intern/vm.h +objspace_dump.o: $(hdrdir)/ruby/internal/interpreter.h +objspace_dump.o: $(hdrdir)/ruby/internal/iterator.h +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/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 +objspace_dump.o: $(hdrdir)/ruby/internal/variable.h +objspace_dump.o: $(hdrdir)/ruby/internal/warning_push.h +objspace_dump.o: $(hdrdir)/ruby/internal/xmalloc.h objspace_dump.o: $(hdrdir)/ruby/io.h objspace_dump.o: $(hdrdir)/ruby/missing.h objspace_dump.o: $(hdrdir)/ruby/onigmo.h @@ -58,20 +595,47 @@ objspace_dump.o: $(hdrdir)/ruby/ruby.h objspace_dump.o: $(hdrdir)/ruby/st.h objspace_dump.o: $(hdrdir)/ruby/subst.h objspace_dump.o: $(hdrdir)/ruby/thread_native.h +objspace_dump.o: $(hdrdir)/ruby/util.h 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)/encindex.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/box.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/set_table.h +objspace_dump.o: $(top_srcdir)/internal/static_assert.h +objspace_dump.o: $(top_srcdir)/internal/string.h +objspace_dump.o: $(top_srcdir)/internal/struct.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 new file mode 100644 index 0000000000..47873f5112 --- /dev/null +++ b/ext/objspace/lib/objspace.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require 'objspace.so' + +module ObjectSpace + class << self + private :_dump + private :_dump_all + private :_dump_shapes + end + + module_function + + # 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 + # not guaranteed to be compatible in future versions of ruby. + def dump(obj, output: :string) + out = case output + when :file, nil + require 'tempfile' + Tempfile.create(%w(rubyobj .json)) + when :stdout + STDOUT + when :string + +'' + when IO + output + else + raise ArgumentError, "wrong output option: #{output.inspect}" + end + + ret = _dump(obj, out) + return nil if output == :stdout + ret + end + + + # 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+). + # + # _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 omitted or is +nil+, all objects are dumped. + # + # _shapes_ must be a boolean or a non-negative integer. + # + # 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_shapes(output: :file, since: 0) + out = case output + when :file, nil + require 'tempfile' + Tempfile.create(%w(rubyshapes .json)) + when :stdout + STDOUT + when :string + +'' + when IO + output + else + raise ArgumentError, "wrong output option: #{output.inspect}" + end + + ret = _dump_shapes(out, since) + return nil if output == :stdout + ret + end +end diff --git a/ext/objspace/lib/objspace/trace.rb b/ext/objspace/lib/objspace/trace.rb new file mode 100644 index 0000000000..c23f5a9d52 --- /dev/null +++ b/ext/objspace/lib/objspace/trace.rb @@ -0,0 +1,45 @@ +# This is a simple tool to enable the object allocation tracer. +# When you have an object of unknown provenance, you can use this +# to investigate where the object in question is created. +# +# = Important notice +# +# This is only for debugging purpose. Do not use this in production. +# Require'ing this file immediately starts tracing the object allocation, +# which brings a large performance overhead. +# +# = Usage +# +# 1. Add `require "objspace/trace"` into your code (or add `-robjspace/trace` into the command line) +# 2. `p obj` will show the allocation site of `obj` +# +# Note: This redefines `Kernel#p` method, but not `Object#inspect`. +# +# = Examples +# +# 1: require "objspace/trace" +# 2: +# 3: obj = "str" +# 4: +# 5: p obj #=> "str" @ test.rb:3 + +require 'objspace.so' + +module Kernel + remove_method :p + define_method(:p) do |*objs| + objs.each do |obj| + file = ObjectSpace.allocation_sourcefile(obj) + line = ObjectSpace.allocation_sourceline(obj) + if file + puts "#{ obj.inspect } @ #{ file }:#{ line }" + else + puts obj.inspect + end + end + end +end + +ObjectSpace.trace_object_allocations_start + +warn "objspace/trace is enabled" diff --git a/ext/objspace/object_tracing.c b/ext/objspace/object_tracing.c index 7c354498ab..c06f1f68dd 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,42 +32,50 @@ 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; } } +static int +delete_unique_str_dec(st_data_t *key, st_data_t *value, st_data_t arg, int existing) +{ + assert(existing); + *value = arg; + return ST_CONTINUE; +} + 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_update(tbl, (st_data_t)str, delete_unique_str_dec, (st_data_t)(n-1)); + } } } @@ -87,18 +96,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,36 +130,123 @@ 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 -free_keys_i(st_data_t key, st_data_t value, void *data) +free_keys_i(st_data_t key, st_data_t value, st_data_t data) { ruby_xfree((void *)key); return ST_CONTINUE; } static int -free_values_i(st_data_t key, st_data_t value, void *data) +free_values_i(st_data_t key, st_data_t value, st_data_t data) { ruby_xfree((void *)value); return ST_CONTINUE; } +static void +allocation_info_tracer_mark(void *ptr) +{ + struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr; + rb_gc_mark(trace_arg->newobj_trace); + rb_gc_mark(trace_arg->freeobj_trace); +} + +static void +allocation_info_tracer_free(void *ptr) +{ + struct traceobj_arg *arg = (struct traceobj_arg *)ptr; + /* clear tables */ + st_foreach(arg->object_table, free_values_i, 0); + st_free_table(arg->object_table); + st_foreach(arg->str_table, free_keys_i, 0); + st_free_table(arg->str_table); + xfree(arg); +} + +static size_t +allocation_info_tracer_memsize(const void *ptr) +{ + size_t size; + struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr; + size = sizeof(*trace_arg); + size += st_memsize(trace_arg->object_table); + size += st_memsize(trace_arg->str_table); + return size; +} + +static int +allocation_info_tracer_compact_update_object_table_i(st_data_t key, st_data_t value, st_data_t data) +{ + st_table *table = (st_table *)data; + + if (!rb_gc_pointer_to_heap_p(key)) { + struct allocation_info *info = (struct allocation_info *)value; + xfree(info); + return ST_DELETE; + } + + if (key != rb_gc_location(key)) { + DURING_GC_COULD_MALLOC_REGION_START(); + { + st_insert(table, rb_gc_location(key), value); + } + DURING_GC_COULD_MALLOC_REGION_END(); + + return ST_DELETE; + } + + return ST_CONTINUE; +} + +static void +allocation_info_tracer_compact(void *ptr) +{ + struct traceobj_arg *trace_arg = (struct traceobj_arg *)ptr; + + if (trace_arg->object_table && + st_foreach( + trace_arg->object_table, + allocation_info_tracer_compact_update_object_table_i, + (st_data_t)trace_arg->object_table)) { + rb_raise(rb_eRuntimeError, "hash modified during iteration"); + } +} + +static const rb_data_type_t allocation_info_tracer_type = { + "ObjectTracing/allocation_info_tracer", + { + allocation_info_tracer_mark, + allocation_info_tracer_free, /* Never called because global */ + allocation_info_tracer_memsize, + allocation_info_tracer_compact, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY +}; + +static VALUE traceobj_arg; static struct traceobj_arg *tmp_trace_arg; /* TODO: Do not use global variables */ static int tmp_keep_remains; /* TODO: Do not use global variables */ @@ -158,13 +254,15 @@ static struct traceobj_arg * get_traceobj_arg(void) { if (tmp_trace_arg == 0) { - tmp_trace_arg = ALLOC_N(struct traceobj_arg, 1); - 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(); + 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(); } return tmp_trace_arg; } @@ -181,17 +279,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); - rb_gc_register_mark_object(arg->newobj_trace); - arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg); - rb_gc_register_mark_object(arg->freeobj_trace); - } - 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; @@ -212,12 +308,16 @@ 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) { - rb_tracepoint_disable(arg->newobj_trace); - rb_tracepoint_disable(arg->freeobj_trace); + if (arg->newobj_trace != 0) { + rb_tracepoint_disable(arg->newobj_trace); + } + if (arg->freeobj_trace != 0) { + rb_tracepoint_disable(arg->freeobj_trace); + } } return Qnil; @@ -295,8 +395,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"); @@ -308,18 +408,25 @@ 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"); } +/* + * call-seq: trace_object_allocations_debug_start + * + * Starts tracing object allocations for GC debugging. + * If you encounter the BUG "... is T_NONE" (and so on) on your + * application, please try this method at the beginning of your app. + */ static VALUE 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); @@ -329,10 +436,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; } @@ -356,10 +463,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; } } @@ -376,10 +483,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; } } @@ -407,10 +514,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; } } @@ -439,10 +546,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; } } @@ -471,10 +578,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 427d2c4e98..457ffc2789 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -12,28 +12,37 @@ **********************************************************************/ -#include <ruby/io.h> #include "internal.h" -#include <ruby/st.h> -#include <ruby/re.h> -#include "node.h" -#include "gc.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 "ruby/io.h" +#include "ruby/re.h" +#include "ruby/st.h" #include "symbol.h" +#undef rb_funcall + +#include "ruby/ruby.h" + /* * call-seq: * ObjectSpace.memsize_of(obj) -> Integer * - * Return consuming memory size of obj. + * Return consuming memory size of obj in bytes. * * Note that the return size is incomplete. You need to deal with this * information as only a *HINT*. Especially, the size of +T_DATA+ may not be * correct. * - * This method is only expected to work with C Ruby. + * This method is only expected to work with CRuby. * - * From Ruby 2.2, memsize_of(obj) returns a memory size includes - * sizeof(RVALUE). + * From Ruby 3.2 with Variable Width Allocation, it returns the actual slot + * size used plus any additional memory allocated outside the slot (such + * as external strings, arrays, or hash tables). */ static VALUE @@ -47,56 +56,76 @@ struct total_data { VALUE klass; }; +static void +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); + } + } +} + +typedef void (*each_obj_with_flags)(VALUE, void*); + +struct obj_itr { + each_obj_with_flags cb; + void *data; +}; + static int -total_i(void *vstart, void *vend, size_t stride, void *ptr) +heap_iter(void *vstart, void *vend, size_t stride, void *ptr) { + struct obj_itr * ctx = (struct obj_itr *)ptr; VALUE v; - struct total_data *data = (struct total_data *)ptr; for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) { - if (RBASIC(v)->flags) { - switch (BUILTIN_TYPE(v)) { - case T_NONE: - case T_IMEMO: - case T_ICLASS: - case T_NODE: - case T_ZOMBIE: - continue; - default: - if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) { - data->total += rb_obj_memsize_of(v); - } - } - } + void *poisoned = rb_asan_poisoned_object_p(v); + rb_asan_unpoison_object(v, false); + + if (RBASIC(v)->flags) { + (*ctx->cb)(v, ctx->data); + } + + if (poisoned) { + rb_asan_poison_object(v); + } } return 0; } +static void +each_object_with_flags(each_obj_with_flags cb, void *ctx) +{ + struct obj_itr data; + data.cb = cb; + data.data = ctx; + rb_objspace_each_objects(heap_iter, &data); +} + /* * call-seq: - * ObjectSpace.memsize_of_all([klass]) -> Integer - * - * Return consuming memory size of all living objects. + * ObjectSpace.memsize_of_all(klass = nil) -> integer * - * If +klass+ (should be Class object) is given, return the total memory size - * of instances of the given class. + * Returns the total memory size of all living objects in bytes. * - * Note that the returned size is incomplete. You need to deal with this - * information as only a *HINT*. Especially, the size of +T_DATA+ may not be - * correct. + * ObjectSpace.memsize_of_all # => 12502001 * - * Note that this method does *NOT* return total malloc'ed memory size. + * If +klass+ is given (which must be a Class or Module), returns the total + * memory size of objects whose class is, or is a subclass, of +klass+. * - * This method can be defined by the following Ruby code: + * class MyClass; end + * ObjectSpace.memsize_of_all(MyClass) # => 0 + * o = MyClass.new + * ObjectSpace.memsize_of_all(MyClass) # => 40 * - * def memsize_of_all klass = false - * total = 0 - * ObjectSpace.each_object{|e| - * total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass) - * } - * total - * end + * Note that the value returned may be an underestimate of the actual amount + * of memory used. Therefore, the value returned should only be used as a hint, + * rather than a source of truth. In particular, the size of +T_DATA+ objects may + * not be correct. * * This method is only expected to work with C Ruby. */ @@ -107,10 +136,10 @@ 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); } - rb_objspace_each_objects(total_i, &data); + each_object_with_flags(total_i, &data); return SIZET2NUM(data.total); } @@ -137,24 +166,17 @@ setup_hash(int argc, VALUE *argv) hash = rb_hash_new(); } else if (!RHASH_EMPTY_P(hash)) { - st_foreach(RHASH_TBL(hash), set_zero_i, hash); + rb_hash_foreach(hash, set_zero_i, (st_data_t)hash); } return hash; } -static int -cos_i(void *vstart, void *vend, size_t stride, void *data) +static void +cos_i(VALUE v, void *data) { size_t *counts = (size_t *)data; - VALUE v = (VALUE)vstart; - - for (;v != (VALUE)vend; v += stride) { - if (RBASIC(v)->flags) { - counts[BUILTIN_TYPE(v)] += rb_obj_memsize_of(v); - } - } - return 0; + counts[BUILTIN_TYPE(v)] += rb_obj_memsize_of(v); } static VALUE @@ -163,33 +185,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); } @@ -228,17 +250,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; } - rb_objspace_each_objects(cos_i, &counts[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; @@ -249,53 +271,47 @@ struct dynamic_symbol_counts { size_t immortal; }; -static int -cs_i(void *vstart, void *vend, size_t stride, void *n) +static void +cs_i(VALUE v, void *n) { struct dynamic_symbol_counts *counts = (struct dynamic_symbol_counts *)n; - VALUE v = (VALUE)vstart; - - for (; v != (VALUE)vend; v += stride) { - if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_SYMBOL) { - ID id = RSYMBOL(v)->id; - if ((id & ~ID_SCOPE_MASK) == 0) { - counts->mortal++; - } - else { - counts->immortal++; - } - } - } - return 0; + if (BUILTIN_TYPE(v) == T_SYMBOL) { + ID id = RSYMBOL(v)->id; + if ((id & ~ID_SCOPE_MASK) == 0) { + counts->mortal++; + } + else { + counts->immortal++; + } + } } size_t rb_sym_immortal_count(void); /* * call-seq: - * ObjectSpace.count_symbols([result_hash]) -> hash + * ObjectSpace.count_symbols(result_hash = nil) -> hash * - * Counts symbols for each Symbol type. + * Returns a hash containing the number of objects for each Symbol type. * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * The types of Symbols are the following: * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * Note: - * The contents of the returned hash is implementation defined. - * It may be changed in future. + * - +mortal_dynamic_symbol+: Symbols that are garbage collectable. + * - +immortal_dynamic_symbol+: Symbols that are objects allocated from the + * garbage collector, but are not garbage collectable. + * - +immortal_static_symbol+: Symbols that are not allocated from the + * garbage collector, and are thus not garbage collectable. + * - +immortal_symbol+: the sum of +immortal_dynamic_symbol+ and +immortal_static_symbol+. * - * This method is only expected to work with C Ruby. + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * On this version of MRI, they have 3 types of Symbols (and 1 total counts). + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * - * * mortal_dynamic_symbol: GC target symbols (collected by GC) - * * immortal_dynamic_symbol: Immortal symbols promoted from dynamic symbols (do not collected by GC) - * * immortal_static_symbol: Immortal symbols (do not collected by GC) - * * immortal_symbol: total immortal symbols (immortal_dynamic_symbol+immortal_static_symbol) + * This method is only expected to work with C Ruby. */ static VALUE @@ -305,7 +321,7 @@ count_symbols(int argc, VALUE *argv, VALUE os) VALUE hash = setup_hash(argc, argv); size_t immortal_symbols = rb_sym_immortal_count(); - rb_objspace_each_objects(cs_i, &dynamic_counts); + each_object_with_flags(cs_i, &dynamic_counts); rb_hash_aset(hash, ID2SYM(rb_intern("mortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.mortal)); rb_hash_aset(hash, ID2SYM(rb_intern("immortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.immortal)); @@ -315,239 +331,51 @@ count_symbols(int argc, VALUE *argv, VALUE os) return hash; } -static int -cn_i(void *vstart, void *vend, size_t stride, void *n) -{ - size_t *nodes = (size_t *)n; - VALUE v = (VALUE)vstart; - - for (; v != (VALUE)vend; v += stride) { - if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_NODE) { - size_t s = nd_type((NODE *)v); - nodes[s]++; - } - } - - return 0; -} - -/* - * call-seq: - * ObjectSpace.count_nodes([result_hash]) -> hash - * - * Counts nodes for each node type. - * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. - * - * It returns a hash as: - * - * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...} - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * Note: - * The contents of the returned hash is implementation defined. - * It may be changed in future. - * - * This method is only expected to work with C Ruby. - */ - -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; - } - - rb_objspace_each_objects(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_DASGN_CURR); - 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_ARRAY); - COUNT_NODE(NODE_ZARRAY); - 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_METHREF); - COUNT_NODE(NODE_ARYPTN); - COUNT_NODE(NODE_HSHPTN); -#undef COUNT_NODE - case NODE_LAST: break; - } - UNREACHABLE; - set: - rb_hash_aset(hash, node, SIZET2NUM(nodes[i])); - } - } - return hash; -} - -static int -cto_i(void *vstart, void *vend, size_t stride, void *data) +static void +cto_i(VALUE v, void *data) { VALUE hash = (VALUE)data; - VALUE v = (VALUE)vstart; - - for (; v != (VALUE)vend; v += stride) { - if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_DATA) { - VALUE counter; - VALUE key = RBASIC(v)->klass; - - if (key == 0) { - const char *name = rb_objspace_data_type_name(v); - if (name == 0) name = "unknown"; - key = ID2SYM(rb_intern(name)); - } - - counter = rb_hash_aref(hash, key); - if (NIL_P(counter)) { - counter = INT2FIX(1); - } - else { - counter = INT2FIX(FIX2INT(counter) + 1); - } - - rb_hash_aset(hash, key, counter); - } - } - return 0; + if (BUILTIN_TYPE(v) == T_DATA) { + VALUE counter; + VALUE key = RBASIC(v)->klass; + + if (key == 0) { + const char *name = rb_objspace_data_type_name(v); + if (name == 0) name = "unknown"; + key = ID2SYM(rb_intern(name)); + } + + counter = rb_hash_aref(hash, key); + if (NIL_P(counter)) { + counter = INT2FIX(1); + } + else { + counter = INT2FIX(FIX2INT(counter) + 1); + } + + rb_hash_aset(hash, key, counter); + } } /* * call-seq: - * ObjectSpace.count_tdata_objects([result_hash]) -> hash - * - * Counts objects for each +T_DATA+ type. - * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * ObjectSpace.count_tdata_objects(result_hash = nil) -> hash * - * It returns a hash as: - * - * {RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6, - * :mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99, - * ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1, - * Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2} - * # T_DATA objects existing at startup on r32276. - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. + * Returns a hash containing the number of objects for each +T_DATA+ type. + * The keys are Class objects when the +T_DATA+ object has an associated class, + * or Symbol objects of the name defined in the +rb_data_type_struct+ for internal + * +T_DATA+ objects. * - * The contents of the returned hash is implementation specific and may change - * in the future. + * ObjectSpace.count_tdata_objects + * # => {RBS::Location => 39255, marshal_compat_table: 1, Encoding => 103, mutex: 1, ... } * - * In this version, keys are Class object or Symbol object. + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * If object is kind of normal (accessible) object, the key is Class object. - * If object is not a kind of normal (internal) object, the key is symbol - * name, registered by rb_data_type_struct. + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * * This method is only expected to work with C Ruby. */ @@ -556,63 +384,52 @@ static VALUE count_tdata_objects(int argc, VALUE *argv, VALUE self) { VALUE hash = setup_hash(argc, argv); - rb_objspace_each_objects(cto_i, (void *)hash); + each_object_with_flags(cto_i, (void *)hash); return hash; } static ID imemo_type_ids[IMEMO_MASK+1]; -static int -count_imemo_objects_i(void *vstart, void *vend, size_t stride, void *data) +static void +count_imemo_objects_i(VALUE v, void *data) { VALUE hash = (VALUE)data; - VALUE v = (VALUE)vstart; - for (; v != (VALUE)vend; v += stride) { - if (RBASIC(v)->flags && BUILTIN_TYPE(v) == T_IMEMO) { - VALUE counter; - VALUE key = ID2SYM(imemo_type_ids[imemo_type(v)]); + if (BUILTIN_TYPE(v) == T_IMEMO) { + VALUE counter; + VALUE key = ID2SYM(imemo_type_ids[imemo_type(v)]); - counter = rb_hash_aref(hash, key); + counter = rb_hash_aref(hash, key); - if (NIL_P(counter)) { - counter = INT2FIX(1); - } - else { - counter = INT2FIX(FIX2INT(counter) + 1); - } + if (NIL_P(counter)) { + counter = INT2FIX(1); + } + else { + counter = INT2FIX(FIX2INT(counter) + 1); + } - rb_hash_aset(hash, key, counter); - } + rb_hash_aset(hash, key, counter); } - - return 0; } /* * call-seq: - * ObjectSpace.count_imemo_objects([result_hash]) -> hash - * - * Counts objects for each +T_IMEMO+ type. - * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * ObjectSpace.count_imemo_objects(result_hash = nil) -> hash * - * It returns a hash as: - * - * {:imemo_ifunc=>8, - * :imemo_svar=>7, - * :imemo_cref=>509, - * :imemo_memo=>1, - * :imemo_throw_data=>1} + * Returns a hash containing the number of objects for each +T_IMEMO+ type. + * The keys are Symbol objects of the +T_IMEMO+ type name. + * +T_IMEMO+ objects are Ruby internal objects that are not visible to Ruby + * programs. * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. + * ObjectSpace.count_imemo_objects + * # => {imemo_callcache: 5482, imemo_constcache: 1258, imemo_ment: 13906, ... } * - * The contents of the returned hash is implementation specific and may change - * in the future. + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * In this version, keys are symbol objects. + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * * This method is only expected to work with C Ruby. */ @@ -623,20 +440,24 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self) VALUE hash = setup_hash(argc, argv); if (imemo_type_ids[0] == 0) { - imemo_type_ids[0] = rb_intern("imemo_env"); - imemo_type_ids[1] = rb_intern("imemo_cref"); - imemo_type_ids[2] = rb_intern("imemo_svar"); - imemo_type_ids[3] = rb_intern("imemo_throw_data"); - imemo_type_ids[4] = rb_intern("imemo_ifunc"); - imemo_type_ids[5] = rb_intern("imemo_memo"); - imemo_type_ids[6] = rb_intern("imemo_ment"); - imemo_type_ids[7] = rb_intern("imemo_iseq"); - imemo_type_ids[8] = rb_intern("imemo_tmpbuf"); - imemo_type_ids[9] = rb_intern("imemo_ast"); - imemo_type_ids[10] = rb_intern("imemo_parser_strterm"); +#define INIT_IMEMO_TYPE_ID(n) (imemo_type_ids[n] = rb_intern_const(#n)) + INIT_IMEMO_TYPE_ID(imemo_env); + INIT_IMEMO_TYPE_ID(imemo_cref); + INIT_IMEMO_TYPE_ID(imemo_svar); + INIT_IMEMO_TYPE_ID(imemo_throw_data); + INIT_IMEMO_TYPE_ID(imemo_ifunc); + INIT_IMEMO_TYPE_ID(imemo_memo); + INIT_IMEMO_TYPE_ID(imemo_ment); + INIT_IMEMO_TYPE_ID(imemo_iseq); + INIT_IMEMO_TYPE_ID(imemo_tmpbuf); + INIT_IMEMO_TYPE_ID(imemo_callinfo); + INIT_IMEMO_TYPE_ID(imemo_callcache); + INIT_IMEMO_TYPE_ID(imemo_constcache); + INIT_IMEMO_TYPE_ID(imemo_fields); +#undef INIT_IMEMO_TYPE_ID } - rb_objspace_each_objects(count_imemo_objects_i, (void *)hash); + each_object_with_flags(count_imemo_objects_i, (void *)hash); return hash; } @@ -660,12 +481,12 @@ static const rb_data_type_t iow_data_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY }; -static VALUE rb_mInternalObjectWrapper; +static VALUE rb_cInternalObjectWrapper; static VALUE iow_newobj(VALUE obj) { - return TypedData_Wrap_Struct(rb_mInternalObjectWrapper, &iow_data_type, (void *)obj); + return TypedData_Wrap_Struct(rb_cInternalObjectWrapper, &iow_data_type, (void *)obj); } /* Returns the type of the internal object. */ @@ -695,8 +516,8 @@ iow_internal_object_id(VALUE self) } struct rof_data { - st_table *refs; - VALUE internals; + VALUE refs; + VALUE values; }; static void @@ -706,12 +527,16 @@ reachable_object_from_i(VALUE obj, void *data_ptr) VALUE key = obj; VALUE val = obj; - if (rb_objspace_markable_object_p(obj)) { - if (rb_objspace_internal_object_p(obj)) { - val = iow_newobj(obj); - rb_ary_push(data->internals, val); - } - st_insert(data->refs, key, val); + if (!rb_objspace_garbage_object_p(obj)) { + if (NIL_P(rb_hash_lookup(data->refs, key))) { + rb_hash_aset(data->refs, key, Qtrue); + + if (rb_objspace_internal_object_p(obj)) { + val = iow_newobj(obj); + } + + rb_ary_push(data->values, val); + } } } @@ -748,7 +573,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']) @@ -768,24 +593,22 @@ collect_values(st_data_t key, st_data_t value, st_data_t data) static VALUE reachable_objects_from(VALUE self, VALUE obj) { - if (rb_objspace_markable_object_p(obj)) { - VALUE ret = rb_ary_new(); - struct rof_data data; + if (!RB_SPECIAL_CONST_P(obj)) { + struct rof_data data; - if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { - obj = (VALUE)DATA_PTR(obj); - } + if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { + obj = (VALUE)DATA_PTR(obj); + } - data.refs = st_init_numtable(); - data.internals = rb_ary_new(); + data.refs = rb_obj_hide(rb_ident_hash_new()); + data.values = rb_ary_new(); - rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data); + rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data); - st_foreach(data.refs, collect_values, (st_data_t)ret); - return ret; + return data.values; } else { - return Qnil; + return Qnil; } } @@ -804,26 +627,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); + if (!rb_objspace_garbage_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); } } @@ -859,13 +682,14 @@ static VALUE wrap_klass_iow(VALUE klass) { if (!RTEST(klass)) { - return Qnil; + return Qnil; } - else if (RB_TYPE_P(klass, T_ICLASS)) { - return iow_newobj(klass); + else if (RB_TYPE_P(klass, T_ICLASS) || + CLASS_OF(klass) == Qfalse /* hidden object */) { + return iow_newobj(klass); } else { - return klass; + return klass; } } @@ -884,11 +708,16 @@ 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); } - klass = CLASS_OF(obj); - return wrap_klass_iow(klass); + if (RB_TYPE_P(obj, T_IMEMO)) { + return Qnil; + } + else { + klass = CLASS_OF(obj); + return wrap_klass_iow(klass); + } } /* @@ -906,17 +735,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 = rb_class_super_of(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); @@ -934,7 +763,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. @@ -955,7 +784,6 @@ Init_objspace(void) rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1); rb_define_module_function(rb_mObjSpace, "count_symbols", count_symbols, -1); - rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1); rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1); rb_define_module_function(rb_mObjSpace, "count_imemo_objects", count_imemo_objects, -1); @@ -974,10 +802,11 @@ Init_objspace(void) * * You can use the #type method to check the type of the internal object. */ - rb_mInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject); - rb_define_method(rb_mInternalObjectWrapper, "type", iow_type, 0); - rb_define_method(rb_mInternalObjectWrapper, "inspect", iow_inspect, 0); - rb_define_method(rb_mInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0); + rb_cInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject); + rb_undef_alloc_func(rb_cInternalObjectWrapper); + rb_define_method(rb_cInternalObjectWrapper, "type", iow_type, 0); + rb_define_method(rb_cInternalObjectWrapper, "inspect", iow_inspect, 0); + rb_define_method(rb_cInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0); Init_object_tracing(rb_mObjSpace); Init_objspace_dump(rb_mObjSpace); diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c index 7dcee589bf..da64698346 100644 --- a/ext/objspace/objspace_dump.c +++ b/ext/objspace/objspace_dump.c @@ -12,46 +12,189 @@ **********************************************************************/ -#include "ruby/io.h" +#include "id_table.h" #include "internal.h" -#include "ruby/debug.h" -#include "gc.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 "vm_core.h" #include "objspace.h" +#include "ruby/debug.h" +#include "ruby/util.h" +#include "ruby/io.h" +#include "vm_callinfo.h" +#include "vm_sync.h" + +RUBY_EXTERN const char ruby_hexdigits[]; -static VALUE sym_output, sym_stdout, sym_string, sym_file; -static VALUE sym_full; +#define BUFFER_CAPACITY 4096 struct dump_config { - VALUE type; - FILE *stream; + VALUE given_output; + VALUE output_io; VALUE string; + FILE *stream; const char *root_category; VALUE cur_obj; VALUE cur_obj_klass; + size_t cur_page_slot_size; size_t cur_obj_references; unsigned int roots: 1; unsigned int full_heap: 1; + unsigned int partial_dump; + size_t since; + size_t shapes_since; + unsigned long buffer_len; + char buffer[BUFFER_CAPACITY]; }; -PRINTF_ARGS(static void dump_append(struct dump_config *, const char *, ...), 2, 3); static void -dump_append(struct dump_config *dc, const char *format, ...) +dump_flush(struct dump_config *dc) { - va_list vl; - va_start(vl, format); + if (dc->buffer_len) { + if (dc->stream) { + size_t written = fwrite(dc->buffer, sizeof(dc->buffer[0]), dc->buffer_len, dc->stream); + if (written < dc->buffer_len) { + MEMMOVE(dc->buffer, dc->buffer + written, char, dc->buffer_len - written); + dc->buffer_len -= written; + return; + } + } + else if (dc->string) { + rb_str_cat(dc->string, dc->buffer, dc->buffer_len); + } + dc->buffer_len = 0; + } +} - if (dc->stream) { - vfprintf(dc->stream, format, vl); +static inline void +buffer_ensure_capa(struct dump_config *dc, unsigned long requested) +{ + RUBY_ASSERT(requested <= BUFFER_CAPACITY); + if (requested + dc->buffer_len >= BUFFER_CAPACITY) { + dump_flush(dc); + if (requested + dc->buffer_len >= BUFFER_CAPACITY) { + rb_raise(rb_eIOError, "full buffer"); + } + } +} + +static void +buffer_append(struct dump_config *dc, const char *cstr, unsigned long len) +{ + if (LIKELY(len > 0)) { + buffer_ensure_capa(dc, len); + MEMCPY(dc->buffer + dc->buffer_len, cstr, char, len); + dc->buffer_len += len; + } +} + +# define dump_append(dc, str) buffer_append(dc, (str), (long)strlen(str)) + +static void +dump_append_ld(struct dump_config *dc, const long number) +{ + const unsigned int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT - 1) + 2; + buffer_ensure_capa(dc, width); + unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "%ld", number); + RUBY_ASSERT(required <= width); + dc->buffer_len += required; +} + +static void +dump_append_lu(struct dump_config *dc, const unsigned long number) +{ + const unsigned int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT) + 1; + buffer_ensure_capa(dc, width); + unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "%lu", number); + RUBY_ASSERT(required <= width); + dc->buffer_len += required; +} + +static void +dump_append_g(struct dump_config *dc, const double number) +{ + unsigned long capa_left = BUFFER_CAPACITY - dc->buffer_len; + unsigned long required = snprintf(dc->buffer + dc->buffer_len, capa_left, "%#g", number); + + if (required >= capa_left) { + buffer_ensure_capa(dc, required); + capa_left = BUFFER_CAPACITY - dc->buffer_len; + snprintf(dc->buffer + dc->buffer_len, capa_left, "%#g", number); + } + dc->buffer_len += required; +} + +static void +dump_append_d(struct dump_config *dc, const int number) +{ + const unsigned int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT - 1) + 2; + buffer_ensure_capa(dc, width); + unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "%d", number); + RUBY_ASSERT(required <= width); + dc->buffer_len += required; +} + +static void +dump_append_sizet(struct dump_config *dc, const size_t number) +{ + const unsigned int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT) + 1; + buffer_ensure_capa(dc, width); + unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "%"PRIuSIZE, number); + RUBY_ASSERT(required <= width); + dc->buffer_len += required; +} + +static void +dump_append_c(struct dump_config *dc, unsigned char c) +{ + if (c <= 0x1f) { + 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); + dc->buffer_len += required; + } + else { + buffer_ensure_capa(dc, 1); + dc->buffer[dc->buffer_len] = c; + dc->buffer_len++; } - else if (dc->string) - rb_str_vcatf(dc->string, format, vl); +} - va_end(vl); +static void +dump_append_ptr(struct dump_config *dc, VALUE ref) +{ + char buffer[roomof(sizeof(VALUE) * CHAR_BIT, 4) + rb_strlen_lit("\"0x\"")]; + char *buffer_start, *buffer_end; + + buffer_start = buffer_end = &buffer[sizeof(buffer)]; + *--buffer_start = '"'; + while (ref) { + *--buffer_start = ruby_hexdigits[ref & 0xF]; + ref >>= 4; + } + *--buffer_start = 'x'; + *--buffer_start = '0'; + *--buffer_start = '"'; + buffer_append(dc, buffer_start, buffer_end - buffer_start); } 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; @@ -60,35 +203,37 @@ dump_append_string_value(struct dump_config *dc, VALUE obj) dump_append(dc, "\""); for (i = 0, value = RSTRING_PTR(obj); i < RSTRING_LEN(obj); i++) { - switch ((c = value[i])) { - case '\\': - case '"': - dump_append(dc, "\\%c", c); - break; - case '\0': - dump_append(dc, "\\u0000"); - break; - case '\b': - dump_append(dc, "\\b"); - break; - case '\t': - dump_append(dc, "\\t"); - break; - case '\f': - dump_append(dc, "\\f"); - break; - case '\n': - dump_append(dc, "\\n"); - break; - case '\r': - dump_append(dc, "\\r"); - break; - default: - if (c <= 0x1f) - dump_append(dc, "\\u%04d", c); - else - dump_append(dc, "%c", c); - } + switch ((c = value[i])) { + case '\\': + dump_append(dc, "\\\\"); + break; + case '"': + dump_append(dc, "\\\""); + break; + case '\0': + dump_append(dc, "\\u0000"); + break; + case '\b': + dump_append(dc, "\\b"); + break; + case '\t': + dump_append(dc, "\\t"); + break; + case '\f': + dump_append(dc, "\\f"); + break; + case '\n': + dump_append(dc, "\\n"); + break; + case '\r': + dump_append(dc, "\\r"); + break; + case '\177': + dump_append(dc, "\\u007f"); + break; + default: + dump_append_c(dc, c); + } } dump_append(dc, "\""); } @@ -106,33 +251,34 @@ 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; } return "UNKNOWN"; } @@ -141,25 +287,25 @@ static void dump_append_special_const(struct dump_config *dc, VALUE value) { if (value == Qtrue) { - dump_append(dc, "true"); + dump_append(dc, "true"); } else if (value == Qfalse) { - dump_append(dc, "false"); + dump_append(dc, "false"); } else if (value == Qnil) { - dump_append(dc, "null"); + dump_append(dc, "null"); } else if (FIXNUM_P(value)) { - dump_append(dc, "%ld", FIX2LONG(value)); + dump_append_ld(dc, FIX2LONG(value)); } else if (FLONUM_P(value)) { - dump_append(dc, "%#g", RFLOAT_VALUE(value)); + dump_append_g(dc, RFLOAT_VALUE(value)); } else if (SYMBOL_P(value)) { - dump_append_symbol_value(dc, value); + dump_append_symbol_value(dc, value); } else { - dump_append(dc, "{}"); + dump_append(dc, "{}"); } } @@ -169,175 +315,352 @@ reachable_object_i(VALUE ref, void *data) struct dump_config *dc = (struct dump_config *)data; if (dc->cur_obj_klass == ref) - return; + return; - if (dc->cur_obj_references == 0) - dump_append(dc, ", \"references\":[\"%#"PRIxVALUE"\"", ref); - else - dump_append(dc, ", \"%#"PRIxVALUE"\"", ref); + if (dc->cur_obj_references == 0) { + dump_append(dc, ", \"references\":["); + dump_append_ref(dc, ref); + } + else { + dump_append(dc, ", "); + dump_append_ref(dc, ref); + } 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) { - dump_append(dc, ", \"bytesize\":%ld", RSTRING_LEN(obj)); - if (!STR_EMBED_P(obj) && !STR_SHARED_P(obj) && (long)rb_str_capacity(obj) != RSTRING_LEN(obj)) - dump_append(dc, ", \"capacity\":%"PRIuSIZE, rb_str_capacity(obj)); + dump_append(dc, ", \"bytesize\":"); + dump_append_ld(dc, RSTRING_LEN(obj)); + if (!STR_EMBED_P(obj) && !STR_SHARED_P(obj) && (long)rb_str_capacity(obj) != RSTRING_LEN(obj)) { + dump_append(dc, ", \"capacity\":"); + 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 const char * -imemo_name(int imemo) +static inline void +dump_append_id(struct dump_config *dc, ID id) { - switch(imemo) { -#define TYPE_STR(t) case(imemo_##t): return #t - TYPE_STR(env); - TYPE_STR(cref); - TYPE_STR(svar); - TYPE_STR(throw_data); - TYPE_STR(ifunc); - TYPE_STR(memo); - TYPE_STR(ment); - TYPE_STR(iseq); - TYPE_STR(tmpbuf); - TYPE_STR(ast); - TYPE_STR(parser_strterm); - default: - return "unknown"; -#undef TYPE_STR + 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) { size_t memsize; - struct allocation_info *ainfo; + struct allocation_info *ainfo = objspace_lookup_allocation_info(obj); 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); - return; + dump_append_special_const(dc, obj); + return; } 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 && !IMEMO_TYPE_P(obj, imemo_fields))) { + dc->cur_obj_klass = 0; + } + else { + dc->cur_obj_klass = RBASIC_CLASS(obj); + } + + if (dc->partial_dump && (!ainfo || ainfo->generation < dc->since)) { + return; + } if (dc->cur_obj == dc->string) - return; + return; + + dump_append(dc, "{\"address\":"); + dump_append_ref(dc, obj); + + dump_append(dc, ", \"type\":\""); + dump_append(dc, obj_type(obj)); + dump_append(dc, "\""); + + if (BUILTIN_TYPE(obj) != T_IMEMO || IMEMO_TYPE_P(obj, imemo_fields)) { + size_t shape_id = rb_obj_shape_id(obj) & SHAPE_ID_OFFSET_MASK; + dump_append(dc, ", \"shape_id\":"); + dump_append_sizet(dc, shape_id); + } - dump_append(dc, "{\"address\":\"%#"PRIxVALUE"\", \"type\":\"%s\"", obj, obj_type(obj)); + dump_append(dc, ", \"slot_size\":"); + dump_append_sizet(dc, dc->cur_page_slot_size); - if (dc->cur_obj_klass) - dump_append(dc, ", \"class\":\"%#"PRIxVALUE"\"", dc->cur_obj_klass); + if (dc->cur_obj_klass) { + dump_append(dc, ", \"class\":"); + dump_append_ref(dc, dc->cur_obj_klass); + } if (rb_obj_frozen_p(obj)) - dump_append(dc, ", \"frozen\":true"); + dump_append(dc, ", \"frozen\":true"); switch (BUILTIN_TYPE(obj)) { case T_NONE: - dump_append(dc, "}\n"); - return; + dump_append(dc, "}\n"); + return; case T_IMEMO: - dump_append(dc, ", \"imemo_type\":\"%s\"", imemo_name(imemo_type(obj))); - break; + 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: + { + VALUE klass = ((const struct rb_callcache *)obj)->klass; + if (klass != Qundef) { + mid = vm_cc_cme((const struct rb_callcache *)obj)->called_id; + if (mid != 0) { + dump_append(dc, ", \"called_id\":"); + dump_append_id(dc, mid); + + } + + dump_append(dc, ", \"receiver_class\":"); + dump_append_ref(dc, klass); + } + } + break; + + default: + break; + } + break; case T_SYMBOL: - dump_append_string_content(dc, rb_sym2str(obj)); - break; + dump_append_string_content(dc, rb_sym2str(obj)); + break; 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 (STR_SHARED_P(obj)) - dump_append(dc, ", \"shared\":true"); - else - dump_append_string_content(dc, obj); - - if (!ENCODING_IS_ASCII8BIT(obj)) - dump_append(dc, ", \"encoding\":\"%s\"", rb_enc_name(rb_enc_from_index(ENCODING_GET(obj)))); - break; + if (STR_EMBED_P(obj)) + dump_append(dc, ", \"embedded\":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 + dump_append_string_content(dc, obj); + + if (!ENCODING_IS_ASCII8BIT(obj)) { + dump_append(dc, ", \"encoding\":\""); + 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: - dump_append(dc, ", \"size\":%"PRIuSIZE, (size_t)RHASH_SIZE(obj)); - if (FL_TEST(obj, HASH_PROC_DEFAULT)) - dump_append(dc, ", \"default\":\"%#"PRIxVALUE"\"", RHASH_IFNONE(obj)); - break; + dump_append(dc, ", \"size\":"); + dump_append_sizet(dc, (size_t)RHASH_SIZE(obj)); + if (FL_TEST(obj, RHASH_PROC_DEFAULT)) { + dump_append(dc, ", \"default\":"); + dump_append_ref(dc, RHASH_IFNONE(obj)); + } + break; case T_ARRAY: - dump_append(dc, ", \"length\":%ld", RARRAY_LEN(obj)); - if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED)) - dump_append(dc, ", \"shared\":true"); - if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_EMBED_FLAG)) - dump_append(dc, ", \"embedded\":true"); - break; + dump_append(dc, ", \"length\":"); + dump_append_ld(dc, RARRAY_LEN(obj)); + 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"); + break; + + case T_ICLASS: + if (rb_class_get_superclass(obj)) { + dump_append(dc, ", \"superclass\":"); + dump_append_ref(dc, rb_class_get_superclass(obj)); + } + break; case T_CLASS: + dump_append(dc, ", \"variation_count\":"); + dump_append_d(dc, rb_class_variation_count(obj)); + case T_MODULE: - if (dc->cur_obj_klass) - dump_append(dc, ", \"name\":\"%s\"", rb_class2name(obj)); - break; + if (rb_class_get_superclass(obj)) { + dump_append(dc, ", \"superclass\":"); + dump_append_ref(dc, rb_class_get_superclass(obj)); + } + + if (dc->cur_obj_klass) { + VALUE mod_name = rb_mod_name(obj); + if (!NIL_P(mod_name)) { + dump_append(dc, ", \"name\":"); + dump_append_string_value(dc, mod_name); + } + else { + VALUE real_mod_name = rb_mod_name(rb_class_real(obj)); + if (RTEST(real_mod_name)) { + dump_append(dc, ", \"real_class_name\":\""); + dump_append(dc, RSTRING_PTR(real_mod_name)); + dump_append(dc, "\""); + } + } + + if (rb_class_singleton_p(obj)) { + dump_append(dc, ", \"singleton\":true"); + } + } + break; case T_DATA: - if (RTYPEDDATA_P(obj)) - dump_append(dc, ", \"struct\":\"%s\"", RTYPEDDATA_TYPE(obj)->wrap_struct_name); - break; + if (RTYPEDDATA_P(obj)) { + dump_append(dc, ", \"struct\":\""); + dump_append(dc, RTYPEDDATA_TYPE(obj)->wrap_struct_name); + dump_append(dc, "\""); + } + break; case T_FLOAT: - dump_append(dc, ", \"value\":\"%g\"", RFLOAT_VALUE(obj)); - break; + dump_append(dc, ", \"value\":\""); + dump_append_g(dc, RFLOAT_VALUE(obj)); + dump_append(dc, "\""); + break; case T_OBJECT: - dump_append(dc, ", \"ivars\":%u", ROBJECT_NUMIV(obj)); - break; + if (!FL_TEST_RAW(obj, ROBJECT_HEAP)) { + dump_append(dc, ", \"embedded\":true"); + } + + dump_append(dc, ", \"ivars\":"); + dump_append_lu(dc, ROBJECT_FIELDS_COUNT(obj)); + if (rb_shape_obj_too_complex_p(obj)) { + dump_append(dc, ", \"too_complex_shape\":true"); + } + break; case T_FILE: - fptr = RFILE(obj)->fptr; - if (fptr) - dump_append(dc, ", \"fd\":%d", fptr->fd); - break; + fptr = RFILE(obj)->fptr; + if (fptr) { + dump_append(dc, ", \"fd\":"); + dump_append_d(dc, fptr->fd); + } + break; case T_ZOMBIE: - dump_append(dc, "}\n"); - return; + dump_append(dc, "}\n"); + return; + + default: + break; } rb_objspace_reachable_objects_from(obj, reachable_object_i, dc); if (dc->cur_obj_references > 0) - dump_append(dc, "]"); + dump_append(dc, "]"); + + if (ainfo) { + if (ainfo->path) { + dump_append(dc, ", \"file\":\""); + dump_append(dc, ainfo->path); + dump_append(dc, "\""); + } + if (ainfo->line) { + dump_append(dc, ", \"line\":"); + dump_append_lu(dc, ainfo->line); + } + if (RTEST(ainfo->mid)) { + VALUE m = rb_sym2str(ainfo->mid); + dump_append(dc, ", \"method\":"); + dump_append_string_value(dc, m); + } + dump_append(dc, ", \"generation\":"); + dump_append_sizet(dc, ainfo->generation); + } - if ((ainfo = objspace_lookup_allocation_info(obj))) { - dump_append(dc, ", \"file\":\"%s\", \"line\":%lu", ainfo->path, ainfo->line); - if (RTEST(ainfo->mid)) { - VALUE m = rb_sym2str(ainfo->mid); - dump_append(dc, ", \"method\":\"%s\"", RSTRING_PTR(m)); - } - dump_append(dc, ", \"generation\":%"PRIuSIZE, ainfo->generation); + if ((memsize = rb_obj_memsize_of(obj)) > 0) { + dump_append(dc, ", \"memsize\":"); + dump_append_sizet(dc, memsize); } - if ((memsize = rb_obj_memsize_of(obj)) > 0) - dump_append(dc, ", \"memsize\":%"PRIuSIZE, memsize); + struct rb_gc_object_metadata_entry *gc_metadata = rb_gc_object_metadata(obj); + for (int i = 0; gc_metadata[i].name != 0; i++) { + if (i == 0) { + dump_append(dc, ", \"flags\":{"); + } + else { + dump_append(dc, ", "); + } + + dump_append(dc, "\""); + dump_append(dc, rb_id2name(gc_metadata[i].name)); + dump_append(dc, "\":"); + dump_append_special_const(dc, gc_metadata[i].val); + } - if ((n = rb_obj_gc_flags(obj, flags, sizeof(flags))) > 0) { - dump_append(dc, ", \"flags\":{"); - for (i=0; i<n; i++) { - dump_append(dc, "\"%s\":true", rb_id2name(flags[i])); - if (i != n-1) dump_append(dc, ", "); - } - dump_append(dc, "}"); + /* If rb_gc_object_metadata had any entries, we need to close the opening + * `"flags":{`. */ + if (gc_metadata[0].name != 0) { + dump_append(dc, "}"); } dump_append(dc, "}\n"); @@ -349,8 +672,16 @@ heap_i(void *vstart, void *vend, size_t stride, void *data) struct dump_config *dc = (struct dump_config *)data; VALUE v = (VALUE)vstart; for (; v != (VALUE)vend; v += stride) { - if (dc->full_heap || RBASIC(v)->flags) - dump_object(v, dc); + void *ptr = rb_asan_poisoned_object_p(v); + rb_asan_unpoison_object(v, false); + dc->cur_page_slot_size = stride; + + if (dc->full_heap || RBASIC(v)->flags) + dump_object(v, dc); + + if (ptr) { + rb_asan_poison_object(v); + } } return 0; } @@ -361,138 +692,222 @@ root_obj_i(const char *category, VALUE obj, void *data) struct dump_config *dc = (struct dump_config *)data; if (dc->root_category != NULL && category != dc->root_category) - dump_append(dc, "]}\n"); - if (dc->root_category == NULL || category != dc->root_category) - dump_append(dc, "{\"type\":\"ROOT\", \"root\":\"%s\", \"references\":[\"%#"PRIxVALUE"\"", category, obj); - else - dump_append(dc, ", \"%#"PRIxVALUE"\"", obj); + dump_append(dc, "]}\n"); + if (dc->root_category == NULL || category != dc->root_category) { + dump_append(dc, "{\"type\":\"ROOT\", \"root\":\""); + dump_append(dc, category); + dump_append(dc, "\", \"references\":["); + dump_append_ref(dc, obj); + } + else { + dump_append(dc, ", "); + dump_append_ref(dc, obj); + } dc->root_category = category; dc->roots = 1; } -static VALUE -dump_output(struct dump_config *dc, VALUE opts, VALUE output, const char *filename) +static void +dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE shapes) { - VALUE tmp; - + dc->given_output = output; dc->full_heap = 0; + dc->buffer_len = 0; - if (RTEST(opts)) { - output = rb_hash_aref(opts, sym_output); - - if (Qtrue == rb_hash_lookup2(opts, sym_full, Qfalse)) - dc->full_heap = 1; - } - - if (output == sym_stdout) { - dc->stream = stdout; - dc->string = Qnil; + if (TYPE(output) == T_STRING) { + dc->stream = NULL; + dc->string = output; } - else if (output == sym_file) { - rb_io_t *fptr; - rb_require("tempfile"); - tmp = rb_assoc_new(rb_str_new_cstr(filename), rb_str_new_cstr(".json")); - tmp = rb_funcallv(rb_path2class("Tempfile"), rb_intern("create"), 1, &tmp); - io: - dc->string = rb_io_get_write_io(tmp); - rb_io_flush(dc->string); - GetOpenFile(dc->string, fptr); - dc->stream = rb_io_stdio_file(fptr); + else { + rb_io_t *fptr; + // Output should be an IO, typecheck and get a FILE* for writing. + // We cannot write with the usual IO code here because writes + // interleave with calls to rb_gc_mark(). The usual IO code can + // cause a thread switch, raise exceptions, and even run arbitrary + // ruby code through the fiber scheduler. + // + // Mark functions generally can't handle these possibilities so + // the usual IO code is unsafe in this context. (For example, + // there are many ways to crash when ruby code runs and mutates + // the execution context while rb_execution_context_mark() is in + // progress.) + // + // Using FILE* isn't perfect, but it avoids the most acute problems. + output = rb_io_get_io(output); + dc->output_io = rb_io_get_write_io(output); + rb_io_flush(dc->output_io); + GetOpenFile(dc->output_io, fptr); + dc->stream = rb_io_stdio_file(fptr); + dc->string = Qfalse; } - else if (output == sym_string) { - dc->string = rb_str_new_cstr(""); + + if (full == Qtrue) { + dc->full_heap = 1; } - else if (!NIL_P(tmp = rb_io_check_io(output))) { - output = sym_file; - goto io; + + if (RTEST(since)) { + dc->partial_dump = 1; + dc->since = NUM2SIZET(since); } else { - rb_raise(rb_eArgError, "wrong output option: %"PRIsVALUE, output); + dc->partial_dump = 0; } - return output; + + dc->shapes_since = RTEST(shapes) ? NUM2SIZET(shapes) : 0; } static VALUE -dump_result(struct dump_config *dc, VALUE output) +dump_result(struct dump_config *dc) { - if (output == sym_string) { - return rb_str_resurrect(dc->string); - } - else if (output == sym_file) { - rb_io_flush(dc->string); - return dc->string; + dump_flush(dc); + + if (dc->stream) { + fflush(dc->stream); } - else { - return Qnil; + if (dc->string) { + return dc->string; } + return dc->given_output; } -/* - * 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. - * - * 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. - */ - static VALUE -objspace_dump(int argc, VALUE *argv, VALUE os) +dump_locked(void *args_p) { - static const char filename[] = "rubyobj"; - VALUE obj = Qnil, opts = Qnil, output; struct dump_config dc = {0,}; + VALUE obj = ((VALUE*)args_p)[0]; + VALUE output = ((VALUE*)args_p)[1]; - rb_scan_args(argc, argv, "1:", &obj, &opts); - - output = dump_output(&dc, opts, sym_string, filename); + if (!RB_SPECIAL_CONST_P(obj)) { + dc.cur_page_slot_size = rb_gc_obj_slot_size(obj); + } + dump_output(&dc, output, Qnil, Qnil, Qnil); dump_object(obj, &dc); - return dump_result(&dc, output); -} - -/* - * 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> - * - * Dump the contents of the ruby heap as JSON. - * - * 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. - */ + return dump_result(&dc); +} + +/* :nodoc: */ +static VALUE +objspace_dump(VALUE os, VALUE obj, VALUE output) +{ + VALUE args[2]; + args[0] = obj; + args[1] = output; + return rb_vm_lock_with_barrier(dump_locked, (void*)args); +} + +static void +shape_id_i(shape_id_t shape_id, void *data) +{ + struct dump_config *dc = (struct dump_config *)data; + + if (shape_id < dc->shapes_since) { + return; + } + + dump_append(dc, "{\"address\":"); + dump_append_ref(dc, (VALUE)RSHAPE(shape_id)); + + dump_append(dc, ", \"type\":\"SHAPE\", \"id\":"); + dump_append_sizet(dc, shape_id); + + if (RSHAPE_TYPE(shape_id) != SHAPE_ROOT) { + dump_append(dc, ", \"parent_id\":"); + dump_append_lu(dc, RSHAPE_PARENT_RAW_ID(shape_id)); + } + + dump_append(dc, ", \"depth\":"); + dump_append_sizet(dc, rb_shape_depth(shape_id)); + + switch (RSHAPE_TYPE(shape_id)) { + case SHAPE_ROOT: + dump_append(dc, ", \"shape_type\":\"ROOT\""); + break; + case SHAPE_IVAR: + dump_append(dc, ", \"shape_type\":\"IVAR\""); + + dump_append(dc, ",\"edge_name\":"); + dump_append_id(dc, RSHAPE_EDGE_NAME(shape_id)); + + break; + case SHAPE_OBJ_ID: + dump_append(dc, ", \"shape_type\":\"OBJ_ID\""); + break; + } + + dump_append(dc, ", \"edges\":"); + dump_append_sizet(dc, rb_shape_edges_count(shape_id)); + + dump_append(dc, ", \"memsize\":"); + dump_append_sizet(dc, rb_shape_memsize(shape_id)); + + dump_append(dc, "}\n"); +} static VALUE -objspace_dump_all(int argc, VALUE *argv, VALUE os) +dump_all_locked(void *args_p) { - static const char filename[] = "rubyheap"; - VALUE opts = Qnil, output; struct dump_config dc = {0,}; + VALUE output = ((VALUE*)args_p)[0]; + VALUE full = ((VALUE*)args_p)[1]; + VALUE since = ((VALUE*)args_p)[2]; + VALUE shapes = ((VALUE*)args_p)[3]; - rb_scan_args(argc, argv, "0:", &opts); + dump_output(&dc, output, full, since, shapes); - output = dump_output(&dc, opts, sym_file, filename); + if (!dc.partial_dump || dc.since == 0) { + /* dump roots */ + rb_objspace_reachable_objects_from_root(root_obj_i, &dc); + if (dc.roots) dump_append(&dc, "]}\n"); + } - /* dump roots */ - rb_objspace_reachable_objects_from_root(root_obj_i, &dc); - if (dc.roots) dump_append(&dc, "]}\n"); + if (RTEST(shapes)) { + rb_shape_each_shape_id(shape_id_i, &dc); + } /* dump all objects */ rb_objspace_each_objects(heap_i, &dc); - return dump_result(&dc, output); + return dump_result(&dc); +} + +/* :nodoc: */ +static VALUE +objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since, VALUE shapes) +{ + VALUE args[4]; + args[0] = output; + args[1] = full; + args[2] = since; + args[3] = shapes; + return rb_vm_lock_with_barrier(dump_all_locked, (void*)args); +} + +static VALUE +dump_shapes_locked(void *args_p) +{ + struct dump_config dc = {0,}; + VALUE output = ((VALUE*)args_p)[0]; + VALUE shapes = ((VALUE*)args_p)[1]; + + dump_output(&dc, output, Qfalse, Qnil, shapes); + + if (RTEST(shapes)) { + rb_shape_each_shape_id(shape_id_i, &dc); + } + return dump_result(&dc); +} + +/* :nodoc: */ +static VALUE +objspace_dump_shapes(VALUE os, VALUE output, VALUE shapes) +{ + VALUE args[2]; + args[0] = output; + args[1] = shapes; + return rb_vm_lock_with_barrier(dump_shapes_locked, (void*)args); } void @@ -502,16 +917,11 @@ Init_objspace_dump(VALUE rb_mObjSpace) #if 0 rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */ #endif +#ifdef HAVE_RB_EXT_RACTOR_SAFE + RB_EXT_RACTOR_SAFE(true); +#endif - rb_define_module_function(rb_mObjSpace, "dump", objspace_dump, -1); - rb_define_module_function(rb_mObjSpace, "dump_all", objspace_dump_all, -1); - - sym_output = ID2SYM(rb_intern("output")); - sym_stdout = ID2SYM(rb_intern("stdout")); - sym_string = ID2SYM(rb_intern("string")); - sym_file = ID2SYM(rb_intern("file")); - sym_full = ID2SYM(rb_intern("full")); - - /* force create static IDs */ - rb_obj_gc_flags(rb_mObjSpace, 0, 0); + rb_define_module_function(rb_mObjSpace, "_dump", objspace_dump, 2); + rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 4); + rb_define_module_function(rb_mObjSpace, "_dump_shapes", objspace_dump_shapes, 2); } |
