heap_dump 0.0.27 → 0.0.28
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/README.md +9 -3
- data/ext/heap_dump/heap_dump.c +70 -6
- data/ext/heap_dump/ruby_io.h +2 -2
- data/lib/heap_dump/version.rb +1 -1
- metadata +3 -3
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# HeapDump
|
2
2
|
|
3
|
-
Low-level ruby heap memory dump - including data and code references.
|
4
|
-
|
3
|
+
Low-level ruby heap memory dump - including data and code references, useful for finding leaks.
|
4
|
+
Has no performance overhead while not active, so can be used in production environment.
|
5
5
|
|
6
|
+
Originally written across ruby 1.9.2-p290 data structures.
|
6
7
|
Does work on other 1.9.2s and 1.9.3, but not well-tested yet(output is not proven to be as full etc.).
|
7
8
|
|
8
9
|
Currently is under development and output format may differ.
|
@@ -35,6 +36,10 @@ Json contains one object per line, thus can be easily grepped.
|
|
35
36
|
|
36
37
|
### Injecting into live process via GDB
|
37
38
|
|
39
|
+
For long-running applications common and recommended usecase is to have some kind of custom action or signal handler in app itself that invokes dump.
|
40
|
+
|
41
|
+
But also dump can be invoked from gdb.
|
42
|
+
|
38
43
|
run gdb
|
39
44
|
|
40
45
|
```bash
|
@@ -51,7 +56,8 @@ call heapdump_dump("mydump.json")
|
|
51
56
|
|
52
57
|
or `call heapdump_dump(0)`, filename defaults to dump.json.
|
53
58
|
|
54
|
-
Note that ruby
|
59
|
+
Note that yajl-ruby gem (and heap_dump itself) should be available to process this being injected into.
|
60
|
+
Also on rare ocassions process(for example if gdb attached while a signal/gc) may crash after and even during dumping, so safer way is to embed it in advance, there's no performance overhead.
|
55
61
|
|
56
62
|
### Importing dump in MongoDB
|
57
63
|
|
data/ext/heap_dump/heap_dump.c
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#include "ruby.h"
|
2
|
+
#include "ruby/encoding.h"
|
2
3
|
#include <stdlib.h>
|
3
4
|
#include <stdio.h>
|
4
5
|
|
@@ -107,8 +108,8 @@ static void flush_yajl(walk_ctx_t *ctx){
|
|
107
108
|
|
108
109
|
static inline int is_in_heap(void *ptr, void* osp);
|
109
110
|
|
110
|
-
static inline const char*
|
111
|
-
switch(
|
111
|
+
static inline const char* rb_type_str(int type){
|
112
|
+
switch(type){
|
112
113
|
#define T(t) case t: return #t;
|
113
114
|
T(T_NONE); T(T_NIL);
|
114
115
|
T(T_OBJECT); T(T_CLASS); T(T_ICLASS); T(T_MODULE);
|
@@ -124,9 +125,16 @@ static inline const char* rb_builtin_type(VALUE obj){
|
|
124
125
|
T(T_NODE); // code?
|
125
126
|
T(T_ZOMBIE);
|
126
127
|
#undef T
|
128
|
+
default:
|
129
|
+
return "_unknown_type_";
|
127
130
|
}
|
128
131
|
}
|
129
132
|
|
133
|
+
static inline const char* rb_builtin_type(VALUE obj){
|
134
|
+
//NOTE: only for heap objects, on embedded use (TYPE(...))
|
135
|
+
return rb_type_str(BUILTIN_TYPE(obj));
|
136
|
+
}
|
137
|
+
|
130
138
|
#define true 1
|
131
139
|
#define false 0
|
132
140
|
|
@@ -185,7 +193,14 @@ static void yg_id1(VALUE obj, walk_ctx_t* ctx){
|
|
185
193
|
if(BUILTIN_TYPE(obj) == T_STRING && (!(RBASIC(obj)->flags & RSTRING_NOEMBED))){
|
186
194
|
//printf("embedded string\n");
|
187
195
|
//yajl_gen_null(ctx->yajl);
|
188
|
-
|
196
|
+
|
197
|
+
if(rb_enc_get_index(obj) == rb_usascii_encindex())
|
198
|
+
yg_rstring(obj);
|
199
|
+
else{
|
200
|
+
//FIXME: convert encoding/safe syms etc?
|
201
|
+
//yg_cstring("(encoded string)");
|
202
|
+
yg_rstring(obj);
|
203
|
+
}
|
189
204
|
return;
|
190
205
|
}
|
191
206
|
|
@@ -444,7 +459,36 @@ static inline void dump_node(NODE* obj, walk_ctx_t *ctx){
|
|
444
459
|
|
445
460
|
static int
|
446
461
|
dump_keyvalue(st_data_t key, st_data_t value, walk_ctx_t *ctx){
|
447
|
-
|
462
|
+
if ((VALUE)key == Qundef){
|
463
|
+
printf("undef key!\n");
|
464
|
+
// return ST_CONTINUE;
|
465
|
+
}
|
466
|
+
|
467
|
+
if(!key || (VALUE)key == Qnil){
|
468
|
+
yg_cstring("___null_key___"); //TODO: just ""?
|
469
|
+
} else {
|
470
|
+
//TODO: keys must be strings
|
471
|
+
const int type = TYPE(key);
|
472
|
+
if(type == T_SYMBOL || type == T_STRING && (!(RBASIC(key)->flags & RSTRING_NOEMBED)))
|
473
|
+
yg_id((VALUE)key);
|
474
|
+
else
|
475
|
+
{
|
476
|
+
char buf[128];
|
477
|
+
buf[sizeof(buf)-1] = 0;
|
478
|
+
switch(type){
|
479
|
+
case T_FIXNUM:
|
480
|
+
snprintf(buf, sizeof(buf)-1, "%d", FIX2INT(key));
|
481
|
+
break;
|
482
|
+
case T_FLOAT:
|
483
|
+
snprintf(buf, sizeof(buf)-1, "%lg", NUM2DBL(key));
|
484
|
+
break;
|
485
|
+
default:
|
486
|
+
snprintf(buf, sizeof(buf)-1, "__id_%ld", NUM2LONG(rb_obj_id(key)));
|
487
|
+
break;
|
488
|
+
}
|
489
|
+
yg_cstring(buf);
|
490
|
+
}
|
491
|
+
}
|
448
492
|
yg_id((VALUE)value);
|
449
493
|
return ST_CONTINUE;
|
450
494
|
}
|
@@ -512,7 +556,7 @@ static int dump_iv_entry(ID key, VALUE value, walk_ctx_t *ctx){
|
|
512
556
|
if(key_str)
|
513
557
|
yg_cstring(key_str);
|
514
558
|
else{
|
515
|
-
printf("Null key! %d\n", key);
|
559
|
+
// printf("Null key! %d\n", key);
|
516
560
|
yg_cstring("___null_key___");
|
517
561
|
//yg_null();
|
518
562
|
}
|
@@ -935,13 +979,32 @@ static inline void walk_live_object(VALUE obj, walk_ctx_t *ctx){
|
|
935
979
|
break;
|
936
980
|
case T_STRING:
|
937
981
|
//TODO: limit string len!
|
982
|
+
{
|
983
|
+
int enc_i = rb_enc_get_index(obj);
|
984
|
+
rb_encoding* enc = rb_enc_from_index(enc_i);
|
985
|
+
if(enc){
|
986
|
+
ygh_cstring("encoding", enc->name);
|
987
|
+
}
|
988
|
+
//FIXME: convert encoding and dump?
|
989
|
+
//if(enc_i == rb_usascii_encindex())
|
990
|
+
//this produces warnings on dump read, but recoverable
|
938
991
|
ygh_string("val", RSTRING_PTR(obj), (unsigned int)RSTRING_LEN(obj));
|
992
|
+
}
|
939
993
|
break;
|
940
994
|
case T_SYMBOL:
|
941
995
|
ygh_cstring("val", rb_id2name(SYM2ID(obj)));
|
942
996
|
break;
|
943
997
|
case T_REGEXP:
|
998
|
+
{
|
999
|
+
int enc_i = rb_enc_get_index(obj);
|
1000
|
+
rb_encoding* enc = rb_enc_from_index(enc_i);
|
1001
|
+
if(enc){
|
1002
|
+
ygh_cstring("encoding", enc->name);
|
1003
|
+
}
|
1004
|
+
//FIXME: encodings?
|
1005
|
+
// if(enc_i == rb_usascii_encindex())
|
944
1006
|
ygh_string("val", RREGEXP_SRC_PTR(obj), (unsigned int)RREGEXP_SRC_LEN(obj));
|
1007
|
+
}
|
945
1008
|
break;
|
946
1009
|
// T(T_MATCH);
|
947
1010
|
|
@@ -1494,7 +1557,7 @@ void heapdump_dump(const char* filename){
|
|
1494
1557
|
|
1495
1558
|
dump_machine_context(ctx);
|
1496
1559
|
flush_yajl(ctx);
|
1497
|
-
fprintf(ctx->file, "\n");
|
1560
|
+
// fprintf(ctx->file, "\n");
|
1498
1561
|
|
1499
1562
|
struct gc_list *list;
|
1500
1563
|
/* mark protected global variables */
|
@@ -1521,6 +1584,7 @@ void heapdump_dump(const char* filename){
|
|
1521
1584
|
|
1522
1585
|
yajl_gen_map_close(ctx->yajl); //id:roots
|
1523
1586
|
flush_yajl(ctx);
|
1587
|
+
fprintf(ctx->file, "\n");
|
1524
1588
|
|
1525
1589
|
//now dump all live objects
|
1526
1590
|
printf("starting objspace walk\n");
|
data/ext/heap_dump/ruby_io.h
CHANGED
@@ -11,8 +11,8 @@ typedef struct {
|
|
11
11
|
|
12
12
|
//#include "encoding.h" // and this also (rb_encoding + rb_econv_t)
|
13
13
|
//FIXME: nasty:
|
14
|
-
typedef int rb_encoding;
|
15
|
-
typedef struct rb_econv rb_econv_t;
|
14
|
+
// typedef int rb_encoding;
|
15
|
+
// typedef struct rb_econv rb_econv_t;
|
16
16
|
|
17
17
|
typedef struct rb_io_t {
|
18
18
|
int fd; /* file descriptor */
|
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.28
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debugger-ruby_core_source
|
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
105
|
version: '0'
|
106
106
|
segments:
|
107
107
|
- 0
|
108
|
-
hash: -
|
108
|
+
hash: -3488312299511292318
|
109
109
|
requirements: []
|
110
110
|
rubyforge_project:
|
111
111
|
rubygems_version: 1.8.24
|