rbkit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }