heap_dump 0.0.24 → 0.0.25
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|