rbkit 0.0.1 → 0.1.6

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +17 -0
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +6 -2
  7. data/LICENSE.txt +22 -0
  8. data/README.md +74 -11
  9. data/Rakefile +27 -3
  10. data/docs/EVENT_FORMAT.md +195 -0
  11. data/experiments/object_dump.rb +1 -1
  12. data/experiments/rbkit_command_test.rb +3 -1
  13. data/experiments/using_rbkit.rb +1 -1
  14. data/ext/extconf.rb +95 -12
  15. data/ext/rbkit_allocation_info.c +91 -0
  16. data/ext/rbkit_allocation_info.h +17 -0
  17. data/ext/rbkit_event.c +71 -0
  18. data/ext/rbkit_event.h +63 -0
  19. data/ext/rbkit_event_packer.c +251 -0
  20. data/ext/rbkit_event_packer.h +23 -0
  21. data/ext/rbkit_message_aggregator.c +9 -16
  22. data/ext/rbkit_message_aggregator.h +0 -1
  23. data/ext/rbkit_object_graph.c +6 -49
  24. data/ext/rbkit_object_graph.h +12 -3
  25. data/ext/rbkit_test_helper.c +25 -0
  26. data/ext/rbkit_test_helper.h +1 -0
  27. data/ext/rbkit_tracer.c +106 -323
  28. data/ext/rbkit_tracer.h +2 -10
  29. data/lib/rbkit.rb +57 -35
  30. data/lib/rbkit/rbkit_gc.rb +79 -0
  31. data/lib/rbkit/version.rb +1 -1
  32. data/logo.png +0 -0
  33. data/rbkit.gemspec +1 -0
  34. data/setup.rb +37 -0
  35. data/spec/gc_stat_spec.rb +31 -0
  36. data/spec/hash_event_spec.rb +29 -0
  37. data/spec/obj_created_spec.rb +52 -0
  38. data/spec/obj_destroyed_spec.rb +44 -0
  39. data/spec/object_space_dump_spec.rb +77 -0
  40. data/spec/rbkit_helpful_messages_spec.rb +61 -0
  41. data/spec/spec_helper.rb +11 -6
  42. data/spec/start_server_spec.rb +29 -0
  43. data/spec/status_spec.rb +48 -0
  44. data/spec/support/foo_bar_sample_class.rb +24 -0
  45. data/spec/support/have_message_matcher.rb +26 -0
  46. metadata +40 -4
  47. data/schema/tracing_info.md +0 -8
@@ -0,0 +1,23 @@
1
+ #ifndef RBKIT_MESSAGE_PACKER
2
+ #define RBKIT_MESSAGE_PACKER
3
+ #include "msgpack.h"
4
+ #include "rbkit_event.h"
5
+
6
+ typedef enum _rbkit_message_fields {
7
+ rbkit_message_field_event_type,
8
+ rbkit_message_field_timestamp,
9
+ rbkit_message_field_payload,
10
+ rbkit_message_field_object_id,
11
+ rbkit_message_field_class_name,
12
+ rbkit_message_field_references,
13
+ rbkit_message_field_file,
14
+ rbkit_message_field_line,
15
+ rbkit_message_field_size,
16
+ rbkit_message_field_message_counter
17
+ } rbkit_message_fields;
18
+
19
+ VALUE rbkit_message_fields_as_hash();
20
+
21
+ void pack_event(rbkit_event_header *event_header, msgpack_packer *packer);
22
+
23
+ #endif
@@ -1,8 +1,6 @@
1
- #include <assert.h>
2
- #include "zmq.h"
3
1
  #include "rbkit_message_aggregator.h"
2
+ #include "rbkit_event_packer.h"
4
3
 
5
- static msgpack_sbuffer * sbuf;
6
4
  static void* message_array;
7
5
  static size_t used_memsize;
8
6
  static size_t total_capacity;
