majo 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +55 -16
- data/ext/majo/allocation_info.c +10 -0
- data/ext/majo/allocation_info.h +1 -0
- data/ext/majo/attached_object.c +26 -0
- data/ext/majo/attached_object.h +13 -0
- data/ext/majo/majo.c +25 -9
- data/ext/majo/majo.h +2 -0
- data/ext/majo/result.c +32 -1
- data/ext/majo/result.h +4 -0
- data/lib/majo/formatter/color.rb +43 -19
- data/lib/majo/formatter/csv.rb +3 -3
- data/lib/majo/result.rb +1 -1
- data/lib/majo/version.rb +1 -1
- data/lib/majo.rb +10 -5
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5db0056e26ba9974d00512d31930aad34d35af8e5ed9f880eda7665bdb9c2f59
|
4
|
+
data.tar.gz: 7489e069356907943bdf68de696cd3c9071aa4c2918743a5c05ba3b4ce286bf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ab8e2fa890a01d0ac7a36c7be6e2adbe492fbd58319888636e6c733459a0f76dbf3d8aedc08cbe68d488f3a11ff54be758f661b0866afa31b2b4c6ce75abae0
|
7
|
+
data.tar.gz: 42ce9c56b603b4f50ff1504a47bf8f1616d384c4e135034be6b7f9d13c9afa6b5f278fde5683b2011ae9a3b024c9dc2f2b87da955b9dfdaad8fd44bb6fd08e1b
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
## v1.0.0 - 2024-07-26
|
4
|
+
|
5
|
+
The first stable release of Majo!
|
6
|
+
|
7
|
+
* Report retained objects [#32](https://github.com/pocke/majo/pull/32)
|
8
|
+
* Add options for upper and lower threshold of lifetime [#34](https://github.com/pocke/majo/pull/34)
|
9
|
+
* Display class path for singleton method call [#37](https://github.com/pocke/majo/pull/37)
|
10
|
+
|
11
|
+
## v0.1.0 - 2024-07-25
|
12
|
+
|
13
|
+
* Initial release
|
data/README.md
CHANGED
@@ -54,7 +54,7 @@ result.report(out: "majo-result.csv", formatter: :csv)
|
|
54
54
|
|
55
55
|
### Result Example
|
56
56
|
|
57
|
-
The result contains only long-lived objects, which are collected by the major GC.
|
57
|
+
The result contains only long-lived objects, which are collected by the major GC or retained.
|
58
58
|
|
59
59
|
The example is as follows:
|
60
60
|
|
@@ -102,15 +102,53 @@ Objects by class
|
|
102
102
|
22435 RBS::Location
|
103
103
|
11144 RBS::TypeName
|
104
104
|
(snip)
|
105
|
+
|
106
|
+
Retained Memory by file
|
107
|
+
-----------------------------------
|
108
|
+
7040 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/parser_aux.rb
|
109
|
+
256 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/resolver/type_name_resolver.rb
|
110
|
+
(snip)
|
111
|
+
|
112
|
+
Retained Memory by location
|
113
|
+
-----------------------------------
|
114
|
+
7040 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/parser_aux.rb:20
|
115
|
+
256 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/resolver/type_name_resolver.rb:22
|
116
|
+
(snip)
|
117
|
+
|
118
|
+
Retained Memory by class
|
119
|
+
-----------------------------------
|
120
|
+
6920 String
|
121
|
+
256 Hash
|
122
|
+
120 Symbol
|
123
|
+
(snip)
|
124
|
+
|
125
|
+
Retained Objects by file
|
126
|
+
-----------------------------------
|
127
|
+
160 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/parser_aux.rb
|
128
|
+
1 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/resolver/type_name_resolver.rb
|
129
|
+
(snip)
|
130
|
+
|
131
|
+
Retained Objects by location
|
132
|
+
-----------------------------------
|
133
|
+
160 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/parser_aux.rb:20
|
134
|
+
1 /path/to/gems/3.3.0/gems/rbs-3.5.1/lib/rbs/resolver/type_name_resolver.rb:22
|
135
|
+
(snip)
|
136
|
+
|
137
|
+
Retained Objects by class
|
138
|
+
-----------------------------------
|
139
|
+
157 String
|
140
|
+
3 Symbol
|
141
|
+
1 Hash
|
142
|
+
(snip)
|
105
143
|
```
|
106
144
|
|
107
145
|
The CSV format is as follows:
|
108
146
|
|
109
147
|
```csv
|
110
|
-
Object class path,Class path,Method ID,Path,Line,Alloc generation,Free generation,Memsize,Count
|
111
|
-
Hash
|
112
|
-
Hash,RBS::EnvironmentLoader,each_signature,/path/to/gems/rbs-3.5.1/lib/rbs/environment_loader.rb,159,20,22,160,1
|
113
|
-
Hash
|
148
|
+
Object class path,Class path,Method ID,Singleton method,Path,Line,Alloc generation,Free generation,Memsize,Count
|
149
|
+
Hash,RBS::Parser,_parse_signature,true,/path/to/gems/rbs-3.5.1/lib/rbs/parser_aux.rb,20,20,22,160,3
|
150
|
+
Hash,RBS::EnvironmentLoader,each_signature,false,/path/to/gems/rbs-3.5.1/lib/rbs/environment_loader.rb,159,20,22,160,1
|
151
|
+
Hash,RBS::Parser,_parse_signature,true,/path/to/gems/rbs-3.5.1/lib/rbs/parser_aux.rb,20,21,23,160,1
|
114
152
|
(snip)
|
115
153
|
```
|
116
154
|
|
@@ -118,17 +156,18 @@ You can find the raw data in the CSV format. It is useful for further analysis.
|
|
118
156
|
|
119
157
|
The columns are as follows:
|
120
158
|
|
121
|
-
| Column name | Description
|
122
|
-
| ------------------- |
|
123
|
-
| `Object class path` | The class name of the allocated object
|
124
|
-
| `Class path` | The class name of the receiver of the method allocating the object
|
125
|
-
| `Method ID` | The method name allocating the object
|
126
|
-
| `
|
127
|
-
| `
|
128
|
-
| `
|
129
|
-
| `
|
130
|
-
| `
|
131
|
-
| `
|
159
|
+
| Column name | Description |
|
160
|
+
| ------------------- | ----------------------------------------------------------------------------------- |
|
161
|
+
| `Object class path` | The class name of the allocated object |
|
162
|
+
| `Class path` | The class name of the receiver of the method allocating the object |
|
163
|
+
| `Method ID` | The method name allocating the object |
|
164
|
+
| `Singleton method` | `true` if the method call is for a singleton method |
|
165
|
+
| `Path` | The file path of the method allocating the object |
|
166
|
+
| `Line` | The line number of the method allocating the object |
|
167
|
+
| `Alloc generation` | The GC generation number when the object is allocated |
|
168
|
+
| `Free generation` | The GC generation number when the object is freed. It's empty for retained objects. |
|
169
|
+
| `Memsize` | The memory size of the object in bytes |
|
170
|
+
| `Count` | Number of objects allocated with the same conditions |
|
132
171
|
|
133
172
|
## Name
|
134
173
|
|
data/ext/majo/allocation_info.c
CHANGED
@@ -84,6 +84,12 @@ allocation_info_method_id(VALUE self) {
|
|
84
84
|
return info->mid;
|
85
85
|
}
|
86
86
|
|
87
|
+
static VALUE
|
88
|
+
allocation_info_singleton_p(VALUE self) {
|
89
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
90
|
+
return info->singleton_p ? Qtrue : Qfalse;
|
91
|
+
}
|
92
|
+
|
87
93
|
static VALUE
|
88
94
|
allocation_info_alloc_generation(VALUE self) {
|
89
95
|
majo_allocation_info *info = majo_check_allocation_info(self);
|
@@ -93,6 +99,9 @@ allocation_info_alloc_generation(VALUE self) {
|
|
93
99
|
static VALUE
|
94
100
|
allocation_info_free_generation(VALUE self) {
|
95
101
|
majo_allocation_info *info = majo_check_allocation_info(self);
|
102
|
+
if (info->free_generation == 0) {
|
103
|
+
return Qnil;
|
104
|
+
}
|
96
105
|
return SIZET2NUM(info->free_generation);
|
97
106
|
}
|
98
107
|
|
@@ -110,6 +119,7 @@ majo_init_allocation_info() {
|
|
110
119
|
rb_define_method(rb_cMajo_AllocationInfo, "path", allocation_info_path, 0);
|
111
120
|
rb_define_method(rb_cMajo_AllocationInfo, "class_path", allocation_info_class_path, 0);
|
112
121
|
rb_define_method(rb_cMajo_AllocationInfo, "method_id", allocation_info_method_id, 0);
|
122
|
+
rb_define_method(rb_cMajo_AllocationInfo, "singleton?", allocation_info_singleton_p, 0);
|
113
123
|
rb_define_method(rb_cMajo_AllocationInfo, "line", allocation_info_line, 0);
|
114
124
|
rb_define_method(rb_cMajo_AllocationInfo, "object_class_path", allocation_info_object_class_path, 0);
|
115
125
|
rb_define_method(rb_cMajo_AllocationInfo, "alloc_generation", allocation_info_alloc_generation, 0);
|
data/ext/majo/allocation_info.h
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
#include "majo.h"
|
2
|
+
|
3
|
+
#if RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR == 1
|
4
|
+
|
5
|
+
static ID id_attached;
|
6
|
+
|
7
|
+
VALUE
|
8
|
+
majo_attached_object(VALUE klass) {
|
9
|
+
if (!FL_TEST(klass, FL_SINGLETON)) {
|
10
|
+
rb_raise(rb_eTypeError, "`%"PRIsVALUE"' is not a singleton class", klass);
|
11
|
+
}
|
12
|
+
|
13
|
+
return rb_attr_get(klass, id_attached);
|
14
|
+
}
|
15
|
+
|
16
|
+
void
|
17
|
+
majo_init_attached_object() {
|
18
|
+
id_attached = rb_intern_const("__attached__");
|
19
|
+
}
|
20
|
+
|
21
|
+
#else
|
22
|
+
|
23
|
+
void
|
24
|
+
majo_init_attached_object() {}
|
25
|
+
|
26
|
+
#endif
|
data/ext/majo/majo.c
CHANGED
@@ -36,6 +36,18 @@ internal_object_p(VALUE obj)
|
|
36
36
|
}
|
37
37
|
}
|
38
38
|
|
39
|
+
static const char*
|
40
|
+
to_class_path(VALUE klass, st_table *str_table)
|
41
|
+
{
|
42
|
+
if (FL_TEST(klass, FL_SINGLETON)) {
|
43
|
+
return to_class_path(majo_attached_object(klass), str_table);
|
44
|
+
}
|
45
|
+
|
46
|
+
VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
|
47
|
+
const char *class_path_cstr = RTEST(class_path) ? majo_make_unique_str(str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
|
48
|
+
return class_path_cstr;
|
49
|
+
}
|
50
|
+
|
39
51
|
static void
|
40
52
|
newobj_i(VALUE tpval, void *data)
|
41
53
|
{
|
@@ -57,19 +69,16 @@ newobj_i(VALUE tpval, void *data)
|
|
57
69
|
majo_allocation_info *info = (majo_allocation_info *)malloc(sizeof(majo_allocation_info));
|
58
70
|
|
59
71
|
const char *path_cstr = RTEST(path) ? majo_make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
|
60
|
-
|
61
|
-
VALUE class_path = (RTEST(klass) && !OBJ_FROZEN(klass)) ? rb_class_path_cached(klass) : Qnil;
|
62
|
-
const char *class_path_cstr = RTEST(class_path) ? majo_make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
|
72
|
+
const char *class_path_cstr = to_class_path(klass, arg->str_table);
|
63
73
|
|
64
74
|
VALUE obj_class = rb_obj_class(obj);
|
65
|
-
|
66
|
-
const char *obj_class_path_cstr = RTEST(obj_class_path) ? majo_make_unique_str(arg->str_table, RSTRING_PTR(obj_class_path), RSTRING_LEN(obj_class_path)) : 0;
|
67
|
-
|
75
|
+
const char *obj_class_path_cstr = to_class_path(obj_class, arg->str_table);
|
68
76
|
|
69
77
|
info->result = res;
|
70
78
|
info->path = path_cstr;
|
71
79
|
info->line = NUM2INT(line);
|
72
80
|
info->mid = mid;
|
81
|
+
info->singleton_p = FL_TEST(klass, FL_SINGLETON);
|
73
82
|
info->object_class_path = obj_class_path_cstr;
|
74
83
|
|
75
84
|
info->class_path = class_path_cstr;
|
@@ -88,7 +97,11 @@ freeobj_i(VALUE tpval, void *data)
|
|
88
97
|
if (st_delete(arg->object_table, &obj, &v)) {
|
89
98
|
majo_allocation_info *info = (majo_allocation_info *)v;
|
90
99
|
size_t gc_count = rb_gc_count();
|
91
|
-
|
100
|
+
int lifetime = (int)(gc_count - info->alloc_generation - 1);
|
101
|
+
if (
|
102
|
+
(NIL_P(arg->upper_lifetime) || lifetime <= NUM2INT(arg->upper_lifetime)) &&
|
103
|
+
(NIL_P(arg->lower_lifetime) || NUM2INT(arg->lower_lifetime) <= lifetime)
|
104
|
+
) {
|
92
105
|
info->memsize = rb_obj_memsize_of((VALUE)obj);
|
93
106
|
info->free_generation = gc_count;
|
94
107
|
|
@@ -102,9 +115,11 @@ freeobj_i(VALUE tpval, void *data)
|
|
102
115
|
}
|
103
116
|
|
104
117
|
static VALUE
|
105
|
-
start(VALUE self) {
|
118
|
+
start(VALUE self, VALUE upper_lifetime, VALUE lower_lifetime) {
|
106
119
|
VALUE res = majo_new_result();
|
107
120
|
majo_result *arg = majo_check_result(res);
|
121
|
+
arg->upper_lifetime = upper_lifetime;
|
122
|
+
arg->lower_lifetime = lower_lifetime;
|
108
123
|
|
109
124
|
VALUE stack = rb_ivar_get(rb_mMajo, running_tracer_stack);
|
110
125
|
rb_ary_push(stack, res);
|
@@ -136,7 +151,7 @@ Init_majo(void)
|
|
136
151
|
{
|
137
152
|
rb_mMajo = rb_define_module("Majo");
|
138
153
|
|
139
|
-
rb_define_module_function(rb_mMajo, "__start", start,
|
154
|
+
rb_define_module_function(rb_mMajo, "__start", start, 2);
|
140
155
|
rb_define_module_function(rb_mMajo, "__stop", stop, 0);
|
141
156
|
|
142
157
|
running_tracer_stack = rb_intern("running_tracer_stack");
|
@@ -144,4 +159,5 @@ Init_majo(void)
|
|
144
159
|
|
145
160
|
majo_init_result();
|
146
161
|
majo_init_allocation_info();
|
162
|
+
majo_init_attached_object();
|
147
163
|
}
|
data/ext/majo/majo.h
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
#include <stdbool.h>
|
5
5
|
|
6
6
|
#include "ruby.h"
|
7
|
+
#include "ruby/version.h"
|
7
8
|
#include "ruby/debug.h"
|
8
9
|
#include "ruby/internal/gc.h"
|
9
10
|
|
@@ -12,6 +13,7 @@
|
|
12
13
|
#include "allocation_info.h"
|
13
14
|
#include "result.h"
|
14
15
|
#include "unique_str.h"
|
16
|
+
#include "attached_object.h"
|
15
17
|
|
16
18
|
extern VALUE rb_mMajo;
|
17
19
|
extern VALUE rb_cMajo_Result;
|
data/ext/majo/result.c
CHANGED
@@ -5,6 +5,9 @@ static void majo_result_mark(void *ptr)
|
|
5
5
|
majo_result *arg = (majo_result*)ptr;
|
6
6
|
rb_gc_mark(arg->newobj_trace);
|
7
7
|
rb_gc_mark(arg->freeobj_trace);
|
8
|
+
rb_gc_mark(arg->retained);
|
9
|
+
rb_gc_mark(arg->upper_lifetime);
|
10
|
+
rb_gc_mark(arg->lower_lifetime);
|
8
11
|
}
|
9
12
|
|
10
13
|
static int
|
@@ -50,6 +53,7 @@ static VALUE result_alloc(VALUE klass) {
|
|
50
53
|
arg->object_table = st_init_numtable();
|
51
54
|
arg->str_table = st_init_strtable();
|
52
55
|
arg->olds = NULL;
|
56
|
+
arg->retained = rb_ary_new();
|
53
57
|
|
54
58
|
return obj;
|
55
59
|
}
|
@@ -69,7 +73,7 @@ majo_result_append_info(majo_result *res, majo_allocation_info info) {
|
|
69
73
|
rb_darray_append(&res->olds, info);
|
70
74
|
}
|
71
75
|
|
72
|
-
VALUE
|
76
|
+
static VALUE
|
73
77
|
majo_result_allocations(VALUE self) {
|
74
78
|
majo_result *res = majo_check_result(self);
|
75
79
|
VALUE ary = rb_ary_new_capa(rb_darray_size(res->olds));
|
@@ -83,10 +87,37 @@ majo_result_allocations(VALUE self) {
|
|
83
87
|
return ary;
|
84
88
|
}
|
85
89
|
|
90
|
+
static VALUE
|
91
|
+
majo_result_retained(VALUE self) {
|
92
|
+
majo_result *res = majo_check_result(self);
|
93
|
+
return res->retained;
|
94
|
+
}
|
95
|
+
|
96
|
+
static VALUE
|
97
|
+
majo_result_store_retained_object(VALUE self, VALUE obj) {
|
98
|
+
majo_result *res = majo_check_result(self);
|
99
|
+
|
100
|
+
st_data_t value;
|
101
|
+
if (st_lookup(res->object_table, (st_data_t)obj, &value)) {
|
102
|
+
majo_allocation_info *info = (majo_allocation_info *)value;
|
103
|
+
int lifetime = (int)(rb_gc_count() - info->alloc_generation - 1);
|
104
|
+
if (NIL_P(res->upper_lifetime) || lifetime <= NUM2INT(res->upper_lifetime)) {
|
105
|
+
info->memsize = rb_obj_memsize_of(obj);
|
106
|
+
|
107
|
+
VALUE info_r = majo_new_allocation_info(info);
|
108
|
+
rb_ary_push(res->retained, info_r);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
return Qnil;
|
113
|
+
}
|
114
|
+
|
86
115
|
void
|
87
116
|
majo_init_result() {
|
88
117
|
rb_cMajo_Result = rb_define_class_under(rb_mMajo, "Result", rb_cObject);
|
89
118
|
rb_define_alloc_func(rb_cMajo_Result, result_alloc);
|
90
119
|
|
91
120
|
rb_define_method(rb_cMajo_Result, "allocations", majo_result_allocations, 0);
|
121
|
+
rb_define_method(rb_cMajo_Result, "retained", majo_result_retained, 0);
|
122
|
+
rb_define_method(rb_cMajo_Result, "store_retained_object", majo_result_store_retained_object, 1);
|
92
123
|
}
|
data/ext/majo/result.h
CHANGED
data/lib/majo/formatter/color.rb
CHANGED
@@ -8,32 +8,60 @@ module Majo
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def call
|
11
|
+
allocs = result.allocations
|
12
|
+
retained = result.retained
|
13
|
+
|
11
14
|
<<~RESULT
|
12
|
-
Total #{total_memory} bytes (#{total_objects} objects)
|
15
|
+
Total #{total_memory(allocs)} bytes (#{total_objects(allocs)} objects)
|
16
|
+
Total retained #{total_memory(retained)} bytes (#{total_objects(retained)} objects)
|
13
17
|
|
14
18
|
Memory by file
|
15
19
|
#{bar}
|
16
|
-
#{format_two_columns(memory_by_file)}
|
20
|
+
#{format_two_columns(memory_by_file(allocs))}
|
17
21
|
|
18
22
|
Memory by location
|
19
23
|
#{bar}
|
20
|
-
#{format_two_columns(memory_by_location)}
|
24
|
+
#{format_two_columns(memory_by_location(allocs))}
|
21
25
|
|
22
26
|
Memory by class
|
23
27
|
#{bar}
|
24
|
-
#{format_two_columns(memory_by_class)}
|
28
|
+
#{format_two_columns(memory_by_class(allocs))}
|
25
29
|
|
26
30
|
Objects by file
|
27
31
|
#{bar}
|
28
|
-
#{format_two_columns(objects_by_file)}
|
32
|
+
#{format_two_columns(objects_by_file(allocs))}
|
29
33
|
|
30
34
|
Objects by location
|
31
35
|
#{bar}
|
32
|
-
#{format_two_columns(objects_by_location)}
|
36
|
+
#{format_two_columns(objects_by_location(allocs))}
|
33
37
|
|
34
38
|
Objects by class
|
35
39
|
#{bar}
|
36
|
-
#{format_two_columns(objects_by_class)}
|
40
|
+
#{format_two_columns(objects_by_class(allocs))}
|
41
|
+
|
42
|
+
Retained Memory by file
|
43
|
+
#{bar}
|
44
|
+
#{format_two_columns(memory_by_file(retained))}
|
45
|
+
|
46
|
+
Retained Memory by location
|
47
|
+
#{bar}
|
48
|
+
#{format_two_columns(memory_by_location(retained))}
|
49
|
+
|
50
|
+
Retained Memory by class
|
51
|
+
#{bar}
|
52
|
+
#{format_two_columns(memory_by_class(retained))}
|
53
|
+
|
54
|
+
Retained Objects by file
|
55
|
+
#{bar}
|
56
|
+
#{format_two_columns(objects_by_file(retained))}
|
57
|
+
|
58
|
+
Retained Objects by location
|
59
|
+
#{bar}
|
60
|
+
#{format_two_columns(objects_by_location(retained))}
|
61
|
+
|
62
|
+
Retained Objects by class
|
63
|
+
#{bar}
|
64
|
+
#{format_two_columns(objects_by_class(retained))}
|
37
65
|
RESULT
|
38
66
|
end
|
39
67
|
|
@@ -45,45 +73,45 @@ module Majo
|
|
45
73
|
cyan '-----------------------------------'
|
46
74
|
end
|
47
75
|
|
48
|
-
def total_objects
|
76
|
+
def total_objects(allocs)
|
49
77
|
allocs.size
|
50
78
|
end
|
51
79
|
|
52
|
-
def total_memory
|
80
|
+
def total_memory(allocs)
|
53
81
|
allocs.sum(&:memsize)
|
54
82
|
end
|
55
83
|
|
56
|
-
def memory_by_file
|
84
|
+
def memory_by_file(allocs)
|
57
85
|
allocs.group_by(&:path).map do |path, allocations|
|
58
86
|
[allocations.sum(&:memsize), path]
|
59
87
|
end.sort_by(&:first).reverse
|
60
88
|
end
|
61
89
|
|
62
|
-
def memory_by_location
|
90
|
+
def memory_by_location(allocs)
|
63
91
|
allocs.group_by { |a| "#{a.path}:#{a.line}" }.map do |location, allocations|
|
64
92
|
[allocations.sum(&:memsize), location]
|
65
93
|
end.sort_by(&:first).reverse
|
66
94
|
end
|
67
95
|
|
68
|
-
def memory_by_class
|
96
|
+
def memory_by_class(allocs)
|
69
97
|
allocs.group_by(&:object_class_path).map do |class_path, allocations|
|
70
98
|
[allocations.sum(&:memsize), class_path]
|
71
99
|
end.sort_by(&:first).reverse
|
72
100
|
end
|
73
101
|
|
74
|
-
def objects_by_file
|
102
|
+
def objects_by_file(allocs)
|
75
103
|
allocs.group_by(&:path).map do |path, allocations|
|
76
104
|
[allocations.size, path]
|
77
105
|
end.sort_by(&:first).reverse
|
78
106
|
end
|
79
107
|
|
80
|
-
def objects_by_location
|
108
|
+
def objects_by_location(allocs)
|
81
109
|
allocs.group_by { |a| "#{a.path}:#{a.line}" }.map do |location, allocations|
|
82
110
|
[allocations.size, location]
|
83
111
|
end.sort_by(&:first).reverse
|
84
112
|
end
|
85
113
|
|
86
|
-
def objects_by_class
|
114
|
+
def objects_by_class(allocs)
|
87
115
|
allocs.group_by(&:object_class_path).map do |class_path, allocations|
|
88
116
|
[allocations.size, class_path]
|
89
117
|
end.sort_by(&:first).reverse
|
@@ -95,10 +123,6 @@ module Majo
|
|
95
123
|
max_length = data.max_by { |row| row[0].to_s.size }[0].to_s.size
|
96
124
|
data.map { |row| "#{blue(row[0].to_s.rjust(max_length))} #{row[1]}" }.join("\n")
|
97
125
|
end
|
98
|
-
|
99
|
-
def allocs
|
100
|
-
@allocs ||= result.allocations
|
101
|
-
end
|
102
126
|
end
|
103
127
|
end
|
104
128
|
end
|
data/lib/majo/formatter/csv.rb
CHANGED
@@ -9,15 +9,15 @@ module Majo
|
|
9
9
|
|
10
10
|
def call
|
11
11
|
::CSV.generate do |csv|
|
12
|
-
csv << ['Object class path', 'Class path', 'Method ID', 'Path', 'Line', 'Alloc generation', 'Free generation', 'Memsize', "Count"]
|
12
|
+
csv << ['Object class path', 'Class path', 'Method ID', 'Singleton method', 'Path', 'Line', 'Alloc generation', 'Free generation', 'Memsize', "Count"]
|
13
13
|
groups.each do |row, count|
|
14
|
-
csv << [row.object_class_path, row.class_path, row.method_id, row.path, row.line, row.alloc_generation, row.free_generation, row.memsize, count]
|
14
|
+
csv << [row.object_class_path, row.class_path, row.method_id, row.singleton?, row.path, row.line, row.alloc_generation, row.free_generation, row.memsize, count]
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
def groups
|
20
|
-
|
20
|
+
[*@result.allocations, *@result.retained].tally
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/majo/result.rb
CHANGED
data/lib/majo/version.rb
CHANGED
data/lib/majo.rb
CHANGED
@@ -10,22 +10,27 @@ require_relative 'majo/formatter'
|
|
10
10
|
module Majo
|
11
11
|
class Error < StandardError; end
|
12
12
|
|
13
|
-
def self.start
|
13
|
+
def self.start(upper_lifetime: nil, lower_lifetime: 1)
|
14
14
|
GC.start
|
15
15
|
GC.start
|
16
16
|
GC.start
|
17
|
-
__start
|
17
|
+
__start(upper_lifetime, lower_lifetime)
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.stop
|
21
21
|
GC.start
|
22
22
|
GC.start
|
23
23
|
GC.start
|
24
|
-
__stop
|
24
|
+
res = __stop
|
25
|
+
|
26
|
+
# Collect retained objcects
|
27
|
+
ObjectSpace.each_object do |obj|
|
28
|
+
res.store_retained_object(obj)
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
|
-
def self.run
|
28
|
-
r = start
|
32
|
+
def self.run(upper_lifetime: nil, lower_lifetime: 1)
|
33
|
+
r = start(upper_lifetime:, lower_lifetime:)
|
29
34
|
yield
|
30
35
|
r
|
31
36
|
ensure
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: majo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masataka Pocke Kuwabara
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A memory profiler focusing on long-lived objects.
|
14
14
|
email:
|
@@ -21,11 +21,14 @@ files:
|
|
21
21
|
- ".rspec"
|
22
22
|
- ".rubocop.yml"
|
23
23
|
- ARCHITECTURE.md
|
24
|
+
- CHANGELOG.md
|
24
25
|
- LICENSE
|
25
26
|
- README.md
|
26
27
|
- Rakefile
|
27
28
|
- ext/majo/allocation_info.c
|
28
29
|
- ext/majo/allocation_info.h
|
30
|
+
- ext/majo/attached_object.c
|
31
|
+
- ext/majo/attached_object.h
|
29
32
|
- ext/majo/darray.h
|
30
33
|
- ext/majo/extconf.rb
|
31
34
|
- ext/majo/majo.c
|
@@ -50,6 +53,7 @@ licenses:
|
|
50
53
|
metadata:
|
51
54
|
homepage_uri: https://github.com/pocke/majo
|
52
55
|
source_code_uri: https://github.com/pocke/majo
|
56
|
+
changelog_uri: https://github.com/pocke/majo/blob/master/CHANGELOG.md
|
53
57
|
post_install_message:
|
54
58
|
rdoc_options: []
|
55
59
|
require_paths:
|