8000 ObjectSpace.dump_all: dump shapes as well · ruby/ruby@40edcdd · GitHub
[go: up one dir, main page]

Skip to content

Commit 40edcdd

Browse files
committed
ObjectSpace.dump_all: dump shapes as well
I see several arguments in doing so. First they use a non trivial amount of memory, so for various memory profiling/mapping tools it is relevant to have visibility of the space occupied by shapes. Then, some pathological code can create a tons of shape, so it is valuable to have a way to have a way to observe shapes without having to compile Ruby with `SHAPE_DEBUG=1`. And additionally it's likely much faster to dump then this way than to use `RubyVM::Shape`. There are however a few open questions: - Shapes can't respect the `since:` argument. Not sure what to do when it is provided. Would probably make sense to not dump them. - Maybe it would make more sense to have a separate `ObjectSpace.dump_shapes`? - Maybe instead `dump_all` should take a `shapes: false` argument?
1 parent 901471a commit 40edcdd

File tree

3 files changed

+123
-6
lines changed

3 files changed

+123
-6
lines changed

ext/objspace/objspace_dump.c

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "internal/hash.h"
1919
#include "internal/string.h"
2020
#include "internal/sanitizers.h"
21+
#include "shape.h"
2122
#include "node.h"
2223
#include "objspace.h"
2324
#include "ruby/debug.h"
@@ -350,6 +351,17 @@ dump_append_string_content(struct dump_config *dc, VALUE obj)
350351
}
351352
}
352353

354+
static inline void
355+
dump_append_id(struct dump_config *dc, ID id)
356+
{
357+
if (id) {
358+
dump_append_string_value(dc, rb_sym2str(ID2SYM(id)));
359+
}
360+
else {
361+
dump_append(dc, "null");
362+
}
363+
}
364+
353365
static void
354366
dump_object(VALUE obj, struct dump_config *dc)
355367
{
@@ -378,13 +390,13 @@ dump_object(VALUE obj, struct dump_config *dc)
378390
dump_append(dc, "{\"address\":");
379391
dump_append_ref(dc, obj);
380392

381-
dump_append(dc, ", \"shape_id\":");
382-
dump_append_sizet(dc, rb_shape_get_shape_id(obj));
383-
384393
dump_append(dc, ", \"type\":\"");
385394
dump_append(dc, obj_type(obj));
386395
dump_append(dc, "\"");
387396

397+
dump_append(dc, ", \"shape_id\":");
398+
dump_append_sizet(dc, rb_shape_get_shape_id(obj));
399+
388400
dump_append(dc, ", \"slot_size\":");
389401
dump_append_sizet(dc, dc->cur_page_slot_size);
390402

@@ -679,6 +691,61 @@ objspace_dump(VALUE os, VALUE obj, VALUE output)
679691
return dump_result(&dc);
680692
}
681693

694+
static void
695+
shape_i(rb_shape_t *shape, void *data)
696+
{
697+
struct dump_config *dc = (struct dump_config *)data;
698+
699+
dump_append(dc, "{\"address\":");
700+
dump_append_ref(dc, (VALUE)shape);
701+
702+
dump_append(dc, ", \"type\":\"SHAPE\", \"id\":");
703+
dump_append_lu(dc, rb_shape_id(shape));
704+
705+
dump_append(dc, ", \"parent_id\":");
706+
dump_append_lu(dc, shape->parent_id);
707+
708+
dump_append(dc, ", \"edges\":");
709+
dump_append_sizet(dc, rb_shape_edges_count(shape));
710+
711+
dump_append(dc, ", \"shape_type\":");
712+
switch(shape->type) {
713+
case SHAPE_ROOT:
714+
dump_append(dc, "\"ROOT\"");
715+
break;
716+
case SHAPE_IVAR:
717+
dump_append(dc, "\"IVAR\"");
718+
break;
719+
case SHAPE_FROZEN:
720+
dump_append(dc, "\"FROZEN\"");
721+
break;
722+
case SHAPE_CAPACITY_CHANGE:
723+
dump_append(dc, "\"CAPACITY_CHANGE\"");
724+
break;
725+
case SHAPE_IVAR_UNDEF:
726+
dump_append(dc, "\"IVAR_UNDEF\"");
727+
break;
728+
case SHAPE_INITIAL_CAPACITY:
729+
dump_append(dc, "\"INITIAL_CAPACITY\"");
730+
break;
731+
case SHAPE_T_OBJECT:
732+
dump_append(dc, "\"T_OBJECT\"");
733+
break;
734+
default:
735+
rb_bug("[objspace] unexpected shape type");
736+
}
737+
738+
if (shape->edge_name && ((shape->edge_name & ID_INTERNAL) != ID_INTERNAL)) {
739+
dump_append(dc, ",\"edge_name\":");
740+
dump_append_id(dc, shape->edge_name);
741+
}
742+
743+
dump_append(dc, ", \"memsize\":");
744+
dump_append_sizet(dc, rb_shape_memsize(shape));
745+
746+
dump_append(dc, "}\n");
747+
}
748+
682749
static VALUE
683750
objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
684751
{
@@ -691,6 +758,8 @@ objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
691758
if (dc.roots) dump_append(&dc, "]}\n");
692759
}
693760