@@ -15,7 +13,6 @@ static int has_enough_space_for(size_t size) {
15
13
  static void double_the_capacity() {
16
14
  total_capacity *= 2;
17
15
  message_array = realloc(message_array, total_capacity);
18
- assert(message_array);
19
16
  }
20
17
 
21
18
  void message_list_new() {
@@ -51,16 +48,12 @@ void add_message(msgpack_sbuffer *buffer) {
51
48
  // Creates a message containing all the available
52
49
  // msgpack sbuffers in the array
53
50
  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
- }
51
+ if(no_of_messages == 0)
52
+ return;
53
+
54
+ rbkit_event_collection_event *event = new_rbkit_event_collection_event(message_array, used_memsize, no_of_messages);
55
+ msgpack_packer* pk = msgpack_packer_new(sbuf, msgpack_sbuffer_write);
56
+ pack_event((rbkit_event_header *)event, pk);
57
+ free(event);
58
+ msgpack_packer_free(pk);
66
59
  }
@@ -2,7 +2,6 @@
2
2
  #define RBKIT_MESSAGE_AGGREGATOR_H
3
3
 
4
4
  #include "msgpack.h"
5
- #include "zmq.h"
6
5
 
7
6
  void message_list_new();
8
7
  void add_message(msgpack_sbuffer *);
@@ -37,48 +37,19 @@ static void set_size(VALUE obj, rbkit_object_data * data) {
37
37
  data->size = size;
38
38
  }
39
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)
40
+ static void reachable_object_i(VALUE ref, void *arg)
71
41
  {
42
+ rbkit_object_data *data = (rbkit_object_data *)arg;
72
43
  if(RBASIC_CLASS(ref) == ref)
73
44
  return;
74
45
 
75
46
  data->reference_count ++;
76
47
  if (data->reference_count == 1) {
77
- data->references = malloc(sizeof(void *));
48
+ data->references = malloc(sizeof(unsigned long long));
78
49
  } else {
79
- data->references = realloc(data->references, data->reference_count * sizeof(void *) );
50
+ data->references = realloc(data->references, data->reference_count * sizeof(unsigned long long) );
80
51
  }
81
- data->references[data->reference_count - 1] = (void *)ref;
52
+ data->references[data->reference_count - 1] = FIX2ULONG(rb_obj_id(ref));
82
53
  }
83
54
 
