majo 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +11 -0
- data/LICENSE +24 -0
- data/README.md +31 -0
- data/Rakefile +24 -0
- data/ext/majo/allocation_info.c +119 -0
- data/ext/majo/allocation_info.h +22 -0
- data/ext/majo/darray.h +218 -0
- data/ext/majo/extconf.rb +3 -0
- data/ext/majo/majo.c +148 -0
- data/ext/majo/majo.h +23 -0
- data/ext/majo/result.c +71 -0
- data/ext/majo/result.h +24 -0
- data/ext/majo/unique_str.c +73 -0
- data/ext/majo/unique_str.h +10 -0
- data/lib/majo/allocation_info.rb +23 -0
- data/lib/majo/formatter/color.rb +92 -0
- data/lib/majo/formatter/csv.rb +24 -0
- data/lib/majo/formatter.rb +6 -0
- data/lib/majo/result.rb +34 -0
- data/lib/majo/version.rb +5 -0
- data/lib/majo.rb +33 -0
- data/sig/majo.rbs +4 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 30b12103910ff82b8073d3882debca86928bc05d03e318b8c640e0c3b6f4c6ee
|
4
|
+
data.tar.gz: 851e43d87d7ba4020b5eb04487ae9859522bba5bd7f7a6765d02432efe2b8d74
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee86c8d4f321e54646ec82ee79168a8071ff641be47f9fd006ab70c9f254fbd958e6667c1e9dde36b00e911eee17d970a09a785dd53912f9bc4383101e649957
|
7
|
+
data.tar.gz: 3b707eee86c906409bd26f9b15e0e35aa1e6516e566b7d0f7e526b4fc76d74478110f75aa77b23041133d8b966999882c46d60515617121d7eec9cd41d9c3ac1
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
BSD 2-Clause License
|
2
|
+
|
3
|
+
Copyright (c) 2024, Masataka Pocke Kuwabara
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
9
|
+
list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
13
|
+
and/or other materials provided with the distribution.
|
14
|
+
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Majo
|
2
|
+
|
3
|
+
TODO: Delete this and the text below, and describe your gem
|
4
|
+
|
5
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/majo`. To experiment with that code, run `bin/console` for an interactive prompt.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
10
|
+
|
11
|
+
Install the gem and add to the application's Gemfile by executing:
|
12
|
+
|
13
|
+
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
|
14
|
+
|
15
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
16
|
+
|
17
|
+
$ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/pocke/majo.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
|
5
|
+
require "rubocop/rake_task"
|
6
|
+
|
7
|
+
RuboCop::RakeTask.new
|
8
|
+
|
9
|
+
require 'rake/testtask'
|
10
|
+
|
11
|
+
Rake::TestTask.new do |test|
|
12
|
+
test.libs << 'test'
|
13
|
+
test.test_files = Dir['test/**/*_test.rb']
|
14
|
+
test.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
require "rake/extensiontask"
|
18
|
+
|
19
|
+
Rake::ExtensionTask.new("majo")
|
20
|
+
|
21
|
+
CLEAN.add("{ext,lib}/**/*.{o,so,bundle}", "pkg")
|
22
|
+
|
23
|
+
task test: :compile
|
24
|
+
task default: %i[rubocop test]
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#include "majo.h"
|
2
|
+
|
3
|
+
static void majo_allocation_info_mark(void *ptr)
|
4
|
+
{
|
5
|
+
// TODO
|
6
|
+
majo_allocation_info *info = (majo_allocation_info*)ptr;
|
7
|
+
}
|
8
|
+
|
9
|
+
static void majo_allocation_info_free(majo_allocation_info *info) {
|
10
|
+
// TODO
|
11
|
+
ruby_xfree(info);
|
12
|
+
}
|
13
|
+
|
14
|
+
static size_t majo_allocation_info_memsize(const void *ptr) {
|
15
|
+
return sizeof(majo_allocation_info);
|
16
|
+
}
|
17
|
+
|
18
|
+
static rb_data_type_t allocation_info_type = {
|
19
|
+
"Majo::AllocationInfo",
|
20
|
+
{majo_allocation_info_mark, (RUBY_DATA_FUNC)majo_allocation_info_free, majo_allocation_info_memsize},
|
21
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
22
|
+
};
|
23
|
+
|
24
|
+
|
25
|
+
static VALUE
|
26
|
+
allocation_info_alloc(VALUE klass) {
|
27
|
+
majo_allocation_info *info;
|
28
|
+
VALUE obj = TypedData_Make_Struct(klass, majo_allocation_info, &allocation_info_type, info);
|
29
|
+
return obj;
|
30
|
+
}
|
31
|
+
|
32
|
+
VALUE
|
33
|
+
majo_new_allocation_info(majo_allocation_info *info) {
|
34
|
+
majo_allocation_info *new_info;
|
35
|
+
VALUE obj = TypedData_Make_Struct(rb_cMajo_AllocationInfo, majo_allocation_info, &allocation_info_type, new_info);
|
36
|
+
*new_info = *info;
|
37
|
+
return obj;
|
38
|
+
}
|
39
|
+
|
40
|
+
majo_allocation_info *
|
41
|
+
majo_check_allocation_info(VALUE obj) {
|
42
|
+
return rb_check_typeddata(obj, &allocation_info_type);
|
43
|
+
}
|
44
|
+
|
45
|
+
static VALUE
|
46
|
+
allocation_info_path(VALUE self) {
|
47
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
48
|
+
if (info->path) {
|
49
|
+
return rb_str_new2(info->path);
|
50
|
+
} else {
|
51
|
+
return Qnil;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
static VALUE
|
56
|
+
allocation_info_class_path(VALUE self) {
|
57
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
58
|
+
if (info->class_path) {
|
59
|
+
return rb_str_new2(info->class_path);
|
60
|
+
} else {
|
61
|
+
return Qnil;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
static VALUE
|
66
|
+
allocation_info_object_class_path(VALUE self) {
|
67
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
68
|
+
if (info->object_class_path) {
|
69
|
+
return rb_str_new2(info->object_class_path);
|
70
|
+
} else {
|
71
|
+
return Qnil;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
static VALUE
|
76
|
+
allocation_info_line(VALUE self) {
|
77
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
78
|
+
return LONG2NUM(info->line);
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
static VALUE
|
83
|
+
allocation_info_method_id(VALUE self) {
|
84
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
85
|
+
return info->mid;
|
86
|
+
}
|
87
|
+
|
88
|
+
static VALUE
|
89
|
+
allocation_info_alloc_generation(VALUE self) {
|
90
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
91
|
+
return SIZET2NUM(info->alloc_generation);
|
92
|
+
}
|
93
|
+
|
94
|
+
static VALUE
|
95
|
+
allocation_info_free_generation(VALUE self) {
|
96
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
97
|
+
return SIZET2NUM(info->free_generation);
|
98
|
+
}
|
99
|
+
|
100
|
+
static VALUE
|
101
|
+
allocation_info_memsize(VALUE self) {
|
102
|
+
majo_allocation_info *info = majo_check_allocation_info(self);
|
103
|
+
return SIZET2NUM(info->memsize);
|
104
|
+
}
|
105
|
+
|
106
|
+
void
|
107
|
+
majo_init_allocation_info() {
|
108
|
+
rb_cMajo_AllocationInfo = rb_define_class_under(rb_mMajo, "AllocationInfo", rb_cObject);
|
109
|
+
rb_define_alloc_func(rb_cMajo_AllocationInfo, allocation_info_alloc);
|
110
|
+
|
111
|
+
rb_define_method(rb_cMajo_AllocationInfo, "path", allocation_info_path, 0);
|
112
|
+
rb_define_method(rb_cMajo_AllocationInfo, "class_path", allocation_info_class_path, 0);
|
113
|
+
rb_define_method(rb_cMajo_AllocationInfo, "method_id", allocation_info_method_id, 0);
|
114
|
+
rb_define_method(rb_cMajo_AllocationInfo, "line", allocation_info_line, 0);
|
115
|
+
rb_define_method(rb_cMajo_AllocationInfo, "object_class_path", allocation_info_object_class_path, 0);
|
116
|
+
rb_define_method(rb_cMajo_AllocationInfo, "alloc_generation", allocation_info_alloc_generation, 0);
|
117
|
+
rb_define_method(rb_cMajo_AllocationInfo, "free_generation", allocation_info_free_generation, 0);
|
118
|
+
rb_define_method(rb_cMajo_AllocationInfo, "memsize", allocation_info_memsize, 0);
|
119
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#ifndef MAJO_ALLOCATION_INFO_H
|
2
|
+
#define MAJO_ALLOCATION_INFO_H
|
3
|
+
|
4
|
+
typedef struct {
|
5
|
+
const char *path;
|
6
|
+
const char *class_path;
|
7
|
+
const char *object_class_path;
|
8
|
+
unsigned long line;
|
9
|
+
VALUE mid;
|
10
|
+
|
11
|
+
size_t alloc_generation;
|
12
|
+
size_t free_generation;
|
13
|
+
size_t memsize;
|
14
|
+
} majo_allocation_info;
|
15
|
+
|
16
|
+
VALUE
|
17
|
+
majo_new_allocation_info(majo_allocation_info *info);
|
18
|
+
|
19
|
+
void
|
20
|
+
majo_init_allocation_info();
|
21
|
+
|
22
|
+
#endif
|
data/ext/majo/darray.h
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
// This file is copied from https://github.com/ruby/ruby and modified for this project.
|
2
|
+
//
|
3
|
+
// ----
|
4
|
+
//
|
5
|
+
// Copyright (C) 1993-2013 Yukihiro Matsumoto.
|
6
|
+
// Copyright (C) 2024 Masataka Kuwabara.
|
7
|
+
// All rights reserved.
|
8
|
+
//
|
9
|
+
// Redistribution and use in source and binary forms, with or without
|
10
|
+
// modification, are permitted provided that the following conditions
|
11
|
+
// are met:
|
12
|
+
// 1. Redistributions of source code must retain the above copyright
|
13
|
+
// notice, this list of conditions and the following disclaimer.
|
14
|
+
// 2. Redistributions in binary form must reproduce the above copyright
|
15
|
+
// notice, this list of conditions and the following disclaimer in the
|
16
|
+
// documentation and/or other materials provided with the distribution.
|
17
|
+
//
|
18
|
+
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
19
|
+
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
22
|
+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
24
|
+
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
25
|
+
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
26
|
+
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
27
|
+
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
28
|
+
// SUCH DAMAGE.
|
29
|
+
|
30
|
+
#ifndef RUBY_DARRAY_H
|
31
|
+
#define RUBY_DARRAY_H
|
32
|
+
|
33
|
+
#include <stdint.h>
|
34
|
+
#include <stddef.h>
|
35
|
+
#include <stdlib.h>
|
36
|
+
|
37
|
+
// Type for a dynamic array. Use to declare a dynamic array.
|
38
|
+
// It is a pointer so it fits in st_table nicely. Designed
|
39
|
+
// to be fairly type-safe.
|
40
|
+
//
|
41
|
+
// NULL is a valid empty dynamic array.
|
42
|
+
//
|
43
|
+
// Example:
|
44
|
+
// rb_darray(char) char_array = NULL;
|
45
|
+
// rb_darray_append(&char_array, 'e');
|
46
|
+
// printf("pushed %c\n", *rb_darray_ref(char_array, 0));
|
47
|
+
// rb_darray_free(char_array);
|
48
|
+
//
|
49
|
+
#define rb_darray(T) struct { rb_darray_meta_t meta; T data[]; } *
|
50
|
+
|
51
|
+
// Copy an element out of the array. Warning: not bounds checked.
|
52
|
+
//
|
53
|
+
// T rb_darray_get(rb_darray(T) ary, size_t idx);
|
54
|
+
//
|
55
|
+
#define rb_darray_get(ary, idx) ((ary)->data[(idx)])
|
56
|
+
|
57
|
+
// Assign to an element. Warning: not bounds checked.
|
58
|
+
//
|
59
|
+
// void rb_darray_set(rb_darray(T) ary, size_t idx, T element);
|
60
|
+
//
|
61
|
+
#define rb_darray_set(ary, idx, element) ((ary)->data[(idx)] = (element))
|
62
|
+
|
63
|
+
// Get a pointer to an element. Warning: not bounds checked.
|
64
|
+
//
|
65
|
+
// T *rb_darray_ref(rb_darray(T) ary, size_t idx);
|
66
|
+
//
|
67
|
+
#define rb_darray_ref(ary, idx) (&((ary)->data[(idx)]))
|
68
|
+
|
69
|
+
/* Copy a new element into the array. ptr_to_ary is evaluated multiple times.
|
70
|
+
*
|
71
|
+
* void rb_darray_append(rb_darray(T) *ptr_to_ary, T element);
|
72
|
+
*/
|
73
|
+
#define rb_darray_append(ptr_to_ary, element) \
|
74
|
+
rb_darray_append_impl(ptr_to_ary, element)
|
75
|
+
|
76
|
+
#define rb_darray_append_impl(ptr_to_ary, element) do { \
|
77
|
+
rb_darray_ensure_space((ptr_to_ary), \
|
78
|
+
sizeof(**(ptr_to_ary)), \
|
79
|
+
sizeof((*(ptr_to_ary))->data[0])); \
|
80
|
+
rb_darray_set(*(ptr_to_ary), \
|
81
|
+
(*(ptr_to_ary))->meta.size, \
|
82
|
+
(element)); \
|
83
|
+
(*(ptr_to_ary))->meta.size++; \
|
84
|
+
} while (0)
|
85
|
+
|
86
|
+
// Iterate over items of the array in a for loop
|
87
|
+
//
|
88
|
+
#define rb_darray_foreach(ary, idx_name, elem_ptr_var) \
|
89
|
+
for (size_t idx_name = 0; idx_name < rb_darray_size(ary) && ((elem_ptr_var) = rb_darray_ref(ary, idx_name)); ++idx_name)
|
90
|
+
|
91
|
+
// Iterate over valid indices in the array in a for loop
|
92
|
+
//
|
93
|
+
#define rb_darray_for(ary, idx_name) \
|
94
|
+
for (size_t idx_name = 0; idx_name < rb_darray_size(ary); ++idx_name)
|
95
|
+
|
96
|
+
/* Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
|
97
|
+
* Return 1 on success and 0 on failure.
|
98
|
+
*
|
99
|
+
* Note that NULL is a valid empty dynamic array.
|
100
|
+
*
|
101
|
+
* void rb_darray_make(rb_darray(T) *ptr_to_ary, size_t size);
|
102
|
+
*/
|
103
|
+
#define rb_darray_make(ptr_to_ary, size) \
|
104
|
+
rb_darray_make_impl((ptr_to_ary), size, sizeof(**(ptr_to_ary)), sizeof((*(ptr_to_ary))->data[0]))
|
105
|
+
|
106
|
+
/* Resize the darray to a new capacity. The new capacity must be greater than
|
107
|
+
* or equal to the size of the darray.
|
108
|
+
*
|
109
|
+
* void rb_darray_resize_capa(rb_darray(T) *ptr_to_ary, size_t capa);
|
110
|
+
*/
|
111
|
+
#define rb_darray_resize_capa(ptr_to_ary, capa) \
|
112
|
+
rb_darray_resize_capa_impl((ptr_to_ary), capa, sizeof(**(ptr_to_ary)), sizeof((*(ptr_to_ary))->data[0]))
|
113
|
+
|
114
|
+
#define rb_darray_data_ptr(ary) ((ary)->data)
|
115
|
+
|
116
|
+
typedef struct rb_darray_meta {
|
117
|
+
size_t size;
|
118
|
+
size_t capa;
|
119
|
+
} rb_darray_meta_t;
|
120
|
+
|
121
|
+
/* Set the size of the array to zero without freeing the backing memory.
|
122
|
+
* Allows reusing the same array. */
|
123
|
+
static inline void
|
124
|
+
rb_darray_clear(void *ary)
|
125
|
+
{
|
126
|
+
rb_darray_meta_t *meta = ary;
|
127
|
+
if (meta) {
|
128
|
+
meta->size = 0;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
// Get the size of the dynamic array.
|
133
|
+
//
|
134
|
+
static inline size_t
|
135
|
+
rb_darray_size(const void *ary)
|
136
|
+
{
|
137
|
+
const rb_darray_meta_t *meta = ary;
|
138
|
+
return meta ? meta->size : 0;
|
139
|
+
}
|
140
|
+
|
141
|
+
// Get the capacity of the dynamic array.
|
142
|
+
//
|
143
|
+
static inline size_t
|
144
|
+
rb_darray_capa(const void *ary)
|
145
|
+
{
|
146
|
+
const rb_darray_meta_t *meta = ary;
|
147
|
+
return meta ? meta->capa : 0;
|
148
|
+
}
|
149
|
+
|
150
|
+
/* Free the dynamic array. */
|
151
|
+
static inline void
|
152
|
+
rb_darray_free(void *ary)
|
153
|
+
{
|
154
|
+
free(ary);
|
155
|
+
}
|
156
|
+
|
157
|
+
/* Internal function. Resizes the capacity of a darray. The new capacity must
|
158
|
+
* be greater than or equal to the size of the darray. */
|
159
|
+
static inline void
|
160
|
+
rb_darray_resize_capa_impl(void *ptr_to_ary, size_t new_capa, size_t header_size, size_t element_size)
|
161
|
+
{
|
162
|
+
rb_darray_meta_t **ptr_to_ptr_to_meta = ptr_to_ary;
|
163
|
+
rb_darray_meta_t *meta = *ptr_to_ptr_to_meta;
|
164
|
+
|
165
|
+
rb_darray_meta_t *new_ary = realloc(meta, new_capa * element_size + header_size);
|
166
|
+
|
167
|
+
if (meta == NULL) {
|
168
|
+
/* First allocation. Initialize size. On subsequence allocations
|
169
|
+
* realloc takes care of carrying over the size. */
|
170
|
+
new_ary->size = 0;
|
171
|
+
}
|
172
|
+
|
173
|
+
RUBY_ASSERT(new_ary->size <= new_capa);
|
174
|
+
|
175
|
+
new_ary->capa = new_capa;
|
176
|
+
|
177
|
+
// We don't have access to the type of the dynamic array in function context.
|
178
|
+
// Write out result with memcpy to avoid strict aliasing issue.
|
179
|
+
memcpy(ptr_to_ary, &new_ary, sizeof(new_ary));
|
180
|
+
}
|
181
|
+
|
182
|
+
// Internal function
|
183
|
+
// Ensure there is space for one more element.
|
184
|
+
// Note: header_size can be bigger than sizeof(rb_darray_meta_t) when T is __int128_t, for example.
|
185
|
+
static inline void
|
186
|
+
rb_darray_ensure_space(void *ptr_to_ary, size_t header_size, size_t element_size)
|
187
|
+
{
|
188
|
+
rb_darray_meta_t **ptr_to_ptr_to_meta = ptr_to_ary;
|
189
|
+
rb_darray_meta_t *meta = *ptr_to_ptr_to_meta;
|
190
|
+
size_t current_capa = rb_darray_capa(meta);
|
191
|
+
if (rb_darray_size(meta) < current_capa) return;
|
192
|
+
|
193
|
+
// Double the capacity
|
194
|
+
size_t new_capa = current_capa == 0 ? 1 : current_capa * 2;
|
195
|
+
|
196
|
+
rb_darray_resize_capa_impl(ptr_to_ary, new_capa, header_size, element_size);
|
197
|
+
}
|
198
|
+
|
199
|
+
static inline void
|
200
|
+
rb_darray_make_impl(void *ptr_to_ary, size_t array_size, size_t header_size, size_t element_size)
|
201
|
+
{
|
202
|
+
rb_darray_meta_t **ptr_to_ptr_to_meta = ptr_to_ary;
|
203
|
+
if (array_size == 0) {
|
204
|
+
*ptr_to_ptr_to_meta = NULL;
|
205
|
+
return;
|
206
|
+
}
|
207
|
+
|
208
|
+
rb_darray_meta_t *meta = calloc(array_size * element_size + header_size, 1);
|
209
|
+
|
210
|
+
meta->size = array_size;
|
211
|
+
meta->capa = array_size;
|
212
|
+
|
213
|
+
// We don't have access to the type of the dynamic array in function context.
|
214
|
+
// Write out result with memcpy to avoid strict aliasing issue.
|
215
|
+
memcpy(ptr_to_ary, &meta, sizeof(meta));
|
216
|
+
}
|
217
|
+
|
218
|
+
#endif /* RUBY_DARRAY_H */
|
data/ext/majo/extconf.rb
ADDED
data/ext/majo/majo.c
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
#include "majo.h"
|
2
|
+
|
3
|
+
VALUE rb_mMajo;
|
4
|
+
VALUE rb_cMajo_Result;
|
5
|
+
VALUE rb_cMajo_AllocationInfo;
|
6
|
+
|
7
|
+
ID running_tracer_stack;
|
8
|
+
|
9
|
+
static bool
|
10
|
+
internal_object_p(VALUE obj)
|
11
|
+
{
|
12
|
+
switch (TYPE(obj)) {
|
13
|
+
case T_OBJECT:
|
14
|
+
case T_CLASS:
|
15
|
+
case T_MODULE:
|
16
|
+
case T_FLOAT:
|
17
|
+
case T_STRING:
|
18
|
+
case T_REGEXP:
|
19
|
+
case T_ARRAY:
|
20
|
+
case T_HASH:
|
21
|
+
case T_STRUCT:
|
22
|
+
case T_BIGNUM:
|
23
|
+
case T_FILE:
|
24
|
+
case T_DATA:
|
25
|
+
case T_MATCH:
|
26
|
+
case T_COMPLEX:
|
27
|
+
case T_RATIONAL:
|
28
|
+
case T_NIL:
|
29
|
+
case T_TRUE:
|
30
|
+
case T_FALSE:
|
31
|
+
case T_SYMBOL:
|
32
|
+
case T_FIXNUM:
|
33
|
+
return false;
|
34
|
+
default:
|
35
|
+
return true;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
static void
|
40
|
+
newobj_i(VALUE tpval, void *data)
|
41
|
+
{
|
42
|
+
majo_result *arg = (majo_result *)data;
|
43
|
+
|
44
|
+
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
45
|
+
VALUE obj = rb_tracearg_object(tparg);
|
46
|
+
|
47
|
+
if (internal_object_p(obj)) {
|
48
|
+
return;
|
49
|
+
}
|
50
|
+
|
51
|
+
VALUE path = rb_tracearg_path(tparg);
|
52
|
+
VALUE line = rb_tracearg_lineno(tparg);
|
53
|
+
VALUE mid = rb_tracearg_method_id(tparg);
|
54
|
+
VALUE klass = rb_tracearg_defined_class(tparg);
|
55
|
+
|
56
|
+
// TODO: when the st already has an entry for the value
|
57
|
+
majo_allocation_info *info = (majo_allocation_info *)malloc(sizeof(majo_allocation_info));
|
58
|
+
|
59
|
+
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;
|
63
|
+
|
64
|
+
VALUE obj_class = rb_obj_class(obj);
|
65
|
+
VALUE obj_class_path = (RTEST(obj_class) && !OBJ_FROZEN(obj_class)) ? rb_class_path_cached(obj_class) : Qnil;
|
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
|
+
|
68
|
+
|
69
|
+
info->path = path_cstr;
|
70
|
+
info->line = NUM2INT(line);
|
71
|
+
info->mid = mid;
|
72
|
+
info->object_class_path = obj_class_path_cstr;
|
73
|
+
|
74
|
+
info->class_path = class_path_cstr;
|
75
|
+
info->alloc_generation = rb_gc_count();
|
76
|
+
st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
|
77
|
+
}
|
78
|
+
|
79
|
+
static void
|
80
|
+
freeobj_i(VALUE tpval, void *data)
|
81
|
+
{
|
82
|
+
majo_result *arg = (majo_result *)data;
|
83
|
+
rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
|
84
|
+
st_data_t obj = (st_data_t)rb_tracearg_object(tparg);
|
85
|
+
st_data_t v;
|
86
|
+
|
87
|
+
// TODO refcount of the strings
|
88
|
+
if (st_delete(arg->object_table, &obj, &v)) {
|
89
|
+
majo_allocation_info *info = (majo_allocation_info *)v;
|
90
|
+
size_t gc_count = rb_gc_count();
|
91
|
+
// Reject it for majo
|
92
|
+
if (info->alloc_generation < gc_count-1) {
|
93
|
+
info->memsize = rb_obj_memsize_of((VALUE)obj);
|
94
|
+
info->free_generation = gc_count;
|
95
|
+
|
96
|
+
VALUE obj = rb_tracearg_object(tparg);
|
97
|
+
if (!internal_object_p(obj)) {
|
98
|
+
majo_result_append_info(arg, *info);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
free(info);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
static VALUE
|
106
|
+
start(VALUE self) {
|
107
|
+
VALUE res = majo_new_result();
|
108
|
+
majo_result *arg = majo_check_result(res);
|
109
|
+
|
110
|
+
VALUE stack = rb_ivar_get(rb_mMajo, running_tracer_stack);
|
111
|
+
rb_ary_push(stack, res);
|
112
|
+
|
113
|
+
arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
|
114
|
+
arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
|
115
|
+
|
116
|
+
rb_tracepoint_enable(arg->newobj_trace);
|
117
|
+
rb_tracepoint_enable(arg->freeobj_trace);
|
118
|
+
|
119
|
+
return res;
|
120
|
+
}
|
121
|
+
|
122
|
+
static VALUE
|
123
|
+
stop(VALUE self)
|
124
|
+
{
|
125
|
+
VALUE stack = rb_ivar_get(rb_mMajo, running_tracer_stack);
|
126
|
+
VALUE res = rb_ary_pop(stack);
|
127
|
+
majo_result *arg = majo_check_result(res);
|
128
|
+
|
129
|
+
rb_tracepoint_disable(arg->newobj_trace);
|
130
|
+
rb_tracepoint_disable(arg->freeobj_trace);
|
131
|
+
|
132
|
+
return res;
|
133
|
+
}
|
134
|
+
|
135
|
+
void
|
136
|
+
Init_majo(void)
|
137
|
+
{
|
138
|
+
rb_mMajo = rb_define_module("Majo");
|
139
|
+
|
140
|
+
rb_define_module_function(rb_mMajo, "__start", start, 0);
|
141
|
+
rb_define_module_function(rb_mMajo, "__stop", stop, 0);
|
142
|
+
|
143
|
+
running_tracer_stack = rb_intern("running_tracer_stack");
|
144
|
+
rb_ivar_set(rb_mMajo, running_tracer_stack, rb_ary_new());
|
145
|
+
|
146
|
+
majo_init_result();
|
147
|
+
majo_init_allocation_info();
|
148
|
+
}
|
data/ext/majo/majo.h
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#ifndef MAJO_H
|
2
|
+
#define MAJO_H 1
|
3
|
+
|
4
|
+
#include <stdbool.h>
|
5
|
+
|
6
|
+
#include "ruby.h"
|
7
|
+
#include "ruby/debug.h"
|
8
|
+
#include "ruby/internal/gc.h"
|
9
|
+
|
10
|
+
#include "darray.h"
|
11
|
+
|
12
|
+
#include "allocation_info.h"
|
13
|
+
#include "result.h"
|
14
|
+
#include "unique_str.h"
|
15
|
+
|
16
|
+
extern VALUE rb_mMajo;
|
17
|
+
extern VALUE rb_cMajo_Result;
|
18
|
+
extern VALUE rb_cMajo_AllocationInfo;
|
19
|
+
|
20
|
+
// Exposing the internal functions
|
21
|
+
size_t rb_obj_memsize_of(VALUE);
|
22
|
+
|
23
|
+
#endif
|
data/ext/majo/result.c
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#include "majo.h"
|
2
|
+
|
3
|
+
static void majo_result_mark(void *ptr)
|
4
|
+
{
|
5
|
+
majo_result *arg = (majo_result*)ptr;
|
6
|
+
rb_gc_mark(arg->newobj_trace);
|
7
|
+
rb_gc_mark(arg->freeobj_trace);
|
8
|
+
}
|
9
|
+
|
10
|
+
static void majo_result_free(majo_result *arg) {
|
11
|
+
// TODO
|
12
|
+
ruby_xfree(arg);
|
13
|
+
}
|
14
|
+
|
15
|
+
static size_t majo_result_memsize(const void *ptr) {
|
16
|
+
// TODO
|
17
|
+
return sizeof(majo_result);
|
18
|
+
}
|
19
|
+
|
20
|
+
static rb_data_type_t result_type = {
|
21
|
+
"Majo::Result",
|
22
|
+
{majo_result_mark, (RUBY_DATA_FUNC)majo_result_free, majo_result_memsize},
|
23
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
24
|
+
};
|
25
|
+
|
26
|
+
static VALUE result_alloc(VALUE klass) {
|
27
|
+
majo_result *arg;
|
28
|
+
VALUE obj = TypedData_Make_Struct(klass, majo_result, &result_type, arg);
|
29
|
+
arg->object_table = st_init_numtable();
|
30
|
+
arg->str_table = st_init_strtable();
|
31
|
+
arg->olds = NULL;
|
32
|
+
|
33
|
+
return obj;
|
34
|
+
}
|
35
|
+
|
36
|
+
VALUE
|
37
|
+
majo_new_result() {
|
38
|
+
return rb_class_new_instance(0, NULL, rb_cMajo_Result);
|
39
|
+
}
|
40
|
+
|
41
|
+
majo_result *
|
42
|
+
majo_check_result(VALUE obj) {
|
43
|
+
return rb_check_typeddata(obj, &result_type);
|
44
|
+
}
|
45
|
+
|
46
|
+
void
|
47
|
+
majo_result_append_info(majo_result *res, majo_allocation_info info) {
|
48
|
+
rb_darray_append(&res->olds, info);
|
49
|
+
}
|
50
|
+
|
51
|
+
VALUE
|
52
|
+
majo_result_allocations(VALUE self) {
|
53
|
+
majo_result *res = majo_check_result(self);
|
54
|
+
VALUE ary = rb_ary_new_capa(rb_darray_size(res->olds));
|
55
|
+
|
56
|
+
majo_allocation_info *info_ptr;
|
57
|
+
rb_darray_foreach(res->olds, i, info_ptr) {
|
58
|
+
VALUE v = majo_new_allocation_info(info_ptr);
|
59
|
+
rb_ary_push(ary, v);
|
60
|
+
}
|
61
|
+
|
62
|
+
return ary;
|
63
|
+
}
|
64
|
+
|
65
|
+
void
|
66
|
+
majo_init_result() {
|
67
|
+
rb_cMajo_Result = rb_define_class_under(rb_mMajo, "Result", rb_cObject);
|
68
|
+
rb_define_alloc_func(rb_cMajo_Result, result_alloc);
|
69
|
+
|
70
|
+
rb_define_method(rb_cMajo_Result, "allocations", majo_result_allocations, 0);
|
71
|
+
}
|
data/ext/majo/result.h
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef MAJO_RESULT_H
|
2
|
+
#define MAJO_RESULT_H
|
3
|
+
|
4
|
+
typedef struct {
|
5
|
+
st_table *object_table; /* obj (VALUE) -> allocation_info */
|
6
|
+
st_table *str_table; /* cstr -> refcount */
|
7
|
+
rb_darray(majo_allocation_info) olds;
|
8
|
+
VALUE newobj_trace;
|
9
|
+
VALUE freeobj_trace;
|
10
|
+
} majo_result;
|
11
|
+
|
12
|
+
VALUE
|
13
|
+
majo_new_result();
|
14
|
+
|
15
|
+
majo_result *
|
16
|
+
majo_check_result(VALUE obj);
|
17
|
+
|
18
|
+
void
|
19
|
+
majo_result_append_info(majo_result *res, majo_allocation_info info);
|
20
|
+
|
21
|
+
void
|
22
|
+
majo_init_result();
|
23
|
+
|
24
|
+
#endif
|
@@ -0,0 +1,73 @@
|
|
1
|
+
// This file is copied from https://github.com/ruby/ruby and modified for this project.
|
2
|
+
//
|
3
|
+
// ----
|
4
|
+
//
|
5
|
+
// Copyright (C) 1993-2013 Yukihiro Matsumoto.
|
6
|
+
// Copyright (C) 2024 Masataka Kuwabara.
|
7
|
+
// All rights reserved.
|
8
|
+
//
|
9
|
+
// Redistribution and use in source and binary forms, with or without
|
10
|
+
// modification, are permitted provided that the following conditions
|
11
|
+
// are met:
|
12
|
+
// 1. Redistributions of source code must retain the above copyright
|
13
|
+
// notice, this list of conditions and the following disclaimer.
|
14
|
+
// 2. Redistributions in binary form must reproduce the above copyright
|
15
|
+
// notice, this list of conditions and the following disclaimer in the
|
16
|
+
// documentation and/or other materials provided with the distribution.
|
17
|
+
//
|
18
|
+
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
19
|
+
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
21
|
+
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
22
|
+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
24
|
+
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
25
|
+
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
26
|
+
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
27
|
+
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
28
|
+
// SUCH DAMAGE.
|
29
|
+
|
30
|
+
#include "majo.h"
|
31
|
+
|
32
|
+
const char *
|
33
|
+
majo_make_unique_str(st_table *tbl, const char *str, long len)
|
34
|
+
{
|
35
|
+
if (!str) {
|
36
|
+
return NULL;
|
37
|
+
}
|
38
|
+
else {
|
39
|
+
st_data_t n;
|
40
|
+
char *result;
|
41
|
+
|
42
|
+
if (st_lookup(tbl, (st_data_t)str, &n)) {
|
43
|
+
st_insert(tbl, (st_data_t)str, n+1);
|
44
|
+
st_get_key(tbl, (st_data_t)str, &n);
|
45
|
+
result = (char *)n;
|
46
|
+
}
|
47
|
+
else {
|
48
|
+
result = (char *)malloc(len+1);
|
49
|
+
strncpy(result, str, len);
|
50
|
+
result[len] = 0;
|
51
|
+
st_add_direct(tbl, (st_data_t)result, 1);
|
52
|
+
}
|
53
|
+
return result;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
void
|
58
|
+
majo_delete_unique_str(st_table *tbl, const char *str)
|
59
|
+
{
|
60
|
+
if (str) {
|
61
|
+
st_data_t n;
|
62
|
+
|
63
|
+
st_lookup(tbl, (st_data_t)str, &n);
|
64
|
+
if (n == 1) {
|
65
|
+
n = (st_data_t)str;
|
66
|
+
st_delete(tbl, &n, 0);
|
67
|
+
free((char *)n);
|
68
|
+
}
|
69
|
+
else {
|
70
|
+
st_insert(tbl, (st_data_t)str, n-1);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Majo
|
2
|
+
class AllocationInfo
|
3
|
+
def inspect
|
4
|
+
"#<Majo::AllocationInfo #{object_class_path} #{class_path}##{method_id} #{path}:#{line} gen#{alloc_generation}..#{free_generation}>"
|
5
|
+
end
|
6
|
+
|
7
|
+
def hash
|
8
|
+
path.hash ^ class_path.hash ^ method_id.hash ^ line.hash ^ object_class_path.hash ^ alloc_generation.hash ^ free_generation.hash ^ memsize.hash
|
9
|
+
end
|
10
|
+
|
11
|
+
def eql?(other)
|
12
|
+
other.is_a?(AllocationInfo) &&
|
13
|
+
path == other.path &&
|
14
|
+
class_path == other.class_path &&
|
15
|
+
method_id == other.method_id &&
|
16
|
+
line == other.line &&
|
17
|
+
object_class_path == other.object_class_path &&
|
18
|
+
alloc_generation == other.alloc_generation &&
|
19
|
+
free_generation == other.free_generation &&
|
20
|
+
memsize == other.memsize
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Majo
|
2
|
+
module Formatter
|
3
|
+
class Color
|
4
|
+
def initialize(result)
|
5
|
+
@result = result
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
<<~RESULT
|
10
|
+
Total #{total_memory} bytes (#{total_objects} objects)
|
11
|
+
|
12
|
+
Memory by file
|
13
|
+
#{format_two_columns(memory_by_file)}
|
14
|
+
|
15
|
+
Memory by location
|
16
|
+
#{format_two_columns(memory_by_location)}
|
17
|
+
|
18
|
+
Memory by class
|
19
|
+
#{format_two_columns(memory_by_class)}
|
20
|
+
|
21
|
+
Objects by file
|
22
|
+
#{format_two_columns(objects_by_file)}
|
23
|
+
|
24
|
+
Objects by location
|
25
|
+
#{format_two_columns(objects_by_location)}
|
26
|
+
|
27
|
+
Objects by class
|
28
|
+
#{format_two_columns(objects_by_class)}
|
29
|
+
RESULT
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :result
|
35
|
+
|
36
|
+
def total_objects
|
37
|
+
allocs.size
|
38
|
+
end
|
39
|
+
|
40
|
+
def total_memory
|
41
|
+
allocs.sum(&:memsize)
|
42
|
+
end
|
43
|
+
|
44
|
+
def memory_by_file
|
45
|
+
allocs.group_by(&:path).map do |path, allocations|
|
46
|
+
[allocations.sum(&:memsize), path]
|
47
|
+
end.sort_by(&:first).reverse
|
48
|
+
end
|
49
|
+
|
50
|
+
def memory_by_location
|
51
|
+
allocs.group_by { |a| "#{a.path}:#{a.line}" }.map do |location, allocations|
|
52
|
+
[allocations.sum(&:memsize), location]
|
53
|
+
end.sort_by(&:first).reverse
|
54
|
+
end
|
55
|
+
|
56
|
+
def memory_by_class
|
57
|
+
allocs.group_by(&:object_class_path).map do |class_path, allocations|
|
58
|
+
[allocations.sum(&:memsize), class_path]
|
59
|
+
end.sort_by(&:first).reverse
|
60
|
+
end
|
61
|
+
|
62
|
+
def objects_by_file
|
63
|
+
allocs.group_by(&:path).map do |path, allocations|
|
64
|
+
[allocations.size, path]
|
65
|
+
end.sort_by(&:first).reverse
|
66
|
+
end
|
67
|
+
|
68
|
+
def objects_by_location
|
69
|
+
allocs.group_by { |a| "#{a.path}:#{a.line}" }.map do |location, allocations|
|
70
|
+
[allocations.size, location]
|
71
|
+
end.sort_by(&:first).reverse
|
72
|
+
end
|
73
|
+
|
74
|
+
def objects_by_class
|
75
|
+
allocs.group_by(&:object_class_path).map do |class_path, allocations|
|
76
|
+
[allocations.size, class_path]
|
77
|
+
end.sort_by(&:first).reverse
|
78
|
+
end
|
79
|
+
|
80
|
+
def format_two_columns(data)
|
81
|
+
return "" if data.empty?
|
82
|
+
|
83
|
+
max_length = data.max_by { |row| row[0].to_s.size }[0].size
|
84
|
+
data.map { |row| "#{row[0].to_s.ljust(max_length)} #{row[1]}" }.join("\n")
|
85
|
+
end
|
86
|
+
|
87
|
+
def allocs
|
88
|
+
@allocs ||= result.allocations
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Majo
|
4
|
+
module Formatter
|
5
|
+
class CSV
|
6
|
+
def initialize(result)
|
7
|
+
@result = result
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
::CSV.generate do |csv|
|
12
|
+
csv << ['Object class path', 'Class path', 'Method ID', 'Path', 'Line', 'Alloc generation', 'Free generation', 'Memsize', "Count"]
|
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]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def groups
|
20
|
+
@result.allocations.tally
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/majo/result.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Majo
|
2
|
+
class Result
|
3
|
+
def report(out: $stdout, formatter: nil)
|
4
|
+
fmt =
|
5
|
+
case formatter
|
6
|
+
when nil, :color
|
7
|
+
Formatter::Color
|
8
|
+
when :csv
|
9
|
+
Formatter::CSV
|
10
|
+
else
|
11
|
+
raise "unknown formatter: #{formatter.inspect}"
|
12
|
+
end
|
13
|
+
|
14
|
+
with_output(out) do |io|
|
15
|
+
io.puts fmt.new(self).call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def with_output(out)
|
22
|
+
case
|
23
|
+
when out.is_a?(IO)
|
24
|
+
yield out
|
25
|
+
when out.is_a?(String)
|
26
|
+
File.open(out, "w") { |io| yield io }
|
27
|
+
when out.respond_to?(:to_path)
|
28
|
+
File.open(out.to_path, "w") { |io| yield io }
|
29
|
+
else
|
30
|
+
raise ArgumentError, "out must be an IO or a String"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/majo/version.rb
ADDED
data/lib/majo.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'majo.so'
|
4
|
+
require_relative "majo/version"
|
5
|
+
require_relative 'majo/allocation_info'
|
6
|
+
require_relative 'majo/result'
|
7
|
+
require_relative 'majo/formatter'
|
8
|
+
|
9
|
+
module Majo
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
def self.start
|
13
|
+
GC.start
|
14
|
+
GC.start
|
15
|
+
GC.start
|
16
|
+
__start
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.stop
|
20
|
+
GC.start
|
21
|
+
GC.start
|
22
|
+
GC.start
|
23
|
+
__stop
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.run
|
27
|
+
r = start
|
28
|
+
yield
|
29
|
+
r
|
30
|
+
ensure
|
31
|
+
stop
|
32
|
+
end
|
33
|
+
end
|
data/sig/majo.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: majo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masataka Pocke Kuwabara
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 2024-07-22 00:00:00.000000000 Z
|
11
|
+
dependencies: []
|
12
|
+
description: ''
|
13
|
+
email:
|
14
|
+
- kuwabara@pocke.me
|
15
|
+
executables: []
|
16
|
+
extensions:
|
17
|
+
- ext/majo/extconf.rb
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".rspec"
|
21
|
+
- ".rubocop.yml"
|
22
|
+
- LICENSE
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- ext/majo/allocation_info.c
|
26
|
+
- ext/majo/allocation_info.h
|
27
|
+
- ext/majo/darray.h
|
28
|
+
- ext/majo/extconf.rb
|
29
|
+
- ext/majo/majo.c
|
30
|
+
- ext/majo/majo.h
|
31
|
+
- ext/majo/result.c
|
32
|
+
- ext/majo/result.h
|
33
|
+
- ext/majo/unique_str.c
|
34
|
+
- ext/majo/unique_str.h
|
35
|
+
- lib/majo.rb
|
36
|
+
- lib/majo/allocation_info.rb
|
37
|
+
- lib/majo/formatter.rb
|
38
|
+
- lib/majo/formatter/color.rb
|
39
|
+
- lib/majo/formatter/csv.rb
|
40
|
+
- lib/majo/result.rb
|
41
|
+
- lib/majo/version.rb
|
42
|
+
- sig/majo.rbs
|
43
|
+
homepage: https://github.com/pocke/majo
|
44
|
+
licenses:
|
45
|
+
- BSD-2-Clause
|
46
|
+
metadata:
|
47
|
+
homepage_uri: https://github.com/pocke/majo
|
48
|
+
source_code_uri: https://github.com/pocke/majo
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 3.1.0
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubygems_version: 3.6.0.dev
|
64
|
+
specification_version: 4
|
65
|
+
summary: ''
|
66
|
+
test_files: []
|