761+
rb_shape_each_shape(shape_i, &dc);
762+
694763
/* dump all objects */
695764
rb_objspace_each_objects(heap_i, &dc);
696765

shape.c

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "vm_sync.h"
33
#include "shape.h"
44
#include "gc.h"
5+
#include "id_table.h"
56
#include "internal/class.h"
67
#include "internal/symbol.h"
78
#include "internal/variable.h"
@@ -36,6 +37,17 @@ rb_shape_root_shape_p(rb_shape_t* shape)
3637
return shape == rb_shape_get_root_shape();
3738
}
3839

40+
void
41+
rb_shape_each_shape(each_shape_callback callback, void *data)
42+
{
43+
rb_shape_t *cursor = rb_shape_get_root_shape();
44+
rb_shape_t *end = rb_shape_get_shape_by_id(GET_VM()->next_shape_id);
45+
while (cursor < end) {
46+
callback(cursor, data);
47+
cursor += 1;
48+
}
49+
}
50+
3951
rb_shape_t*
4052
rb_shape_get_shape_by_id(shape_id_t shape_id)
4153
{
@@ -343,16 +355,45 @@ rb_shape_rebuild_shape(rb_shape_t * initial_shape, rb_shape_t * dest_shape)
343355
return midway_shape;
344356
}
345357

358+
size_t
359+
rb_shape_edges_count(rb_shape_t *shape)
360+
{
361+
if (shape->edges) {
362+
return rb_id_table_size(shape->edges);
363+
}
364+
return 0;
365+
}
366+
367+
size_t
368+
rb_shape_memsize(rb_shape_t *shape)
369+
{
370+
size_t memsize = sizeof(rb_shape_t);
371+
if (shape->edges) {
372+
memsize += rb_id_table_memsize(shape->edges);
373+
}
374+
return memsize;
375+
}
376+
346377
#if SHAPE_DEBUG
347378
VALUE rb_cShape;
348379

380+
static size_t
381+
shape_memsize(const void *shape_ptr)
382+
{
383+
return rb_shape_memsize((rb_shape_t *)shape_ptr);
384+
}
385+
349386
/*
350387
* Exposing Shape to Ruby via RubyVM.debug_shape
351388
*/
352389
static const rb_data_type_t shape_data_type = {
353-
"Shape",
354-
{NULL, NULL, NULL,},
355-
0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
390+
.wrap_struct_name = "Shape",
391+
.function = {
392+
.dmark = NULL,
393+
.dfree = NULL,
394+
.dsize = shape_memsize,
395+
},
396+
.flags = RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
356397
};
357398

358399
static VALUE

shape.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,11 @@ bool rb_shape_set_shape_id(VALUE obj, shape_id_t shape_id);
184184
VALUE rb_obj_debug_shape(VALUE self, VALUE obj);
185185
VALUE rb_shape_flags_mask(void);
186186

187+
RUBY_SYMBOL_EXPORT_BEGIN
188+
typedef void each_shape_callback(rb_shape_t * shape, void *data);
189+
void rb_shape_each_shape(each_shape_callback callback, void *data);
190+
size_t rb_shape_memsize(rb_shape_t *shape);
191+
size_t rb_shape_edges_count(rb_shape_t *shape);
192+
RUBY_SYMBOL_EXPORT_END
193+
187194
#endif

0 commit comments

Comments
 (0)
0