summaryrefslogtreecommitdiff
path: root/ext/objspace/objspace_dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/objspace/objspace_dump.c')
-rw-r--r--ext/objspace/objspace_dump.c476
1 files changed, 387 insertions, 89 deletions
diff --git a/ext/objspace/objspace_dump.c b/ext/objspace/objspace_dump.c
index 544d11e254..68ada01af3 100644
--- a/ext/objspace/objspace_dump.c
+++ b/ext/objspace/objspace_dump.c
@@ -12,34 +12,44 @@
**********************************************************************/
-#include "gc.h"
+#include "id_table.h"
#include "internal.h"
+#include "internal/array.h"
+#include "internal/class.h"
+#include "internal/gc.h"
#include "internal/hash.h"
+#include "internal/io.h"
#include "internal/string.h"
#include "internal/sanitizers.h"
+#include "symbol.h"
+#include "shape.h"
#include "node.h"
#include "objspace.h"
#include "ruby/debug.h"
#include "ruby/util.h"
#include "ruby/io.h"
-#include "vm_core.h"
+#include "vm_callinfo.h"
+#include "vm_sync.h"
RUBY_EXTERN const char ruby_hexdigits[];
#define BUFFER_CAPACITY 4096
struct dump_config {
- VALUE type;
- VALUE 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];
};
@@ -49,7 +59,7 @@ dump_flush(struct dump_config *dc)
{
if (dc->buffer_len) {
if (dc->stream) {
- size_t written = rb_io_bufwrite(dc->stream, dc->buffer, dc->buffer_len);
+ 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;
@@ -75,7 +85,8 @@ buffer_ensure_capa(struct dump_config *dc, unsigned long requested)
}
}
-static void buffer_append(struct dump_config *dc, const char *cstr, unsigned long len)
+static void
+buffer_append(struct dump_config *dc, const char *cstr, unsigned long len)
{
if (LIKELY(len > 0)) {
buffer_ensure_capa(dc, len);
@@ -89,7 +100,7 @@ static void buffer_append(struct dump_config *dc, const char *cstr, unsigned lon
static void
dump_append_ld(struct dump_config *dc, const long number)
{
- const int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT - 1) + 2;
+ 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);
@@ -99,7 +110,7 @@ dump_append_ld(struct dump_config *dc, const long number)
static void
dump_append_lu(struct dump_config *dc, const unsigned long number)
{
- const int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT) + 1;
+ 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);
@@ -123,7 +134,7 @@ dump_append_g(struct dump_config *dc, const double number)
static void
dump_append_d(struct dump_config *dc, const int number)
{
- const int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT - 1) + 2;
+ 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);
@@ -133,7 +144,7 @@ dump_append_d(struct dump_config *dc, const int number)
static void
dump_append_sizet(struct dump_config *dc, const size_t number)
{
- const int width = DECIMAL_SIZE_OF_BITS(sizeof(number) * CHAR_BIT) + 1;
+ 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);
@@ -141,10 +152,10 @@ dump_append_sizet(struct dump_config *dc, const size_t number)
}
static void
-dump_append_c(struct dump_config *dc, char c)
+dump_append_c(struct dump_config *dc, unsigned char c)
{
if (c <= 0x1f) {
- const int width = (sizeof(c) * CHAR_BIT / 4) + 5;
+ const unsigned int width = rb_strlen_lit("\\u0000") + 1;
buffer_ensure_capa(dc, width);
unsigned long required = snprintf(dc->buffer + dc->buffer_len, width, "\\u00%02x", c);
RUBY_ASSERT(required <= width);
@@ -158,11 +169,9 @@ dump_append_c(struct dump_config *dc, char c)
}
static void
-dump_append_ref(struct dump_config *dc, VALUE ref)
+dump_append_ptr(struct dump_config *dc, VALUE ref)
{
- RUBY_ASSERT(ref > 0);
-
- char buffer[((sizeof(VALUE) * CHAR_BIT + 3) / 4) + 4];
+ char buffer[roomof(sizeof(VALUE) * CHAR_BIT, 4) + rb_strlen_lit("\"0x\"")];
char *buffer_start, *buffer_end;
buffer_start = buffer_end = &buffer[sizeof(buffer)];
@@ -178,6 +187,14 @@ dump_append_ref(struct dump_config *dc, VALUE ref)
}
static void
+dump_append_ref(struct dump_config *dc, VALUE ref)
+{
+ RUBY_ASSERT(ref > 0);
+ dump_append_ptr(dc, ref);
+}
+
+
+static void
dump_append_string_value(struct dump_config *dc, VALUE obj)
{
long i;
@@ -234,32 +251,32 @@ obj_type(VALUE obj)
{
switch (BUILTIN_TYPE(obj)) {
#define CASE_TYPE(type) case T_##type: return #type
- CASE_TYPE(NONE);
- CASE_TYPE(NIL);
- CASE_TYPE(OBJECT);
- CASE_TYPE(CLASS);
- CASE_TYPE(ICLASS);
- CASE_TYPE(MODULE);
- CASE_TYPE(FLOAT);
- CASE_TYPE(STRING);
- CASE_TYPE(REGEXP);
- CASE_TYPE(ARRAY);
- CASE_TYPE(HASH);
- CASE_TYPE(STRUCT);
- CASE_TYPE(BIGNUM);
- CASE_TYPE(FILE);
- CASE_TYPE(FIXNUM);
- CASE_TYPE(TRUE);
- CASE_TYPE(FALSE);
- CASE_TYPE(DATA);
- CASE_TYPE(MATCH);
- CASE_TYPE(SYMBOL);
- CASE_TYPE(RATIONAL);
- CASE_TYPE(COMPLEX);
- CASE_TYPE(IMEMO);
- CASE_TYPE(UNDEF);
- CASE_TYPE(NODE);
- CASE_TYPE(ZOMBIE);
+ CASE_TYPE(NONE);
+ CASE_TYPE(NIL);
+ CASE_TYPE(OBJECT);
+ CASE_TYPE(CLASS);
+ CASE_TYPE(ICLASS);
+ CASE_TYPE(MODULE);
+ CASE_TYPE(FLOAT);
+ CASE_TYPE(STRING);
+ CASE_TYPE(REGEXP);
+ CASE_TYPE(ARRAY);
+ CASE_TYPE(HASH);
+ CASE_TYPE(STRUCT);
+ CASE_TYPE(BIGNUM);
+ CASE_TYPE(FILE);
+ CASE_TYPE(FIXNUM);
+ CASE_TYPE(TRUE);
+ CASE_TYPE(FALSE);
+ CASE_TYPE(DATA);
+ CASE_TYPE(MATCH);
+ CASE_TYPE(SYMBOL);
+ CASE_TYPE(RATIONAL);
+ CASE_TYPE(COMPLEX);
+ CASE_TYPE(IMEMO);
+ CASE_TYPE(UNDEF);
+ CASE_TYPE(NODE);
+ CASE_TYPE(ZOMBIE);
#undef CASE_TYPE
default: break;
}
@@ -312,6 +329,17 @@ reachable_object_i(VALUE ref, void *data)
dc->cur_obj_references++;
}
+static bool
+dump_string_ascii_only(const char *str, long size)
+{
+ for (long i = 0; i < size; i++) {
+ if (str[i] & 0x80) {
+ return false;
+ }
+ }
+ return true;
+}
+
static void
dump_append_string_content(struct dump_config *dc, VALUE obj)
{
@@ -322,20 +350,42 @@ dump_append_string_content(struct dump_config *dc, VALUE obj)
dump_append_sizet(dc, rb_str_capacity(obj));
}
- if (is_ascii_string(obj)) {
- dump_append(dc, ", \"value\":");
- dump_append_string_value(dc, obj);
+ if (RSTRING_LEN(obj) && rb_enc_asciicompat(rb_enc_from_index(ENCODING_GET(obj)))) {
+ int cr = ENC_CODERANGE(obj);
+ if (cr == RUBY_ENC_CODERANGE_UNKNOWN) {
+ if (dump_string_ascii_only(RSTRING_PTR(obj), RSTRING_LEN(obj))) {
+ cr = RUBY_ENC_CODERANGE_7BIT;
+ }
+ }
+ if (cr == RUBY_ENC_CODERANGE_7BIT) {
+ dump_append(dc, ", \"value\":");
+ dump_append_string_value(dc, obj);
+ }
}
}
+static inline void
+dump_append_id(struct dump_config *dc, ID id)
+{
+ VALUE str = rb_sym2str(ID2SYM(id));
+ if (RTEST(str)) {
+ dump_append_string_value(dc, str);
+ }
+ else {
+ dump_append(dc, "\"ID_INTERNAL(");
+ dump_append_sizet(dc, rb_id_to_serial(id));
+ dump_append(dc, ")\"");
+ }
+}
+
+
static void
dump_object(VALUE obj, struct dump_config *dc)
{
size_t memsize;
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);
@@ -344,7 +394,12 @@ dump_object(VALUE obj, struct dump_config *dc)
dc->cur_obj = obj;
dc->cur_obj_references = 0;
- dc->cur_obj_klass = BUILTIN_TYPE(obj) == T_NODE ? 0 : RBASIC_CLASS(obj);
+ if (BUILTIN_TYPE(obj) == T_NODE || (BUILTIN_TYPE(obj) == T_IMEMO && !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;
@@ -360,6 +415,15 @@ dump_object(VALUE obj, struct dump_config *dc)
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, ", \"slot_size\":");
+ dump_append_sizet(dc, dc->cur_page_slot_size);
+
if (dc->cur_obj_klass) {
dump_append(dc, ", \"class\":");
dump_append_ref(dc, dc->cur_obj_klass);
@@ -376,6 +440,36 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, ", \"imemo_type\":\"");
dump_append(dc, rb_imemo_name(imemo_type(obj)));
dump_append(dc, "\"");
+
+ switch (imemo_type(obj)) {
+ case imemo_callinfo:
+ mid = vm_ci_mid((const struct rb_callinfo *)obj);
+ if (mid != 0) {
+ dump_append(dc, ", \"mid\":");
+ dump_append_id(dc, mid);
+ }
+ break;
+
+ case imemo_callcache:
+ {
+ 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:
@@ -385,10 +479,10 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_STRING:
if (STR_EMBED_P(obj))
dump_append(dc, ", \"embedded\":true");
- if (is_broken_string(obj))
- dump_append(dc, ", \"broken\":true");
if (FL_TEST(obj, RSTRING_FSTR))
dump_append(dc, ", \"fstring\":true");
+ if (CHILLED_STRING_P(obj))
+ dump_append(dc, ", \"chilled\":true");
if (STR_SHARED_P(obj))
dump_append(dc, ", \"shared\":true");
else
@@ -399,6 +493,27 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append(dc, rb_enc_name(rb_enc_from_index(ENCODING_GET(obj))));
dump_append(dc, "\"");
}
+
+ dump_append(dc, ", \"coderange\":\"");
+ switch (RB_ENC_CODERANGE(obj)) {
+ case RUBY_ENC_CODERANGE_UNKNOWN:
+ dump_append(dc, "unknown");
+ break;
+ case RUBY_ENC_CODERANGE_7BIT:
+ dump_append(dc, "7bit");
+ break;
+ case RUBY_ENC_CODERANGE_VALID:
+ dump_append(dc, "valid");
+ break;
+ case RUBY_ENC_CODERANGE_BROKEN:
+ dump_append(dc, "broken");
+ break;
+ }
+ dump_append(dc, "\"");
+
+ if (RB_ENC_CODERANGE(obj) == RUBY_ENC_CODERANGE_BROKEN)
+ dump_append(dc, ", \"broken\":true");
+
break;
case T_HASH:
@@ -413,29 +528,60 @@ dump_object(VALUE obj, struct dump_config *dc)
case T_ARRAY:
dump_append(dc, ", \"length\":");
dump_append_ld(dc, RARRAY_LEN(obj));
- if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED))
+ if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_SHARED_FLAG))
dump_append(dc, ", \"shared\":true");
- if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_EMBED_FLAG))
+ 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 (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(dc, RSTRING_PTR(mod_name));
- dump_append(dc, "\"");
+ 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)) {
+ {
+ const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
dump_append(dc, ", \"struct\":\"");
- dump_append(dc, RTYPEDDATA_TYPE(obj)->wrap_struct_name);
+ dump_append(dc, type->wrap_struct_name);
dump_append(dc, "\"");
+ if (!(type->flags & RUBY_TYPED_FREE_IMMEDIATELY) &&
+ type->function.dfree != RUBY_DEFAULT_FREE) {
+ dump_append(dc, ", \"free_immediately\":false");
+ }
}
break;
@@ -446,8 +592,15 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case T_OBJECT:
+ if (!FL_TEST_RAW(obj, ROBJECT_HEAP)) {
+ dump_append(dc, ", \"embedded\":true");
+ }
+
dump_append(dc, ", \"ivars\":");
- dump_append_lu(dc, ROBJECT_NUMIV(obj));
+ dump_append_lu(dc, ROBJECT_FIELDS_COUNT(obj));
+ if (rb_obj_shape_complex_p(obj)) {
+ dump_append(dc, ", \"complex_shape\":true");
+ }
break;
case T_FILE:
@@ -459,8 +612,8 @@ dump_object(VALUE obj, struct dump_config *dc)
break;
case T_ZOMBIE:
- dump_append(dc, "}\n");
- return;
+ dump_append(dc, "}\n");
+ return;
default:
break;
@@ -494,14 +647,24 @@ dump_object(VALUE obj, struct dump_config *dc)
dump_append_sizet(dc, memsize);
}
- if ((n = rb_obj_gc_flags(obj, flags, sizeof(flags))) > 0) {
- dump_append(dc, ", \"flags\":{");
- for (i=0; i<n; i++) {
- dump_append(dc, "\"");
- dump_append(dc, rb_id2name(flags[i]));
- dump_append(dc, "\":true");
- if (i != n-1) dump_append(dc, ", ");
+ 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 rb_gc_object_metadata had any entries, we need to close the opening
+ * `"flags":{`. */
+ if (gc_metadata[0].name != 0) {
dump_append(dc, "}");
}
@@ -514,14 +677,15 @@ 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) {
- void *ptr = asan_poisoned_object_p(v);
- asan_unpoison_object(v, false);
+ 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 (dc->full_heap || RBASIC(v)->flags)
+ dump_object(v, dc);
if (ptr) {
- asan_poison_object(v);
+ rb_asan_poison_object(v);
}
}
return 0;
@@ -550,17 +714,36 @@ root_obj_i(const char *category, VALUE obj, void *data)
}
static void
-dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
+dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since, VALUE shapes)
{
-
+ dc->given_output = output;
dc->full_heap = 0;
dc->buffer_len = 0;
if (TYPE(output) == T_STRING) {
- dc->stream = Qfalse;
+ dc->stream = NULL;
dc->string = output;
- } else {
- dc->stream = output;
+ }
+ 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;
}
@@ -571,9 +754,12 @@ dump_output(struct dump_config *dc, VALUE output, VALUE full, VALUE since)
if (RTEST(since)) {
dc->partial_dump = 1;
dc->since = NUM2SIZET(since);
- } else {
+ }
+ else {
dc->partial_dump = 0;
}
+
+ dc->shapes_since = RTEST(shapes) ? NUM2SIZET(shapes) : 0;
}
static VALUE
@@ -581,30 +767,100 @@ dump_result(struct dump_config *dc)
{
dump_flush(dc);
+ if (dc->stream) {
+ fflush(dc->stream);
+ }
if (dc->string) {
return dc->string;
- } else {
- rb_io_flush(dc->stream);
- return dc->stream;
}
+ return dc->given_output;
}
static VALUE
-objspace_dump(VALUE os, VALUE obj, VALUE output)
+dump_locked(void *args_p)
{
struct dump_config dc = {0,};
- dump_output(&dc, output, Qnil, Qnil);
+ VALUE obj = ((VALUE*)args_p)[0];
+ VALUE output = ((VALUE*)args_p)[1];
+
+ 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);
}
+/* :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_OFFSET(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(VALUE os, VALUE output, VALUE full, VALUE since)
+dump_all_locked(void *args_p)
{
struct dump_config dc = {0,};
- dump_output(&dc, output, full, since);
+ VALUE output = ((VALUE*)args_p)[0];
+ VALUE full = ((VALUE*)args_p)[1];
+ VALUE since = ((VALUE*)args_p)[2];
+ VALUE shapes = ((VALUE*)args_p)[3];
+
+ dump_output(&dc, output, full, since, shapes);
if (!dc.partial_dump || dc.since == 0) {
/* dump roots */
@@ -612,12 +868,53 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
if (dc.roots) dump_append(&dc, "]}\n");
}
+ if (RTEST(shapes)) {
+ rb_shape_each_shape_id(shape_id_i, &dc);
+ }
+
/* dump all objects */
rb_objspace_each_objects(heap_i, &dc);
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
Init_objspace_dump(VALUE rb_mObjSpace)
{
@@ -625,10 +922,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, 2);
- rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 3);
-
- /* force create static IDs */
- rb_obj_gc_flags(rb_mObjSpace, 0, 0);
+ rb_define_module_function(rb_mObjSpace, "_dump_all", objspace_dump_all, 4);
+ rb_define_module_function(rb_mObjSpace, "_dump_shapes", objspace_dump_shapes, 2);
}