rbkit 0.0.1

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.
@@ -0,0 +1,52 @@
1
+ require "zmq"
2
+ require "pp"
3
+ require "thread"
4
+
5
+ $ctx = ZMQ::Context.new
6
+ Thread.abort_on_exception = true
7
+
8
+ ENDPOINT = "tcp://127.0.0.1:5555"
9
+
10
+ class Server
11
+ @@publish_topic = "foo"
12
+
13
+ def start_publisher
14
+ server_socket = $ctx.socket(:PUB)
15
+ # server_socket.verbose = true
16
+ server_socket.bind(ENDPOINT)
17
+ server_socket.linger = 0
18
+ loop do
19
+ server_socket.sendm(@@publish_topic)
20
+ server_socket.send("hello #{Time.now}")
21
+
22
+ server_socket.sendm("lol")
23
+ server_socket.send("for lol #{Time.now}")
24
+ sleep(1)
25
+ end
26
+ end
27
+
28
+ def start_listener
29
+ listener_socket = $ctx.socket(:SUB)
30
+ listener_socket.subscribe("bar")
31
+ listener_socket.verbose = true
32
+ listener_socket.connect(ENDPOINT)
33
+
34
+ loop do
35
+ data = listener_socket.recv
36
+ puts "********** Received from client #{data}"
37
+ end
38
+ end
39
+ end
40
+
41
+ server = Server.new()
42
+ threads = []
43
+
44
+ threads << Thread.new do
45
+ server.start_publisher
46
+ end
47
+
48
+ threads << Thread.new do
49
+ server.start_listener
50
+ end
51
+
52
+ threads.each { |thr| thr.join }
@@ -0,0 +1,18 @@
1
+ require 'mkmf'
2
+ if ENV['RBKIT_DEV']
3
+ $stderr.puts "Dev environment enabled."
4
+ $CFLAGS << ' -g'
5
+ $defs << '-DRBKIT_DEV'
6
+ end
7
+ if(have_func('rb_postponed_job_register_one') &&
8
+ have_func('rb_profile_frames') &&
9
+ have_func('rb_tracepoint_new') &&
10
+ have_const('RUBY_INTERNAL_EVENT_NEWOBJ') &&
11
+ have_library("zmq") &&
12
+ have_header("zmq.h") &&
13
+ have_library("msgpack") &&
14
+ have_header("msgpack.h"))
15
+ create_makefile('rbkit_tracer')
16
+ else
17
+ fail 'missing API: are you using ruby 2.1+?'
18
+ end
@@ -0,0 +1,66 @@
1
+ #include <assert.h>
2
+ #include "zmq.h"
3
+ #include "rbkit_message_aggregator.h"
4
+
5
+ static msgpack_sbuffer * sbuf;
6
+ static void* message_array;
7
+ static size_t used_memsize;
8
+ static size_t total_capacity;
9
+ static size_t no_of_messages;
10
+
11
+ static int has_enough_space_for(size_t size) {
12
+ return ((total_capacity - used_memsize) >= size);
13
+ }
14
+
15
+ static void double_the_capacity() {
16
+ total_capacity *= 2;
17
+ message_array = realloc(message_array, total_capacity);
18
+ assert(message_array);
19
+ }
20
+
21
+ void message_list_new() {
22
+ size_t initial_size = 1024; // Reserve 1 KB of memory
23
+ message_array = malloc(initial_size);
24
+ total_capacity = initial_size;
25
+ used_memsize = 0;
26
+ no_of_messages = 0;
27
+ }
28
+
29
+ void message_list_destroy() {
30
+ free(message_array);
31
+ used_memsize = 0;
32
+ total_capacity = 0;
33
+ no_of_messages = 0;
34
+ }
35
+
36
+ void message_list_clear() {
37
+ used_memsize = 0;
38
+ no_of_messages = 0;
39
+ }
40
+
41
+ // Copies the msgpack sbuffer to the end of
42
+ // a dynamically growing array
43
+ void add_message(msgpack_sbuffer *buffer) {
44
+ while(!has_enough_space_for(buffer->size))
45
+ double_the_capacity();
46
+ memcpy(message_array + used_memsize, buffer->data, buffer->size);
47
+ used_memsize += buffer->size;
48
+ no_of_messages += 1;
49
+ }
50
+
51
+ // Creates a message containing all the available
52
+ // msgpack sbuffers in the array
53
+ void get_event_collection_message(msgpack_sbuffer *sbuf) {
54
+ if(no_of_messages > 0) {
55
+ msgpack_packer *pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write);
56
+ pack_event_header(pk, "event_collection", 3);
57
+ pack_string(pk, "payload");
58
+ msgpack_pack_array(pk, no_of_messages);
59
+ sbuf->data = realloc(sbuf->data, used_memsize + sbuf->size);
60
+ assert(sbuf->data);
61
+ memcpy(sbuf->data + sbuf->size, message_array, used_memsize);
62
+ sbuf->size += used_memsize;
63
+
64
+ msgpack_packer_free(pk);
65
+ }
66
+ }
@@ -0,0 +1,13 @@
1
+ #ifndef RBKIT_MESSAGE_AGGREGATOR_H
2
+ #define RBKIT_MESSAGE_AGGREGATOR_H
3
+
4
+ #include "msgpack.h"
5
+ #include "zmq.h"
6
+
7
+ void message_list_new();
8
+ void add_message(msgpack_sbuffer *);
9
+ void get_event_collection_message(msgpack_sbuffer *);
10
+ void message_list_destroy();
11
+ void message_list_clear();
12
+
13
+ #endif
@@ -0,0 +1,154 @@
1
+ #include <ruby.h>
2
+ #include "rbkit_object_graph.h"
3
+
4
+ rbkit_object_dump_page * rbkit_object_dump_page_new() {
5
+ rbkit_object_dump_page *page = malloc(sizeof(rbkit_object_dump_page));
6
+ page->count = 0;
7
+ page->next = NULL;
8
+ return page;
9
+ }
10
+
11
+ rbkit_object_data * initialize_object_data(rbkit_object_dump *dump) {
12
+ if(dump->first == NULL) {
13
+ rbkit_object_dump_page *page = rbkit_object_dump_page_new();
14
+ dump->first = page;
15
+ dump->last = page;
16
+ dump->page_count = 1;
17
+ dump->object_count = 0;
18
+ } else if (dump->last->count == RBKIT_OBJECT_DUMP_PAGE_SIZE) {
19
+ rbkit_object_dump_page *page = rbkit_object_dump_page_new();
20
+ dump->last->next = page;
21
+ dump->last = page;
22
+ dump->page_count++;
23
+ }
24
+ rbkit_object_data *data = &(dump->last->data[dump->last->count]);
25
+ data->references = NULL;
26
+ data->class_name = NULL;
27
+ data->reference_count = 0;
28
+ data->file = NULL;
29
+ data->line = 0;
30
+ data->size = 0;
31
+ return(data);
32
+ }
33
+
34
+ static void set_size(VALUE obj, rbkit_object_data * data) {
35
+ size_t size;
36
+ if ((size = rb_obj_memsize_of(obj)) > 0)
37
+ data->size = size;
38
+ }
39
+
40
+ static void dump_root_object(VALUE obj, const char* category, rbkit_object_dump *dump) {
41
+ rbkit_object_data *data = initialize_object_data(dump);
42
+
43
+ //Set object id
44
+ data->object_id = (void *)obj;
45
+ //Set classname
46
+ data->class_name = NULL;
47
+ //Set classname of "symbols" category as "Symbol" to match <symbol>.class output
48
+ if(strcmp(category, "symbols") == 0){
49
+ data->class_name = "Symbol" ;
50
+ } else {
51
+ data->class_name = category ;
52
+ }
53
+
54
+ //Set file path and line no where object is defined
55
+ struct allocation_info *info;
56
+ if (st_lookup(dump->object_table, obj, (st_data_t *)&info)) {
57
+ if(info) {
58
+ data->file = info->path;
59
+ data->line = info->line;
60
+ }
61
+ }
62
+
63
+ set_size(obj, data);
64
+ // Update total number of objects
65
+ dump->object_count++;
66
+ // Update number of objects on last page
67
+ dump->last->count++;
68
+ }
69
+
70
+ static void reachable_object_i(VALUE ref, rbkit_object_data *data)
71
+ {
72
+ if(RBASIC_CLASS(ref) == ref)
73
+ return;
74
+
75
+ data->reference_count ++;
76
+ if (data->reference_count == 1) {
77
+ data->references = malloc(sizeof(void *));
78
+ } else {
79
+ data->references = realloc(data->references, data->reference_count * sizeof(void *) );
80
+ }
81
+ data->references[data->reference_count - 1] = (void *)ref;
82
+ }
83
+
84
+ static void dump_heap_object(VALUE obj, rbkit_object_dump *dump) {
85
+
86
+ // Get next available slot from page
87
+ rbkit_object_data *data = initialize_object_data(dump);
88
+ //Set object id
89
+ data->object_id = (void *)obj;
90
+
91
+ //Set classname
92
+ VALUE klass = RBASIC_CLASS(obj);
93
+ data->class_name = rb_class2name(klass);
94
+
95
+ //Set references
96
+ rb_objspace_reachable_objects_from(obj, reachable_object_i, data);
97
+
98
+ //Set file path and line no where object is defined
99
+ struct allocation_info *info;
100
+ if (st_lookup(dump->object_table, obj, (st_data_t *)&info)) {
101
+ if(info) {
102
+ data->file = info->path;
103
+ data->line = info->line;
104
+ }
105
+ }
106
+
107
+ set_size(obj, data);
108
+ dump->object_count++;
109
+ dump->last->count++;
110
+ }
111
+
112
+ /*
113
+ * The following categories of objects are directly accessible from the root:
114
+ * ["vm", "machine_context", "symbols", "global_list", "end_proc", "global_tbl"]
115
+ */
116
+ static void root_object_i(const char *category, VALUE obj, void *dump_data)
117
+ {
118
+ dump_root_object(obj, category, (rbkit_object_dump *)dump_data);
119
+ }
120
+
121
+ /*
122
+ * Iterator that walks over heap pages
123
+ */
124
+ static int heap_obj_i(void *vstart, void *vend, size_t stride, void *dump_data)
125
+ {
126
+ VALUE obj = (VALUE)vstart;
127
+ VALUE klass ;
128
+
129
+ for (; obj != (VALUE)vend; obj += stride) {
130
+ klass = RBASIC_CLASS(obj);
131
+ if (!NIL_P(klass) && BUILTIN_TYPE(obj) != T_NONE && BUILTIN_TYPE(obj) != T_ZOMBIE && BUILTIN_TYPE(obj) != T_ICLASS) {
132
+ dump_heap_object(obj, (rbkit_object_dump *)dump_data);
133
+ }
134
+ }
135
+ return 0;
136
+ }
137
+
138
+ static void collect_root_objects(rbkit_object_dump * dump) {
139
+ rb_objspace_reachable_objects_from_root(root_object_i, (void *)dump);
140
+ }
141
+
142
+ static void collect_heap_objects(rbkit_object_dump * dump) {
143
+ rb_objspace_each_objects(heap_obj_i, (void *)dump);
144
+ }
145
+
146
+ rbkit_object_dump * get_object_dump(st_table * object_table) {
147
+ rbkit_object_dump * dump = (rbkit_object_dump *) malloc(sizeof(rbkit_object_dump));
148
+ dump->object_table = object_table;
149
+ dump->first = NULL;
150
+ dump->last = NULL;
151
+ collect_root_objects(dump);
152
+ collect_heap_objects(dump);
153
+ return dump;
154
+ }
@@ -0,0 +1,39 @@
1
+ #ifndef RBKIT_OBJECT_GRAPH
2
+ #define RBKIT_OBJECT_GRAPH
3
+
4
+ #define RBKIT_OBJECT_DUMP_PAGE_SIZE 1000
5
+
6
+ typedef struct _rbkit_object_data {
7
+ const void * object_id;
8
+ const char * class_name;
9
+ void ** references;
10
+ size_t reference_count;
11
+ char * file;
12
+ unsigned long line;
13
+ size_t size;
14
+ } rbkit_object_data;
15
+
16
+ typedef struct _rbkit_object_dump_page {
17
+ rbkit_object_data data[RBKIT_OBJECT_DUMP_PAGE_SIZE];
18
+ size_t count;
19
+ struct _rbkit_object_dump_page *next;
20
+ } rbkit_object_dump_page;
21
+
22
+ typedef struct _rbkit_object_dump {
23
+ size_t page_count;
24
+ size_t object_count;
25
+ rbkit_object_dump_page *first;
26
+ rbkit_object_dump_page *last;
27
+ st_table * object_table;
28
+ } rbkit_object_dump;
29
+
30
+ rbkit_object_dump * get_object_dump();
31
+
32
+ struct allocation_info {
33
+ const char *path;
34
+ unsigned long line;
35
+ const char *class_path;
36
+ VALUE method_id;
37
+ size_t generation;
38
+ };
39
+ #endif
@@ -0,0 +1,583 @@
1
+ //
2
+ // rbkit.c
3
+ // gather_stats
4
+ //
5
+ // Created by Hemant Kumar on 26/04/14.
6
+ // Copyright (c) 2014 Codemancers. All rights reserved.
7
+ //
8
+
9
+ #include "rbkit_tracer.h"
10
+ #include "rbkit_object_graph.h"
11
+ #include "rbkit_message_aggregator.h"
12
+ #include <sys/time.h>
13
+
14
+ static const char *event_names[] = {
15
+ "gc_start",
16
+ "gc_end_m",
17
+ "gc_end_s",
18
+ "obj_created",
19
+ "obj_destroyed"
20
+ };
21
+
22
+ static struct gc_hooks *logger;
23
+ static int tmp_keep_remains;
24
+ static void *zmq_publisher;
25
+ static void *zmq_context;
26
+ static void *zmq_response_socket;
27
+ static zmq_pollitem_t items[1];
28
+
29
+ void pack_event_header(msgpack_packer* packer, const char *event_type, int map_size)
30
+ {
31
+ msgpack_pack_map(packer, map_size);
32
+ pack_string(packer, "event_type");
33
+ pack_string(packer, event_type);
34
+
35
+ pack_string(packer, "timestamp");
36
+ pack_timestamp(packer);
37
+ }
38
+
39
+
40
+ static void trace_gc_invocation(void *data, int event_index) {
41
+ if (event_index == 0) {
42
+ msgpack_sbuffer_clear(logger->sbuf);
43
+ pack_event_header(logger->msgpacker, event_names[event_index], 2);
44
+ add_message(logger->sbuf);
45
+ } else if (event_index == 2) {
46
+ msgpack_sbuffer_clear(logger->sbuf);
47
+ pack_event_header(logger->msgpacker, event_names[event_index], 2);
48
+ add_message(logger->sbuf);
49
+ }
50
+ }
51
+
52
+ static struct gc_hooks * get_trace_logger() {
53
+ int i = 0;
54
+ if (logger == 0) {
55
+ logger = ALLOC_N(struct gc_hooks, 1);
56
+ logger->enabled = Qfalse;
57
+ logger->newobj_trace = 0;
58
+ logger->freeobj_trace = 0;
59
+ logger->keep_remains = tmp_keep_remains;
60
+ logger->object_table = st_init_numtable();
61
+ logger->str_table = st_init_strtable();
62
+
63
+ for (i = 0; i < 3; i++) {
64
+ logger->funcs[i] = trace_gc_invocation;
65
+ logger->args[i] = (void *)event_names[i];
66
+ }
67
+ logger->sbuf = msgpack_sbuffer_new();
68
+ logger->msgpacker = msgpack_packer_new(logger->sbuf, msgpack_sbuffer_write);
69
+ logger->data = 0;
70
+ }
71
+ return logger;
72
+ }
73
+
74
+
75
+ static void
76
+ gc_start_i(VALUE tpval, void *data)
77
+ {
78
+ struct gc_hooks *hooks = (struct gc_hooks *)data;
79
+ (*hooks->funcs[0])(hooks->args[0], 0);
80
+ }
81
+
82
+ static void
83
+ gc_end_mark_i(VALUE tpval, void *data)
84
+ {
85
+ struct gc_hooks *hooks = (struct gc_hooks *)data;
86
+ (*hooks->funcs[1])(hooks->args[1], 1);
87
+ }
88
+
89
+ static void
90
+ gc_end_sweep_i(VALUE tpval, void *data)
91
+ {
92
+ struct gc_hooks *hooks = (struct gc_hooks *)data;
93
+ (*hooks->funcs[2])(hooks->args[2], 2);
94
+ }
95
+
96
+ static void
97
+ create_gc_hooks(void)
98
+ {
99
+ int i;
100
+ logger->hooks[0] =
101
+ rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_GC_START, gc_start_i, logger);
102
+ logger->hooks[1] =
103
+ rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_GC_END_MARK, gc_end_mark_i, logger);
104
+ logger->hooks[2] =
105
+ rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_GC_END_SWEEP, gc_end_sweep_i, logger);
106
+ /* mark for GC */
107
+ for (i=0; i<3; i++) rb_gc_register_mark_object(logger->hooks[i]);
108
+ }
109
+
110
+ void pack_pointer(msgpack_packer *packer, VALUE object_id) {
111
+ char *object_string;
112
+ asprintf(&object_string, "%p", object_id);
113
+ pack_string(packer, object_string);
114
+ free(object_string);
115
+ }
116
+ /*
117
+ * make_unique_str helps to reuse memory by allocating memory for a string
118
+ * only once and keeping track of how many times that string is referenced.
119
+ * It does so by creating a map of strings to their no of references.
120
+ * A new map is created for a string on its first use, and for further usages
121
+ * the reference count is incremented.
122
+ */
123
+ static const char * make_unique_str(st_table *tbl, const char *str, long len) {
124
+ if (!str) {
125
+ return NULL;
126
+ }
127
+ else {
128
+ st_data_t n;
129
+ char *result;
130
+
131
+ if (st_lookup(tbl, (st_data_t)str, &n)) {
132
+ st_insert(tbl, (st_data_t)str, n+1);
133
+ st_get_key(tbl, (st_data_t)str, (st_data_t *)&result);
134
+ }
135
+ else {
136
+ result = (char *)ruby_xmalloc(len+1);
137
+ strncpy(result, str, len);
138
+ result[len] = 0;
139
+ st_add_direct(tbl, (st_data_t)result, 1);
140
+ }
141
+ return result;
142
+ }
143
+ }
144
+
145
+ /*
146
+ * Used to free allocation of string when it's not referenced anymore.
147
+ * Decrements the reference count of a string if it's still used, else
148
+ * the map is removed completely.
149
+ */
150
+ static void delete_unique_str(st_table *tbl, const char *str) {
151
+ if (str) {
152
+ st_data_t n;
153
+
154
+ st_lookup(tbl, (st_data_t)str, &n);
155
+ if (n == 1) {
156
+ st_delete(tbl, (st_data_t *)&str, 0);
157
+ ruby_xfree((char *)str);
158
+ }
159
+ else {
160
+ st_insert(tbl, (st_data_t)str, n-1);
161
+ }
162
+ }
163
+ }
164
+
165
+ // Refer Ruby source ext/objspace/object_tracing.c::newobj_i
166
+ static void newobj_i(VALUE tpval, void *data) {
167
+ struct gc_hooks * arg = (struct gc_hooks *)data;
168
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
169
+ VALUE obj = rb_tracearg_object(tparg);
170
+ VALUE klass = RBASIC_CLASS(obj);
171
+ VALUE path = rb_tracearg_path(tparg);
172
+ VALUE line = rb_tracearg_lineno(tparg);
173
+ VALUE method_id = rb_tracearg_method_id(tparg);
174
+ VALUE defined_klass = rb_tracearg_defined_class(tparg);
175
+
176
+ struct allocation_info *info;
177
+ const char *path_cstr = RTEST(path) ? make_unique_str(arg->str_table, RSTRING_PTR(path), RSTRING_LEN(path)) : 0;
178
+ VALUE class_path = (RTEST(defined_klass) && !OBJ_FROZEN(defined_klass)) ? rb_class_path_cached(defined_klass) : Qnil;
179
+ const char *class_path_cstr = RTEST(class_path) ? make_unique_str(arg->str_table, RSTRING_PTR(class_path), RSTRING_LEN(class_path)) : 0;
180
+
181
+ if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
182
+ /* reuse info */
183
+ delete_unique_str(arg->str_table, info->path);
184
+ delete_unique_str(arg->str_table, info->class_path);
185
+ }
186
+ else {
187
+ info = (struct allocation_info *)ruby_xmalloc(sizeof(struct allocation_info));
188
+ }
189
+
190
+ info->path = path_cstr;
191
+ info->line = NUM2INT(line);
192
+ info->method_id = method_id;
193
+ info->class_path = class_path_cstr;
194
+ info->generation = rb_gc_count();
195
+ st_insert(arg->object_table, (st_data_t)obj, (st_data_t)info);
196
+
197
+ msgpack_sbuffer_clear(arg->sbuf);
198
+ pack_event_header(arg->msgpacker, event_names[3], 3);
199
+ pack_string(arg->msgpacker, "payload");
200
+ msgpack_pack_map(arg->msgpacker, 2);
201
+ pack_string(arg->msgpacker, "object_id");
202
+ pack_pointer(arg->msgpacker, obj);
203
+ pack_string(arg->msgpacker, "class");
204
+ if (!NIL_P(klass) && BUILTIN_TYPE(obj) != T_NONE && BUILTIN_TYPE(obj) != T_ZOMBIE && BUILTIN_TYPE(obj) != T_ICLASS) {
205
+ pack_string(arg->msgpacker, rb_class2name(klass));
206
+
207
+ } else {
208
+ msgpack_pack_nil(arg->msgpacker);
209
+ }
210
+ add_message(arg->sbuf);
211
+ }
212
+
213
+ // Refer Ruby source ext/objspace/object_tracing.c::freeobj_i
214
+ static void freeobj_i(VALUE tpval, void *data) {
215
+ rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
216
+ VALUE obj = rb_tracearg_object(tparg);
217
+
218
+ struct gc_hooks *arg = (struct gc_hooks *)data;
219
+ struct allocation_info *info;
220
+
221
+ if (st_lookup(arg->object_table, (st_data_t)obj, (st_data_t *)&info)) {
222
+ st_delete(arg->object_table, (st_data_t *)&obj, (st_data_t *)&info);
223
+ delete_unique_str(arg->str_table, info->path);
224
+ delete_unique_str(arg->str_table, info->class_path);
225
+ ruby_xfree(info);
226
+ }
227
+
228
+ msgpack_sbuffer_clear(logger->sbuf);
229
+ pack_event_header(logger->msgpacker, event_names[4], 3);
230
+ pack_string(logger->msgpacker, "payload");
231
+ msgpack_pack_map(logger->msgpacker, 1);
232
+ pack_string(logger->msgpacker, "object_id");
233
+ pack_pointer(logger->msgpacker, obj);
234
+ add_message(logger->sbuf);
235
+ }
236
+
237
+ static VALUE start_stat_server(int argc, VALUE *argv, VALUE self) {
238
+ int default_pub_port = 5555;
239
+ int default_request_port = 5556;
240
+ VALUE pub_port;
241
+ VALUE request_port;
242
+ int bind_result;
243
+
244
+ rb_scan_args(argc, argv, "02", &pub_port, &request_port);
245
+ if (!NIL_P(pub_port)) {
246
+ default_pub_port = FIX2INT(pub_port);
247
+ if (default_pub_port < 1024 || default_pub_port > 65000)
248
+ rb_raise(rb_eArgError, "invalid port value");
249
+ }
250
+
251
+ if (!NIL_P(request_port)) {
252
+ default_request_port = FIX2INT(request_port);
253
+ if(default_request_port < 1024 || default_request_port > 65000)
254
+ rb_raise(rb_eArgError, "invalid port value");
255
+ }
256
+
257
+ // Creates a list which aggregates messages
258
+ message_list_new();
259
+ logger = get_trace_logger();
260
+ logger->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, logger);
261
+ logger->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, logger);
262
+ rb_gc_register_mark_object(logger->newobj_trace);
263
+ rb_gc_register_mark_object(logger->freeobj_trace);
264
+ create_gc_hooks();
265
+
266
+ char zmq_endpoint[14];
267
+ sprintf(zmq_endpoint, "tcp://*:%d", default_pub_port);
268
+
269
+ zmq_context = zmq_ctx_new();
270
+ zmq_publisher = zmq_socket(zmq_context, ZMQ_PUB);
271
+ bind_result = zmq_bind(zmq_publisher, zmq_endpoint);
272
+ assert(bind_result == 0);
273
+
274
+ char zmq_request_endpoint[14];
275
+ sprintf(zmq_request_endpoint, "tcp://*:%d", default_request_port);
276
+
277
+ zmq_response_socket = zmq_socket(zmq_context, ZMQ_REP);
278
+ bind_result = zmq_bind(zmq_response_socket, zmq_request_endpoint);
279
+ assert(bind_result == 0);
280
+
281
+ items[0].socket = zmq_response_socket;
282
+ items[0].events = ZMQ_POLLIN;
283
+ return Qnil;
284
+ }
285
+
286
+ char * tracer_string_recv(void *socket) {
287
+ zmq_msg_t msg;
288
+ int rc = zmq_msg_init(&msg);
289
+ assert(rc == 0);
290
+
291
+ rc = zmq_msg_recv(&msg, socket, 0);
292
+ assert(rc != -1);
293
+ size_t message_size = zmq_msg_size(&msg);
294
+ char *message = (char *)malloc(message_size +1);
295
+ memcpy(message, zmq_msg_data(&msg), message_size);
296
+ message[message_size] = 0;
297
+ zmq_msg_close(&msg);
298
+ return message;
299
+ }
300
+
301
+
302
+ int tracer_string_send(void *socket, const char *message) {
303
+ int size = zmq_send (socket, message, strlen (message), 0);
304
+ return size;
305
+ }
306
+
307
+ static VALUE poll_for_request() {
308
+ // Wait for 100 millisecond and check if there is a message
309
+ // we can't wait here indefenitely because ruby is not aware this is a
310
+ // blocking operation. Remember ruby releases GVL in a thread
311
+ // whenever it encounters a known blocking operation.
312
+ zmq_poll(items, 1, 100);
313
+ if (items[0].revents && ZMQ_POLLIN) {
314
+ char *message = tracer_string_recv(zmq_response_socket);
315
+ tracer_string_send(zmq_response_socket, "ok");
316
+ VALUE command_ruby_string = rb_str_new_cstr(message);
317
+ free(message);
318
+ return command_ruby_string;
319
+ } else {
320
+ return Qnil;
321
+ }
322
+ }
323
+
324
+ static VALUE stop_stat_tracing() {
325
+ if (logger->hooks[0] != 0) {
326
+ rb_tracepoint_disable(logger->hooks[0]);
327
+ rb_tracepoint_disable(logger->hooks[1]);
328
+ rb_tracepoint_disable(logger->hooks[2]);
329
+ }
330
+
331
+ if (logger->newobj_trace) {
332
+ rb_tracepoint_disable(logger->newobj_trace);
333
+ rb_tracepoint_disable(logger->freeobj_trace);
334
+ }
335
+ logger->enabled = Qfalse;
336
+ return Qnil;
337
+ }
338
+
339
+
340
+ static int free_keys_i(st_data_t key, st_data_t value, void *data) {
341
+ ruby_xfree((void *)key);
342
+ return ST_CONTINUE;
343
+ }
344
+
345
+ static int free_values_i(st_data_t key, st_data_t value, void *data) {
346
+ ruby_xfree((void *)value);
347
+ return ST_CONTINUE;
348
+ }
349
+
350
+ static VALUE stop_stat_server() {
351
+ if (logger->enabled == Qtrue)
352
+ stop_stat_tracing();
353
+
354
+ // Destroy the list which aggregates messages
355
+ message_list_destroy();
356
+ // Clear object_table which holds object allocation info
357
+ st_foreach(logger->object_table, free_values_i, 0);
358
+ st_clear(logger->object_table);
359
+ st_foreach(logger->str_table, free_keys_i, 0);
360
+ st_clear(logger->str_table);
361
+
362
+ msgpack_sbuffer_free(logger->sbuf);
363
+ msgpack_packer_free(logger->msgpacker);
364
+ zmq_close(zmq_publisher);
365
+ zmq_close(zmq_response_socket);
366
+ zmq_ctx_destroy(zmq_context);
367
+ free(logger);
368
+ return Qnil;
369
+ }
370
+
371
+ void pack_value_object(msgpack_packer *packer, VALUE value) {
372
+ switch (TYPE(value)) {
373
+ case T_FIXNUM:
374
+ msgpack_pack_long(packer, FIX2LONG(value));
375
+ break;
376
+ case T_FLOAT:
377
+ msgpack_pack_double(packer, rb_num2dbl(value));
378
+ break;
379
+ default:
380
+ ;
381
+ VALUE rubyString = rb_funcall(value, rb_intern("to_s"), 0, 0);
382
+ char *keyString = StringValueCStr(rubyString);
383
+ pack_string(packer, keyString);
384
+ break;
385
+ }
386
+ }
387
+
388
+ static int hash_iterator(VALUE key, VALUE value, VALUE hash_arg) {
389
+ msgpack_packer *packer = (msgpack_packer *)hash_arg;
390
+
391
+ // pack the key
392
+ pack_value_object(packer,key);
393
+ // pack the value
394
+ pack_value_object(packer, value);
395
+ return ST_CONTINUE;
396
+ }
397
+
398
+
399
+ void pack_string(msgpack_packer *packer, char *string) {
400
+ if(string == NULL) {
401
+ msgpack_pack_nil(packer);
402
+ } else {
403
+ int length = strlen(string);
404
+ msgpack_pack_raw(packer, length);
405
+ msgpack_pack_raw_body(packer, string, length);
406
+ }
407
+ }
408
+
409
+ void pack_timestamp(msgpack_packer *packer) {
410
+ struct timeval tv;
411
+ gettimeofday(&tv, NULL);
412
+
413
+ double time_in_milliseconds = (tv.tv_sec)*1000 + (tv.tv_usec)/1000;
414
+ msgpack_pack_double(packer, time_in_milliseconds);
415
+ }
416
+
417
+ static VALUE send_hash_as_event(int argc, VALUE *argv, VALUE self) {
418
+ VALUE hash_object;
419
+ VALUE event_name;
420
+
421
+ rb_scan_args(argc, argv, "20", &hash_object, &event_name);
422
+
423
+ int size = RHASH_SIZE(hash_object);
424
+ msgpack_sbuffer *buffer = msgpack_sbuffer_new();
425
+ msgpack_packer *packer = msgpack_packer_new(buffer, msgpack_sbuffer_write);
426
+ pack_event_header(packer, StringValueCStr(event_name), 3);
427
+
428
+ pack_string(packer, "payload");
429
+ msgpack_pack_map(packer, size);
430
+
431
+ rb_hash_foreach(hash_object, hash_iterator, (VALUE)packer);
432
+ add_message(buffer);
433
+ msgpack_sbuffer_free(buffer);
434
+ msgpack_packer_free(packer);
435
+ return Qnil;
436
+ }
437
+
438
+ static VALUE start_stat_tracing() {
439
+ if (logger->enabled == Qtrue)
440
+ return Qnil;
441
+ rb_tracepoint_enable(logger->newobj_trace);
442
+ rb_tracepoint_enable(logger->freeobj_trace);
443
+ int i = 0;
444
+ for (i=0; i<3; i++) {
445
+ rb_tracepoint_enable(logger->hooks[i]);
446
+ }
447
+ logger->enabled = Qtrue;
448
+ return Qnil;
449
+ }
450
+
451
+ static VALUE send_objectspace_dump() {
452
+ msgpack_sbuffer* buffer = msgpack_sbuffer_new();
453
+ msgpack_packer* pk = msgpack_packer_new(buffer, msgpack_sbuffer_write);
454
+
455
+ rbkit_object_dump * dump = get_object_dump(logger->object_table);
456
+ pack_event_header(pk, "object_space_dump", 3);
457
+ pack_string(pk, "payload");
458
+ // Set size of array to hold all objects
459
+ msgpack_pack_array(pk, dump->object_count);
460
+
461
+ // Iterate through all object data
462
+ rbkit_object_dump_page * page = dump->first ;
463
+ while(page != NULL) {
464
+ rbkit_object_data *data;
465
+ size_t i = 0;
466
+ for(;i < page->count; i++) {
467
+ data = &(page->data[i]);
468
+ /* Object dump is a map that looks like this :
469
+ * {
470
+ * object_id: <OBJECT_ID_IN_HEX>,
471
+ * class: <CLASS_NAME>,
472
+ * references: [<OBJECT_ID_IN_HEX>, <OBJECT_ID_IN_HEX>, ...],
473
+ * file: <FILE_PATH>,
474
+ * line: <LINE_NO>,
475
+ * size: <SIZE>
476
+ * }
477
+ */
478
+
479
+ msgpack_pack_map(pk, 6);
480
+
481
+ // Key1 : "object_id"
482
+ pack_string(pk, "object_id");
483
+
484
+ // Value1 : pointer address of object
485
+ char * object_id;
486
+ asprintf(&object_id, "%p", data->object_id);
487
+ pack_string(pk, object_id);
488
+ free(object_id);
489
+
490
+ // Key2 : "class_name"
491
+ pack_string(pk, "class_name");
492
+
493
+ // Value2 : Class name of object
494
+ if(data->class_name == NULL) {
495
+ msgpack_pack_nil(pk);
496
+ } else {
497
+ pack_string(pk, data->class_name);
498
+ }
499
+
500
+ // Key3 : "references"
501
+ pack_string(pk, "references");
502
+
503
+ // Value3 : References held by the object
504
+ msgpack_pack_array(pk, data->reference_count);
505
+ if(data->reference_count != 0) {
506
+ size_t count = 0;
507
+ for(; count < data->reference_count; count++ ) {
508
+ char * object_id;
509
+ asprintf(&object_id, "%p", data->references[count]);
510
+ pack_string(pk, object_id);
511
+ free(object_id);
512
+ }
513
+ free(data->references);
514
+ }
515
+
516
+ // Key4 : "file"
517
+ pack_string(pk, "file");
518
+
519
+ // Value4 : File path where object is defined
520
+ pack_string(pk, data->file);
521
+
522
+ // Key5 : "line"
523
+ pack_string(pk, "line");
524
+
525
+ // Value5 : Line no where object is defined
526
+ if(data->line == 0)
527
+ msgpack_pack_nil(pk);
528
+ else
529
+ msgpack_pack_unsigned_long(pk, data->line);
530
+
531
+ // Key6 : "size"
532
+ pack_string(pk, "size");
533
+
534
+ // Value6 : Size of the object in memory
535
+ if(data->size == 0)
536
+ msgpack_pack_nil(pk);
537
+ else
538
+ msgpack_pack_uint32(pk, data->size);
539
+ }
540
+ rbkit_object_dump_page * prev = page;
541
+ page = page->next;
542
+ free(prev);
543
+ }
544
+
545
+ // Send packed message over zmq
546
+ add_message(buffer);
547
+
548
+ //Cleanup
549
+ free(dump);
550
+ msgpack_sbuffer_free(buffer);
551
+ msgpack_packer_free(pk);
552
+
553
+ return Qnil;
554
+ }
555
+
556
+ /*
557
+ * Creates a msgpack array which contains all the messages packed after
558
+ * the last time send_messages() was called, and sends it over the PUB socket.
559
+ */
560
+ static VALUE send_messages() {
561
+ //Get all aggregated messages as payload of a single event.
562
+ msgpack_sbuffer * sbuf = msgpack_sbuffer_new();
563
+ get_event_collection_message(sbuf);
564
+ //Send the msgpack array over zmq PUB socket
565
+ if(sbuf && sbuf->size > 0)
566
+ zmq_send(zmq_publisher, sbuf->data, sbuf->size, 0);
567
+ // Clear the aggregated messages
568
+ message_list_clear();
569
+ msgpack_sbuffer_free(sbuf);
570
+ return Qnil;
571
+ }
572
+
573
+ void Init_rbkit_tracer(void) {
574
+ VALUE objectStatsModule = rb_define_module("Rbkit");
575
+ rb_define_module_function(objectStatsModule, "start_stat_server", start_stat_server, -1);
576
+ rb_define_module_function(objectStatsModule, "stop_stat_server", stop_stat_server, 0);
577
+ rb_define_module_function(objectStatsModule, "start_stat_tracing", start_stat_tracing, 0);
578
+ rb_define_module_function(objectStatsModule, "stop_stat_tracing", stop_stat_tracing, 0);
579
+ rb_define_module_function(objectStatsModule, "poll_for_request", poll_for_request, 0);
580
+ rb_define_module_function(objectStatsModule, "send_objectspace_dump", send_objectspace_dump, 0);
581
+ rb_define_module_function(objectStatsModule, "send_hash_as_event", send_hash_as_event, -1);
582
+ rb_define_module_function(objectStatsModule, "send_messages", send_messages, 0);
583
+ }