84
55
  static void dump_heap_object(VALUE obj, rbkit_object_dump *dump) {
@@ -86,7 +57,7 @@ static void dump_heap_object(VALUE obj, rbkit_object_dump *dump) {
86
57
  // Get next available slot from page
87
58
  rbkit_object_data *data = initialize_object_data(dump);
88
59
  //Set object id
89
- data->object_id = (void *)obj;
60
+ data->object_id = FIX2ULONG(rb_obj_id(obj));
90
61
 
91
62
  //Set classname
92
63
  VALUE klass = RBASIC_CLASS(obj);
@@ -109,15 +80,6 @@ static void dump_heap_object(VALUE obj, rbkit_object_dump *dump) {
109
80
  dump->last->count++;
110
81
  }
111
82
 
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
83
  /*
122
84
  * Iterator that walks over heap pages
123
85
  */
@@ -135,10 +97,6 @@ static int heap_obj_i(void *vstart, void *vend, size_t stride, void *dump_data)
135
97
  return 0;
136
98
  }
137
99
 
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
100
  static void collect_heap_objects(rbkit_object_dump * dump) {
143
101
  rb_objspace_each_objects(heap_obj_i, (void *)dump);
144
102
  }
@@ -148,7 +106,6 @@ rbkit_object_dump * get_object_dump(st_table * object_table) {
148
106
  dump->object_table = object_table;
149
107
  dump->first = NULL;
150
108
  dump->last = NULL;
151
- collect_root_objects(dump);
152
109
  collect_heap_objects(dump);
153
110
  return dump;
154
111
  }
@@ -1,14 +1,15 @@
1
1
  #ifndef RBKIT_OBJECT_GRAPH
2
2
  #define RBKIT_OBJECT_GRAPH
3
+ #include <ruby.h>
3
4
 
4
5
  #define RBKIT_OBJECT_DUMP_PAGE_SIZE 1000
5
6
 
6
7
  typedef struct _rbkit_object_data {
7
- const void * object_id;
8
+ unsigned long long object_id;
8
9
  const char * class_name;
9
- void ** references;
10
+ unsigned long long * references;
10
11
  size_t reference_count;
11
- char * file;
12
+ const char * file;
12
13
  unsigned long line;
13
14
  size_t size;
14
15
  } rbkit_object_data;
@@ -36,4 +37,12 @@ struct allocation_info {
36
37
  VALUE method_id;
37
38
  size_t generation;
38
39
  };
40
+
41
+ // Declarations of ruby internals to silence warnings
42
+ void rb_objspace_reachable_objects_from(VALUE obj, void (func)(VALUE, void *), void *data);
43
+ void rb_objspace_reachable_objects_from_root(void (func)(const char *category, VALUE, void *), void *data);
44
+ size_t rb_obj_memsize_of(VALUE);
45
+ void rb_objspace_each_objects(
46
+ int (*callback)(void *start, void *end, size_t stride, void *data),
47
+ void *data);
39
48
  #endif
@@ -0,0 +1,25 @@
1
+ #include <ruby.h>
2
+ #include "rbkit_message_aggregator.h"
3
+
4
+ static VALUE noop_send_messages() {
5
+ //NOOP
6
+ return Qnil;
7
+ }
8
+
9
+ static VALUE get_queued_messages() {
10
+ msgpack_sbuffer * sbuf = msgpack_sbuffer_new();
11
+ get_event_collection_message(sbuf);
12
+ if(sbuf && sbuf->size > 0) {
13
+ VALUE str = rb_str_new(sbuf->data, sbuf->size);
14
+ message_list_clear();
15
+ msgpack_sbuffer_destroy(sbuf);
16
+ return str;
17
+ }
18
+ return Qnil;
19
+ }
20
+
21
+ void Init_rbkit_test_helper(void) {
22
+ VALUE rbkit_module = rb_define_module("Rbkit");
23
+ rb_define_module_function(rbkit_module, "send_messages", noop_send_messages, 0);
24
+ rb_define_module_function(rbkit_module, "get_queued_messages", get_queued_messages, 0);
25
+ }
@@ -0,0 +1 @@
1
+ void Init_rbkit_test_helper();
@@ -7,8 +7,12 @@
7
7
  //
8
8
 
9
9
  #include "rbkit_tracer.h"
10
+ #include "rbkit_allocation_info.h"
11
+ #include "rbkit_event.h"
12
+ #include "rbkit_event_packer.h"
10
13
  #include "rbkit_object_graph.h"
11
14
  #include "rbkit_message_aggregator.h"
15
+ #include "rbkit_test_helper.h"
12
16
  #include <sys/time.h>
13
17
 
14
18
  static const char *event_names[] = {
@@ -19,51 +23,22 @@ static const char *event_names[] = {
19
23
  "obj_destroyed"
20
24
  };
21
25
 
22
- static struct gc_hooks *logger;
23
- static int tmp_keep_remains;
26
+ static rbkit_logger *logger;
24
27
  static void *zmq_publisher;
25
28
  static void *zmq_context;
26
29
  static void *zmq_response_socket;
27
30
  static zmq_pollitem_t items[1];
28
31
 
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() {
32
+ static rbkit_logger * get_trace_logger() {
53
33
  int i = 0;
54
34
  if (logger == 0) {
55
- logger = ALLOC_N(struct gc_hooks, 1);
35
+ logger = ALLOC_N(rbkit_logger, 1);
56
36
  logger->enabled = Qfalse;
57
37
  logger->newobj_trace = 0;
58
38
  logger->freeobj_trace = 0;
59
- logger->keep_remains = tmp_keep_remains;
60
39
  logger->object_table = st_init_numtable();
61
40
  logger->str_table = st_init_strtable();
62
41
 
63
- for (i = 0; i < 3; i++) {
64
- logger->funcs[i] = trace_gc_invocation;
65
- logger->args[i] = (void *)event_names[i];
66
- }
67
42
  logger->sbuf = msgpack_sbuffer_new();
68
43
  logger->msgpacker = msgpack_packer_new(logger->sbuf, msgpack_sbuffer_write);
69
44
  logger->data = 0;
@@ -71,26 +46,37 @@ static struct gc_hooks * get_trace_logger() {
71
46
  return logger;
72
47
  }
73
48
 
74
-
75
49
  static void
76
50
  gc_start_i(VALUE tpval, void *data)
77
51
  {
78
- struct gc_hooks *hooks = (struct gc_hooks *)data;
79
- (*hooks->funcs[0])(hooks->args[0], 0);
52
+ rbkit_logger * arg = (rbkit_logger *)data;
53
+ rbkit_event_header *event = malloc(sizeof(rbkit_event_header));
54
+ event->event_type = gc_start;
55
+ pack_event(event, arg->msgpacker);
56
+ free(event);
57
+ add_message(arg->sbuf);
80
58
  }
81
59
 
82
60
  static void
83
61
  gc_end_mark_i(VALUE tpval, void *data)
84
62
  {
85
- struct gc_hooks *hooks = (struct gc_hooks *)data;
86
- (*hooks->funcs[1])(hooks->args[1], 1);
63
+ rbkit_logger * arg = (rbkit_logger *)data;
64
+ rbkit_event_header *event = malloc(sizeof(rbkit_event_header));
65
+ event->event_type = gc_end_m;
66
+ pack_event(event, arg->msgpacker);
67
+ free(event);
68
+ add_message(arg->sbuf);
87
69
  }
88
70
 
89
71
  static void
90
72
  gc_end_sweep_i(VALUE tpval, void *data)
91
73
  {
92
- struct gc_hooks *hooks = (struct gc_hooks *)data;
93
- (*hooks->funcs[2])(hooks->args[2], 2);
74
+ rbkit_logger * arg = (rbkit_logger *)data;
75
+ rbkit_event_header *event = malloc(sizeof(rbkit_event_header));
76
+ event->event_type = gc_end_s;
77
+ pack_event(event, arg->msgpacker);
78
+ free(event);
79
+ add_message(arg->sbuf);
94
80
  }
95
81
 
96
82
  static void
@@ -107,106 +93,21 @@ create_gc_hooks(void)
107
93
  for (i=0; i<3; i++) rb_gc_register_mark_object(logger->hooks[i]);
108
94
  }
109
95
 
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
96
  // Refer Ruby source ext/objspace/object_tracing.c::newobj_i
166
97
  static void newobj_i(VALUE tpval, void *data) {
167
- struct gc_hooks * arg = (struct gc_hooks *)data;
98
+ rbkit_logger * arg = (rbkit_logger *)data;
168
99
  rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
100
+ rbkit_allocation_info *info = new_rbkit_allocation_info(tparg, arg->str_table, arg->object_table);
101
+
169
102
  VALUE obj = rb_tracearg_object(tparg);
170
103
  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
- }
104
+ const char *class_name = NULL;
105
+ if (!NIL_P(klass) && BUILTIN_TYPE(obj) != T_NONE && BUILTIN_TYPE(obj) != T_ZOMBIE && BUILTIN_TYPE(obj) != T_ICLASS)
106
+ class_name = rb_class2name(klass);
189
107
 
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
- }
108
+ rbkit_obj_created_event *event = new_rbkit_obj_created_event(FIX2ULONG(rb_obj_id(obj)), class_name, info);
109
+ pack_event((rbkit_event_header *)event, arg->msgpacker);
110
+ free(event);
210
111
  add_message(arg->sbuf);
211
112
  }
212
113
 
@@ -214,45 +115,38 @@ static void newobj_i(VALUE tpval, void *data) {
214
115
  static void freeobj_i(VALUE tpval, void *data) {
215
116
  rb_trace_arg_t *tparg = rb_tracearg_from_tracepoint(tpval);
216
117
  VALUE obj = rb_tracearg_object(tparg);
118
+ rbkit_logger *arg = (rbkit_logger *)data;
217
119
 
218
- struct gc_hooks *arg = (struct gc_hooks *)data;
219
- struct allocation_info *info;
120
+ // Delete allocation info of freed object
121
+ delete_rbkit_allocation_info(tparg, obj, arg->str_table, arg->object_table);
220
122
 
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);
123
+ rbkit_obj_destroyed_event *event = new_rbkit_obj_destroyed_event(FIX2ULONG(rb_obj_id(obj)));
124
+ pack_event((rbkit_event_header *)event, arg->msgpacker);
125
+ free(event);
126
+ add_message(arg->sbuf);
235
127
  }
236
128
 
237
129
  static VALUE start_stat_server(int argc, VALUE *argv, VALUE self) {
238
- int default_pub_port = 5555;
239
- int default_request_port = 5556;
240
130
  VALUE pub_port;
241
131
  VALUE request_port;
242
132
  int bind_result;
243
133
 
244
134
  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
135
 
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
- }
136
+ char zmq_endpoint[14];
137
+ sprintf(zmq_endpoint, "tcp://*:%d", FIX2INT(pub_port));
138
+ zmq_context = zmq_ctx_new();
139
+ zmq_publisher = zmq_socket(zmq_context, ZMQ_PUB);
140
+ bind_result = zmq_bind(zmq_publisher, zmq_endpoint);
141
+ if(bind_result != 0)
142
+ return Qfalse;
143
+
144
+ char zmq_request_endpoint[14];
145
+ sprintf(zmq_request_endpoint, "tcp://*:%d", FIX2INT(request_port));
146
+ zmq_response_socket = zmq_socket(zmq_context, ZMQ_REP);
147
+ bind_result = zmq_bind(zmq_response_socket, zmq_request_endpoint);
148
+ if(bind_result != 0)
149
+ return Qfalse;
256
150
 
257
151
  // Creates a list which aggregates messages
258
152
  message_list_new();
@@ -263,24 +157,9 @@ static VALUE start_stat_server(int argc, VALUE *argv, VALUE self) {
263
157
  rb_gc_register_mark_object(logger->freeobj_trace);
264
158
  create_gc_hooks();
265
159
 
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
160
  items[0].socket = zmq_response_socket;
282
161
  items[0].events = ZMQ_POLLIN;
283
- return Qnil;
162
+ return Qtrue;
284
163
  }
285
164
 
286
165
  char * tracer_string_recv(void *socket) {
@@ -304,6 +183,33 @@ int tracer_string_send(void *socket, const char *message) {
304
183
  return size;
305
184
  }
306
185
 
186
+ static VALUE rbkit_status_as_hash() {
187
+ VALUE status = rb_hash_new();
188
+ VALUE pid = rb_funcall(rb_path2class("Process"), rb_intern("pid"), 0, 0);
189
+ VALUE processName = rb_funcall(rb_path2class("Process"), rb_intern("argv0"), 0, 0);
190
+ int object_trace_enabled = (logger && logger->enabled) ? 1 : 0;
191
+ rb_hash_aset(status, ID2SYM(rb_intern("process_name")), processName);
192
+ rb_hash_aset(status, ID2SYM(rb_intern("pwd")), rb_dir_getwd());
193
+ rb_hash_aset(status, ID2SYM(rb_intern("pid")), pid);
194
+ rb_hash_aset(status, ID2SYM(rb_intern("object_trace_enabled")), INT2FIX(object_trace_enabled));
195
+ return status;
196
+ }
197
+
198
+ static void send_handshake_response() {
199
+ msgpack_sbuffer *buffer = msgpack_sbuffer_new();
200
+ msgpack_packer *packer = msgpack_packer_new(buffer, msgpack_sbuffer_write);
201
+
202
+ rbkit_hash_event *event = new_rbkit_hash_event(handshake, rbkit_status_as_hash());
203
+ pack_event((rbkit_event_header *)event, packer);
204
+ free(event);
205
+
206
+ if(buffer && buffer->size > 0)
207
+ zmq_send(zmq_response_socket, buffer->data, buffer->size, 0);
208
+
209
+ msgpack_sbuffer_free(buffer);
210
+ msgpack_packer_free(packer);
211
+ }
212
+
307
213
  static VALUE poll_for_request() {
308
214
  // Wait for 100 millisecond and check if there is a message
309
215
  // we can't wait here indefenitely because ruby is not aware this is a
@@ -312,7 +218,11 @@ static VALUE poll_for_request() {
312
218
  zmq_poll(items, 1, 100);
313
219
  if (items[0].revents && ZMQ_POLLIN) {
314
220
  char *message = tracer_string_recv(zmq_response_socket);
315
- tracer_string_send(zmq_response_socket, "ok");
221
+ if(strcmp(message, "handshake") == 0) {
222
+ send_handshake_response();
223
+ } else {
224
+ tracer_string_send(zmq_response_socket, "ok");
225
+ }
316
226
  VALUE command_ruby_string = rb_str_new_cstr(message);
317
227
  free(message);
318
228
  return command_ruby_string;
@@ -365,70 +275,23 @@ static VALUE stop_stat_server() {
365
275
  zmq_close(zmq_response_socket);
366
276
  zmq_ctx_destroy(zmq_context);
367
277
  free(logger);
278
+ logger = 0;
368
279
  return Qnil;
369
280
  }
370
281
 
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
282
  static VALUE send_hash_as_event(int argc, VALUE *argv, VALUE self) {
418
283
  VALUE hash_object;
419
- VALUE event_name;
284
+ VALUE event_type;
420
285
 
421
- rb_scan_args(argc, argv, "20", &hash_object, &event_name);
286
+ rb_scan_args(argc, argv, "20", &hash_object, &event_type);
422
287
 
423
- int size = RHASH_SIZE(hash_object);
424
288
  msgpack_sbuffer *buffer = msgpack_sbuffer_new();
425
289
  msgpack_packer *packer = msgpack_packer_new(buffer, msgpack_sbuffer_write);
426
- pack_event_header(packer, StringValueCStr(event_name), 3);
427
290
 
428
- pack_string(packer, "payload");
429
- msgpack_pack_map(packer, size);
291
+ rbkit_hash_event *event = new_rbkit_hash_event(FIX2INT(event_type), hash_object);
292
+ pack_event((rbkit_event_header *)event, packer);
293
+ free(event);
430
294
 
431
- rb_hash_foreach(hash_object, hash_iterator, (VALUE)packer);
432
295
  add_message(buffer);
433
296
  msgpack_sbuffer_free(buffer);
434
297
  msgpack_packer_free(packer);
@@ -453,103 +316,14 @@ static VALUE send_objectspace_dump() {
453
316
  msgpack_packer* pk = msgpack_packer_new(buffer, msgpack_sbuffer_write);
454
317
 
455
318
  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
319
+ rbkit_object_space_dump_event *event = new_rbkit_object_space_dump_event(dump);
320
+ pack_event((rbkit_event_header *)event, pk);
321
+ free(event);
546
322
  add_message(buffer);
547
323
 
548
- //Cleanup
549
324
  free(dump);
550
325
  msgpack_sbuffer_free(buffer);
551
326
  msgpack_packer_free(pk);
552
-
553
327
  return Qnil;
554
328
  }
555
329
 
@@ -570,6 +344,11 @@ static VALUE send_messages() {
570
344
  return Qnil;
571
345
  }
572
346
 
347
+ static VALUE enable_test_mode() {
348
+ Init_rbkit_test_helper();
349
+ return Qnil;
350
+ }
351
+
573
352
  void Init_rbkit_tracer(void) {
574
353
  VALUE objectStatsModule = rb_define_module("Rbkit");
575
354
  rb_define_module_function(objectStatsModule, "start_stat_server", start_stat_server, -1);
@@ -580,4 +359,8 @@ void Init_rbkit_tracer(void) {
580
359
  rb_define_module_function(objectStatsModule, "send_objectspace_dump", send_objectspace_dump, 0);
581
360
  rb_define_module_function(objectStatsModule, "send_hash_as_event", send_hash_as_event, -1);
582
361
  rb_define_module_function(objectStatsModule, "send_messages", send_messages, 0);
362
+ rb_define_module_function(objectStatsModule, "enable_test_mode", enable_test_mode, 0);
363
+ rb_define_module_function(objectStatsModule, "status", rbkit_status_as_hash, 0);
364
+ rb_define_const(objectStatsModule, "EVENT_TYPES", rbkit_event_types_as_hash());
365
+ rb_define_const(objectStatsModule, "MESSAGE_FIELDS", rbkit_message_fields_as_hash());
583
366
  }