heap_dump 0.0.6 → 0.0.8
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 +2 -1
- data/README.md +45 -2
- data/Rakefile +10 -1
- data/ext/heap_dump/extconf.rb +1 -0
- data/ext/heap_dump/heap_dump.c +30 -22
- data/lib/heap_dump/version.rb +1 -1
- metadata +7 -7
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# HeapDump
|
2
2
|
|
3
|
-
|
3
|
+
Low-level ruby heap memory dump - including data and code references.
|
4
|
+
Written across ruby 1.9.2-p290 data structures. Other rubies support may come later (or may not).
|
5
|
+
|
6
|
+
Currently is under development and output format may differ.
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
@@ -18,7 +21,47 @@ Or install it yourself as:
|
|
18
21
|
|
19
22
|
## Usage
|
20
23
|
|
21
|
-
|
24
|
+
In your code call:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
|
28
|
+
HeapDump.dump
|
29
|
+
```
|
30
|
+
|
31
|
+
this will run GC and then create a dump.json with live heap contents.
|
32
|
+
Json contains one object per line, thus can be easily grepped.
|
33
|
+
|
34
|
+
### Injecting into live process via GDB
|
35
|
+
|
36
|
+
run gdb
|
37
|
+
|
38
|
+
```bash
|
39
|
+
|
40
|
+
gdb `which ruby` $YOUR_PID
|
41
|
+
```
|
42
|
+
|
43
|
+
And then run commands like:
|
44
|
+
|
45
|
+
```
|
46
|
+
call rb_require("/Users/vasfed/work/heap_dump/lib/heap_dump.bundle")
|
47
|
+
call heapdump_dump("mydump.json")
|
48
|
+
```
|
49
|
+
|
50
|
+
or `call heapdump_dump(0)`, filename defaults to dump.json.
|
51
|
+
|
52
|
+
Note that ruby-internal and yajl-ruby gems should be available to process this being injected into.
|
53
|
+
|
54
|
+
### Importing dump in MongoDB
|
55
|
+
|
56
|
+
Dump can be imported in mongo for some map-reduce, easy script access etc.
|
57
|
+
|
58
|
+
```bash
|
59
|
+
|
60
|
+
cat dump.json | sed 's/^[,\[]//;s/\]$//;s/^{"id"/{"_id"/' | mongoimport -d database_name -c collection_name --drop --type json
|
61
|
+
```
|
62
|
+
|
63
|
+
Note that even small dumps usually contain a few hundred thousands objects, so do not forget to add some indexes.
|
64
|
+
|
22
65
|
|
23
66
|
## Contributing
|
24
67
|
|
data/Rakefile
CHANGED
@@ -3,4 +3,13 @@ require "bundler/gem_tasks"
|
|
3
3
|
|
4
4
|
|
5
5
|
require 'rake/extensiontask'
|
6
|
-
Rake::ExtensionTask.new('heap_dump')
|
6
|
+
Rake::ExtensionTask.new('heap_dump')
|
7
|
+
|
8
|
+
desc "Simple dump test,just to check if extension compiles and does not segfault on simple dump"
|
9
|
+
task :test => :compile do
|
10
|
+
require 'heap_dump'
|
11
|
+
puts "Dumping..."
|
12
|
+
HeapDump.dump
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => :test
|
data/ext/heap_dump/extconf.rb
CHANGED
@@ -3,6 +3,7 @@ require 'mkmf'
|
|
3
3
|
spec = Gem::Specification.find_by_name('ruby-internal', '~>0.8.5') #FIXME: DRY (see gemfile)
|
4
4
|
find_header('version.h', File.join(spec.gem_dir, 'ext', 'internal', 'yarv-headers'))
|
5
5
|
find_header('yarv-headers/node.h', File.join(spec.gem_dir, 'ext', 'internal'))
|
6
|
+
find_header('internal/method/internal_method.h', File.join(spec.gem_dir, 'ext'))
|
6
7
|
|
7
8
|
yajl = Gem::Specification.find_by_name('yajl-ruby', '~>1.1')
|
8
9
|
find_header('api/yajl_gen.h', File.join(yajl.gem_dir, 'ext', 'yajl'))
|
data/ext/heap_dump/heap_dump.c
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
|
17
17
|
#include "yarv-headers/method.h"
|
18
|
+
#include "method/internal_method.h"
|
18
19
|
|
19
20
|
#include "ruby_io.h" // need rb_io_t
|
20
21
|
|
@@ -476,7 +477,7 @@ static int dump_const_entry_i(ID key, const rb_const_entry_t *ce, walk_ctx_t *ct
|
|
476
477
|
return ST_CONTINUE;
|
477
478
|
}
|
478
479
|
|
479
|
-
const char* iseq_type(VALUE type){
|
480
|
+
static const char* iseq_type(VALUE type){
|
480
481
|
switch(type){
|
481
482
|
case ISEQ_TYPE_TOP: return "top";
|
482
483
|
case ISEQ_TYPE_METHOD: return "method";
|
@@ -492,7 +493,7 @@ const char* iseq_type(VALUE type){
|
|
492
493
|
return "unknown";
|
493
494
|
}
|
494
495
|
|
495
|
-
void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
|
496
|
+
static void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
|
496
497
|
if(iseq->name) ygh_rstring("name", iseq->name);
|
497
498
|
if(iseq->filename) ygh_rstring("filename", iseq->filename);
|
498
499
|
ygh_int("line", iseq->line_no);
|
@@ -520,15 +521,8 @@ void dump_iseq(const rb_iseq_t* iseq, walk_ctx_t *ctx){
|
|
520
521
|
}
|
521
522
|
}
|
522
523
|
|
523
|
-
//!!! from 1.9.2-p290
|
524
|
-
struct METHOD {
|
525
|
-
VALUE recv;
|
526
|
-
VALUE rclass;
|
527
|
-
ID id;
|
528
|
-
rb_method_entry_t me;
|
529
|
-
};
|
530
524
|
|
531
|
-
void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
525
|
+
static void dump_data_if_known(VALUE obj, walk_ctx_t *ctx){
|
532
526
|
|
533
527
|
// VM
|
534
528
|
// VM/env
|
@@ -842,11 +836,11 @@ static int objspace_walker(void *vstart, void *vend, int stride, walk_ctx_t *ctx
|
|
842
836
|
// #elif defined(__i386) && defined(__GNUC__) && !defined(__native_client__)
|
843
837
|
// #define SET_MACHINE_STACK_END(p) __asm__("movl\t%%esp, %0" : "=r" (*(p)))
|
844
838
|
// #else
|
845
|
-
NOINLINE(void rb_gc_set_stack_end(VALUE **stack_end_p));
|
839
|
+
NOINLINE(static void rb_gc_set_stack_end(VALUE **stack_end_p));
|
846
840
|
#define SET_MACHINE_STACK_END(p) rb_gc_set_stack_end(p)
|
847
841
|
#define USE_CONSERVATIVE_STACK_END
|
848
842
|
// #endif
|
849
|
-
void
|
843
|
+
static void
|
850
844
|
rb_gc_set_stack_end(VALUE **stack_end_p)
|
851
845
|
{
|
852
846
|
VALUE stack_end;
|
@@ -922,6 +916,7 @@ extern st_table *rb_class_tbl;
|
|
922
916
|
#endif
|
923
917
|
|
924
918
|
//FIXME: this should be autoextracted from ruby
|
919
|
+
// see how this is done in ruby-internal gem
|
925
920
|
typedef struct RVALUE {
|
926
921
|
union {
|
927
922
|
struct {
|
@@ -1241,16 +1236,17 @@ static int dump_iv_entry1(ID key, rb_const_entry_t* ce/*st_data_t val*/, walk_ct
|
|
1241
1236
|
return ST_CONTINUE;
|
1242
1237
|
}
|
1243
1238
|
|
1244
|
-
|
1245
|
-
|
1246
|
-
{
|
1239
|
+
|
1240
|
+
//public symbol, can be used from GDB
|
1241
|
+
void heapdump_dump(const char* filename){
|
1247
1242
|
struct walk_ctx ctx_o, *ctx = &ctx_o;
|
1248
1243
|
memset(ctx, 0, sizeof(*ctx));
|
1249
1244
|
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1245
|
+
if(!filename){
|
1246
|
+
filename = "dump.json";
|
1247
|
+
}
|
1248
|
+
printf("Dump should go to %s\n", filename);
|
1249
|
+
ctx->file = fopen(filename, "wt");
|
1254
1250
|
ctx->yajl = yajl_gen_alloc(NULL,NULL);
|
1255
1251
|
yajl_gen_array_open(ctx->yajl);
|
1256
1252
|
|
@@ -1295,18 +1291,30 @@ rb_heapdump_dump(VALUE self, VALUE filename)
|
|
1295
1291
|
fclose(ctx->file);
|
1296
1292
|
|
1297
1293
|
printf("Walker called %d times, seen %d live objects.\n", ctx->walker_called, ctx->live_objects);
|
1294
|
+
}
|
1298
1295
|
|
1296
|
+
static VALUE
|
1297
|
+
rb_heapdump_dump(VALUE self, VALUE filename)
|
1298
|
+
{
|
1299
|
+
Check_Type(filename, T_STRING);
|
1300
|
+
heapdump_dump(RSTRING_PTR(filename));
|
1299
1301
|
return Qnil;
|
1300
1302
|
}
|
1301
1303
|
|
1302
|
-
|
1303
1304
|
void Init_heap_dump(){
|
1304
1305
|
//ruby-internal need to be required before linking us, but just in case..
|
1306
|
+
ID require, gem;
|
1307
|
+
CONST_ID(require, "require");
|
1308
|
+
CONST_ID(gem, "gem");
|
1309
|
+
CONST_ID(classid, "__classid__");
|
1310
|
+
|
1311
|
+
rb_require("rubygems");
|
1312
|
+
rb_funcall(rb_mKernel, gem, 1, rb_str_new2("yajl-ruby"));
|
1313
|
+
rb_funcall(rb_mKernel, gem, 1, rb_str_new2("ruby-internal")); //TODO: version requirements
|
1305
1314
|
rb_require("internal/node");
|
1306
1315
|
rb_require("yajl");
|
1307
|
-
init_node_type_descrips();
|
1308
1316
|
|
1309
|
-
|
1317
|
+
init_node_type_descrips();
|
1310
1318
|
|
1311
1319
|
rb_mHeapDumpModule = rb_define_module("HeapDump");
|
1312
1320
|
rb_define_singleton_method(rb_mHeapDumpModule, "dump_ext", rb_heapdump_dump, 1);
|
data/lib/heap_dump/version.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.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-05-30 00:00:00.000000000Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ruby-internal
|
16
|
-
requirement: &
|
16
|
+
requirement: &70110343222520 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 0.8.5
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70110343222520
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: yajl-ruby
|
27
|
-
requirement: &
|
27
|
+
requirement: &70110343221760 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.1'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70110343221760
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake-compiler
|
38
|
-
requirement: &
|
38
|
+
requirement: &70110343221160 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70110343221160
|
47
47
|
description: dump ruby 1.9 heap contents
|
48
48
|
email:
|
49
49
|
- vasilyfedoseyev@gmail.com
|