heap_dump 0.0.24 → 0.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -1
- data/ext/heap_dump/extconf.rb +33 -6
- data/ext/heap_dump/heap_dump.c +70 -36
- data/heap_dump.gemspec +1 -1
- data/lib/heap_dump/version.rb +1 -1
- data/lib/heap_dump.rb +0 -4
- metadata +30 -13
data/.gitignore
CHANGED
data/ext/heap_dump/extconf.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#encoding: utf-8
|
3
|
+
|
1
4
|
require 'mkmf'
|
5
|
+
require 'debugger/ruby_core_source'
|
2
6
|
|
3
7
|
def find_spec name,*requirements
|
4
8
|
return Gem::Specification.find_by_name(name, *requirements) if Gem::Specification.respond_to? :find_by_name
|
@@ -19,12 +23,35 @@ def find_gem_dir(name, *req)
|
|
19
23
|
gem.full_gem_path
|
20
24
|
end
|
21
25
|
|
22
|
-
dir = find_gem_dir('ruby-internal', '~>0.8.5') #FIXME: DRY (see gemfile)
|
23
|
-
find_header('version.h', File.join(dir, 'ext', 'internal', 'yarv-headers'))
|
24
|
-
find_header('yarv-headers/node.h', File.join(dir, 'ext', 'internal'))
|
25
|
-
find_header('internal/method/internal_method.h', File.join(dir, 'ext'))
|
26
26
|
|
27
|
-
|
27
|
+
gemspec = File.expand_path(File.join(File.dirname(__FILE__), '../../heap_dump.gemspec'))
|
28
|
+
spec = instance_eval(File.read(gemspec), gemspec).dependencies.find{|d|d.name == 'yajl-ruby'}
|
29
|
+
#$defs.push(format("-DREQUIRED_YAJL_VERSION=\\"%s\\"", spec.requirement)) #does not work in this form :(
|
30
|
+
|
31
|
+
yajl = find_gem_dir(spec.name, spec.requirement)
|
28
32
|
find_header('api/yajl_gen.h', File.join(yajl, 'ext', 'yajl'))
|
29
33
|
|
30
|
-
|
34
|
+
|
35
|
+
hdrs = proc {
|
36
|
+
res = %w{
|
37
|
+
vm_core.h
|
38
|
+
iseq.h
|
39
|
+
node.h
|
40
|
+
method.h
|
41
|
+
}.all?{|hdr| have_header(hdr)}
|
42
|
+
# atomic.h
|
43
|
+
# constant.h
|
44
|
+
|
45
|
+
#optional:
|
46
|
+
%w{
|
47
|
+
constant.h
|
48
|
+
}.each{|h| have_header(h)}
|
49
|
+
|
50
|
+
res
|
51
|
+
}
|
52
|
+
|
53
|
+
dir_config("ruby") # allow user to pass in non-standard core include directory
|
54
|
+
|
55
|
+
if !Debugger::RubyCoreSource::create_makefile_with_core(hdrs, "heap_dump")
|
56
|
+
exit(1)
|
57
|
+
end
|
data/ext/heap_dump/heap_dump.c
CHANGED
@@ -3,25 +3,35 @@
|
|
3
3
|
#include <stdio.h>
|
4
4
|
|
5
5
|
|
6
|
-
#
|
7
|
-
|
8
|
-
#include "
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
#ifdef HAVE_CONSTANT_H
|
7
|
+
//have this in 1.9.3+, for future compatibility
|
8
|
+
#include "constant.h"
|
9
|
+
#else
|
10
|
+
//from 1.9.2
|
11
|
+
typedef enum {
|
12
|
+
CONST_PUBLIC = 0x00,
|
13
|
+
CONST_PRIVATE = 0x01
|
14
|
+
} rb_const_flag_t;
|
15
|
+
|
16
|
+
typedef struct rb_const_entry_struct {
|
17
|
+
rb_const_flag_t flag;
|
18
|
+
VALUE value; /* should be mark */
|
19
|
+
} rb_const_entry_t;
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#include "node.h"
|
23
|
+
#include "vm_core.h"
|
24
|
+
// #include "atomic.h"
|
25
|
+
#include "iseq.h"
|
26
|
+
|
14
27
|
#define RCLASS_EXT(c) (RCLASS(c)->ptr)
|
15
28
|
|
29
|
+
#define NODE_OP_ASGN2_ARG NODE_LAST + 1
|
16
30
|
|
17
|
-
#include "
|
18
|
-
#include "method/internal_method.h"
|
31
|
+
#include "method.h"
|
19
32
|
|
20
33
|
#include "ruby_io.h" // need rb_io_t
|
21
34
|
|
22
|
-
#include "node/ruby_internal_node.h"
|
23
|
-
#include "node/node_type_descrip.c"
|
24
|
-
|
25
35
|
#include "api/yajl_gen.h"
|
26
36
|
|
27
37
|
#ifndef RUBY_VM
|
@@ -163,6 +173,26 @@ static void yg_id1(VALUE obj, walk_ctx_t* ctx){
|
|
163
173
|
yg_int(NUM2LONG(rb_obj_id(obj)));
|
164
174
|
}
|
165
175
|
|
176
|
+
const char* node_type_name(const NODE* obj){
|
177
|
+
#define N(n) case NODE_##n: return #n;
|
178
|
+
switch(nd_type(obj)){
|
179
|
+
N(ALIAS)
|
180
|
+
#ifdef HAVE_NODE_ALLOCA
|
181
|
+
N(ALLOCA)
|
182
|
+
#endif
|
183
|
+
N(AND) N(ARGS) N(ARGSCAT) N(ARGSPUSH) N(ARRAY) N(ATTRASGN) N(BACK_REF) N(BEGIN) N(BLOCK) N(BLOCK_ARG) N(BLOCK_PASS) N(BMETHOD) N(BREAK)
|
184
|
+
N(CALL) N(CASE) N(CDECL) N(CLASS) N(COLON2) N(COLON3) N(CONST) N(CVAR) N(CVASGN) N(CVDECL) N(DASGN) N(DASGN_CURR) N(DEFINED) N(DEFN)
|
185
|
+
N(DEFS) N(DOT2) N(DOT3) N(DREGX) N(DREGX_ONCE) N(DSTR) N(DSYM) N(DVAR) N(DXSTR) N(ENSURE) N(EVSTR) N(FALSE) N(FCALL) N(FLIP2) N(FLIP3)
|
186
|
+
N(FOR) N(GASGN) N(GVAR) N(HASH) N(IASGN) N(IF) N(IFUNC) N(ITER) N(IVAR) N(LASGN) N(LIT) N(LVAR) N(MASGN) N(MATCH) N(MATCH2) N(MATCH3)
|
187
|
+
N(MEMO) N(MODULE) N(NEXT) N(NIL) N(NTH_REF) N(OPT_N) N(OP_ASGN1) N(OP_ASGN2) N(OP_ASGN2_ARG) N(OP_ASGN_AND) N(OP_ASGN_OR) N(OR) N(POSTEXE)
|
188
|
+
N(REDO) N(RESBODY) N(RESCUE) N(RETRY) N(RETURN) N(SCLASS) N(SCOPE) N(SELF) N(SPLAT) N(STR) N(SUPER) N(TO_ARY) N(TRUE) N(UNDEF) N(UNTIL)
|
189
|
+
N(VALIAS) N(VCALL) N(WHEN) N(WHILE) N(XSTR) N(YIELD) N(ZARRAY) N(ZSUPER) N(LAST)
|
190
|
+
default:
|
191
|
+
return "unknown";
|
192
|
+
};
|
193
|
+
#undef N
|
194
|
+
}
|
195
|
+
|
166
196
|
|
167
197
|
static void dump_node_refs(NODE* obj, walk_ctx_t* ctx){
|
168
198
|
switch (nd_type(obj)) {
|
@@ -369,9 +399,8 @@ static void dump_node_refs(NODE* obj, walk_ctx_t* ctx){
|
|
369
399
|
default: /* unlisted NODE */
|
370
400
|
//FIXME: check pointers!
|
371
401
|
|
372
|
-
{
|
373
|
-
|
374
|
-
printf("UNKNOWN NODE TYPE %d(%s): %p %p %p\n", nd_type(obj), descrip ? descrip->name : "unknown", (void*)obj->u1.node, (void*)obj->u2.node, (void*)obj->u3.node);
|
402
|
+
{
|
403
|
+
printf("UNKNOWN NODE TYPE %d(%s): %p %p %p\n", nd_type(obj), node_type_name(obj), (void*)obj->u1.node, (void*)obj->u2.node, (void*)obj->u3.node);
|
375
404
|
}
|
376
405
|
|
377
406
|
// if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) { gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev); }
|
@@ -385,10 +414,8 @@ static void dump_node_refs(NODE* obj, walk_ctx_t* ctx){
|
|
385
414
|
}
|
386
415
|
|
387
416
|
static inline void dump_node(NODE* obj, walk_ctx_t *ctx){
|
388
|
-
const Node_Type_Descrip* descrip = node_type_descrips[nd_type(obj)]; // node_type_descrip(nd_type(obj)) raises exception on unknown 65, 66 and 92
|
389
|
-
|
390
417
|
ygh_int("nd_type", nd_type(obj));
|
391
|
-
ygh_cstring("nd_type_str",
|
418
|
+
ygh_cstring("nd_type_str", node_type_name(obj));
|
392
419
|
|
393
420
|
yg_cstring("refs");
|
394
421
|
yajl_gen_array_open(ctx->yajl);
|
@@ -542,17 +569,18 @@ static void dump_block(const rb_block_t* block, walk_ctx_t *ctx){
|
|
542
569
|
yg_cstring("iseq");
|
543
570
|
yajl_gen_map_open(ctx->yajl);
|
544
571
|
//FIXME: id may be different (due to RBasic fields)!!!
|
545
|
-
ygh_id("id", block->iseq);
|
572
|
+
ygh_id("id", (VALUE)block->iseq);
|
546
573
|
dump_iseq(block->iseq, ctx);
|
547
574
|
yajl_gen_map_close(ctx->yajl);
|
548
575
|
} else {
|
549
|
-
ygh_id("iseq", block->iseq);
|
576
|
+
ygh_id("iseq", (VALUE)block->iseq);
|
550
577
|
}
|
551
578
|
|
552
579
|
ygh_id("self", block->self);
|
553
580
|
|
554
|
-
|
555
|
-
ygh_id("
|
581
|
+
//FIXME: these are pointers to some memory, may be dumped more clever
|
582
|
+
ygh_id("lfp", (VALUE)block->lfp);
|
583
|
+
ygh_id("dfp", (VALUE)block->dfp);
|
556
584
|
//lfp = local frame pointer? local_num elems?
|
557
585
|
// dfp = ?
|
558
586
|
}
|
@@ -627,19 +655,19 @@ static void yg_fiber_type(enum context_type status, walk_ctx_t* ctx){
|
|
627
655
|
}
|
628
656
|
}
|
629
657
|
|
630
|
-
static void dump_locations(VALUE* p, int n, walk_ctx_t *ctx){
|
658
|
+
static void dump_locations(VALUE* p, long int n, walk_ctx_t *ctx){
|
631
659
|
if(n > 0){
|
632
660
|
VALUE* x = p;
|
633
661
|
while(n--){
|
634
662
|
VALUE v = *x;
|
635
|
-
if(is_pointer_to_heap(v, NULL)) //TODO: sometimes thread is known, may get its th->vm->objspace (in case there's a few)
|
663
|
+
if(is_pointer_to_heap((void*)v, NULL)) //TODO: sometimes thread is known, may get its th->vm->objspace (in case there's a few)
|
636
664
|
yg_id(v);
|
637
665
|
x++;
|
638
666
|
}
|
639
667
|
}
|
640
668
|
}
|
641
669
|
|
642
|
-
static void dump_thread(rb_thread_t* th, walk_ctx_t *ctx);
|
670
|
+
static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx);
|
643
671
|
|
644
672
|
|
645
673
|
vm_dump_each_thread_func(st_data_t key, VALUE obj, walk_ctx_t *ctx){
|
@@ -655,6 +683,15 @@ vm_dump_each_thread_func(st_data_t key, VALUE obj, walk_ctx_t *ctx){
|
|
655
683
|
return ST_CONTINUE;
|
656
684
|
}
|
657
685
|
|
686
|
+
//FIXME: parse this from ruby source!
|
687
|
+
struct METHOD {
|
688
|
+
//for 1.9.2 only
|
689
|
+
VALUE recv;
|
690
|
+
VALUE rclass;
|
691
|
+
ID id;
|
692
|
+
rb_method_entry_t me;
|
693
|
+
};
|
694
|
+
|
658
695
|
|
659
696
|
static void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
660
697
|
|
@@ -686,7 +723,7 @@ static void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
|
686
723
|
const st_table *tbl = RTYPEDDATA_DATA(obj);
|
687
724
|
yg_cstring("val");
|
688
725
|
yajl_gen_map_open(ctx->yajl);
|
689
|
-
st_foreach(tbl, dump_method_entry_i, (st_data_t)ctx);
|
726
|
+
st_foreach((st_table *)tbl, dump_method_entry_i, (st_data_t)ctx); //removing const, but this should not affect hash
|
690
727
|
yajl_gen_map_close(ctx->yajl);
|
691
728
|
return;
|
692
729
|
}
|
@@ -817,7 +854,7 @@ static void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
|
817
854
|
if (vm->living_threads) {
|
818
855
|
yg_cstring("threads");
|
819
856
|
yg_array();
|
820
|
-
st_foreach(vm->living_threads, vm_dump_each_thread_func, ctx);
|
857
|
+
st_foreach(vm->living_threads, vm_dump_each_thread_func, (st_data_t)ctx);
|
821
858
|
yg_array_end();
|
822
859
|
}
|
823
860
|
// rb_gc_mark_locations(vm->special_exceptions, vm->special_exceptions + ruby_special_error_count);
|
@@ -1493,8 +1530,9 @@ is_pointer_to_heap(void *ptr, void* osp)
|
|
1493
1530
|
|
1494
1531
|
|
1495
1532
|
static int
|
1496
|
-
dump_backtrace(
|
1533
|
+
dump_backtrace(void* data, VALUE file, int line, VALUE method)
|
1497
1534
|
{
|
1535
|
+
walk_ctx_t *ctx = data;
|
1498
1536
|
yg_map();
|
1499
1537
|
const char *filename = NIL_P(file) ? "<ruby>" : RSTRING_PTR(file);
|
1500
1538
|
|
@@ -1515,7 +1553,7 @@ dump_backtrace(walk_ctx_t *ctx, VALUE file, int line, VALUE method)
|
|
1515
1553
|
//TODO: autogen, this func is just copied from vm.c
|
1516
1554
|
//typedef int rb_backtrace_iter_func(void *, VALUE, int, VALUE);
|
1517
1555
|
static int
|
1518
|
-
vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg)
|
1556
|
+
vm_backtrace_each(const rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg)
|
1519
1557
|
{
|
1520
1558
|
const rb_control_frame_t *limit_cfp = th->cfp;
|
1521
1559
|
const rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size);
|
@@ -1558,7 +1596,7 @@ vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_i
|
|
1558
1596
|
return TRUE;
|
1559
1597
|
}
|
1560
1598
|
|
1561
|
-
static void dump_thread(rb_thread_t* th, walk_ctx_t *ctx){
|
1599
|
+
static void dump_thread(const rb_thread_t* th, walk_ctx_t *ctx){
|
1562
1600
|
if(th->stack){
|
1563
1601
|
VALUE *p = th->stack;
|
1564
1602
|
VALUE *sp = th->cfp->sp;
|
@@ -1655,7 +1693,7 @@ static void dump_thread(rb_thread_t* th, walk_ctx_t *ctx){
|
|
1655
1693
|
yg_cstring("local_storage");
|
1656
1694
|
yajl_gen_array_open(ctx->yajl);
|
1657
1695
|
if(th->local_storage){
|
1658
|
-
st_foreach(th->local_storage, dump_iv_entry, ctx); //?
|
1696
|
+
st_foreach(th->local_storage, dump_iv_entry, (st_data_t)ctx); //?
|
1659
1697
|
}
|
1660
1698
|
yajl_gen_array_close(ctx->yajl);
|
1661
1699
|
|
@@ -1834,12 +1872,8 @@ void Init_heap_dump(){
|
|
1834
1872
|
|
1835
1873
|
rb_require("rubygems");
|
1836
1874
|
rb_funcall(rb_mKernel, gem, 1, rb_str_new2("yajl-ruby"));
|
1837
|
-
rb_funcall(rb_mKernel, gem, 1, rb_str_new2("ruby-internal")); //TODO: version requirements
|
1838
|
-
rb_require("internal/node");
|
1839
1875
|
rb_require("yajl");
|
1840
1876
|
|
1841
|
-
init_node_type_descrips();
|
1842
|
-
|
1843
1877
|
rb_mHeapDumpModule = rb_define_module("HeapDump");
|
1844
1878
|
rb_define_singleton_method(rb_mHeapDumpModule, "dump_ext", rb_heapdump_dump, 1);
|
1845
1879
|
}
|
data/heap_dump.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
|
21
21
|
gem.extensions = "ext/heap_dump/extconf.rb"
|
22
22
|
|
23
|
-
gem.add_dependency "
|
23
|
+
gem.add_dependency "debugger-ruby_core_source"
|
24
24
|
gem.add_dependency 'yajl-ruby', '~>1.1'
|
25
25
|
gem.add_development_dependency "rake-compiler"
|
26
26
|
end
|
data/lib/heap_dump/version.rb
CHANGED
data/lib/heap_dump.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heap_dump
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.25
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,27 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement:
|
15
|
+
name: debugger-ruby_core_source
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0
|
21
|
+
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: yajl-ruby
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '1.1'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.1'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rake-compiler
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,7 +53,12 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
description: Ruby 1.9 heap contents dumper
|
48
63
|
email:
|
49
64
|
- vasilyfedoseyev@gmail.com
|
@@ -81,12 +96,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
96
|
- - ! '>='
|
82
97
|
- !ruby/object:Gem::Version
|
83
98
|
version: '0'
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
hash: -3428409175290370269
|
84
102
|
requirements: []
|
85
103
|
rubyforge_project:
|
86
|
-
rubygems_version: 1.8.
|
104
|
+
rubygems_version: 1.8.24
|
87
105
|
signing_key:
|
88
106
|
specification_version: 3
|
89
107
|
summary: Allows to dump heap to track reference leaks, including leaks in proc contexts
|
90
108
|
and fibers
|
91
109
|
test_files: []
|
92
|
-
has_rdoc:
|