ruby-dtrace 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ /* Ruby-Dtrace
2
+ * (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ */
4
+
5
+ #include "dtrace_api.h"
6
+
7
+ RUBY_EXTERN VALUE eDtraceException;
8
+ RUBY_EXTERN VALUE cDtraceProbe;
9
+
10
+ /* :nodoc: */
11
+ VALUE dtraceprobedata_init(VALUE self)
12
+ {
13
+ dtrace_probedata_t *data;
14
+
15
+ Data_Get_Struct(self, dtrace_probedata_t, data);
16
+ return self;
17
+ }
18
+
19
+ VALUE dtraceprobedata_probedesc(VALUE self)
20
+ {
21
+ dtrace_probedata_t *data;
22
+ dtrace_probedesc_t *pdp;
23
+ VALUE probe;
24
+
25
+ Data_Get_Struct(self, dtrace_probedata_t, data);
26
+
27
+ pdp = data->dtpda_pdesc;
28
+ probe = Data_Wrap_Struct(cDtraceProbe, 0, NULL, (dtrace_probedesc_t *)pdp);
29
+
30
+ return probe;
31
+ }
32
+
33
+ static VALUE _handle_stack_record(const caddr_t addr, const dtrace_recdesc_t *rec)
34
+ {
35
+ dtrace_actkind_t act;
36
+ uint64_t *pc;
37
+ pid_t pid = -1;
38
+ int size; /* size of raw bytes not including trailing zeros */
39
+ int i; /* index of last non-zero byte */
40
+ VALUE raw;
41
+
42
+ for (i = rec->dtrd_size - 1; (i >= 0) && !addr[i]; --i) {
43
+ }
44
+ size = (i + 1);
45
+
46
+ raw = rb_ary_new();
47
+ for (i = 0; i < size; i++)
48
+ rb_ary_push(raw, INT2FIX(addr[i]));
49
+
50
+ act = rec->dtrd_action;
51
+ switch (act) {
52
+ case DTRACEACT_STACK:
53
+ break;
54
+ case DTRACEACT_USTACK:
55
+ case DTRACEACT_JSTACK:
56
+ /* Get pid of user process */
57
+ pc = (uint64_t *)(uintptr_t)addr;
58
+ pid = (pid_t)*pc;
59
+ break;
60
+ default:
61
+ rb_raise(eDtraceException, "Expected stack action, got %d\n", act);
62
+ }
63
+
64
+ return raw;
65
+ }
66
+
67
+ static int
68
+ _is_stack_action(dtrace_actkind_t act)
69
+ {
70
+ int stack_action;
71
+ switch (act) {
72
+ case DTRACEACT_STACK:
73
+ case DTRACEACT_USTACK:
74
+ case DTRACEACT_JSTACK:
75
+ stack_action = 1;
76
+ break;
77
+ default:
78
+ stack_action = 0;
79
+ }
80
+ return (stack_action);
81
+ }
82
+
83
+ VALUE dtraceprobedata_each_record(VALUE self)
84
+ {
85
+ dtrace_probedata_t *data;
86
+ dtrace_eprobedesc_t *eprobe;
87
+ dtrace_recdesc_t *rec;
88
+ int i;
89
+ VALUE dtracerecord;
90
+ VALUE v;
91
+ caddr_t addr;
92
+
93
+ Data_Get_Struct(self, dtrace_probedata_t, data);
94
+ eprobe = data->dtpda_edesc;
95
+
96
+ for (i = 0; i < eprobe->dtepd_nrecs; i++) {
97
+ rec = &eprobe->dtepd_rec[i];
98
+ if (rec->dtrd_size > 0) {
99
+ addr = data->dtpda_data + rec->dtrd_offset;
100
+
101
+ if (_is_stack_action(rec->dtrd_action)) {
102
+ v = _handle_stack_record(addr, rec);
103
+ }
104
+ else {
105
+ switch (rec->dtrd_size) {
106
+ case 1:
107
+ v = INT2FIX((int)(*((uint8_t *)addr)));
108
+ break;
109
+ case 2:
110
+ v = INT2FIX((int)(*((uint16_t *)addr)));
111
+ break;
112
+ case 4:
113
+ v = INT2FIX(*((int32_t *)addr));
114
+ break;
115
+ case 8:
116
+ v = INT2FIX(*((int64_t *)addr));
117
+ break;
118
+ default:
119
+ v = handle_bytedata(addr, rec->dtrd_size);
120
+ break;
121
+ }
122
+ }
123
+
124
+ dtracerecord = rb_class_new_instance(0, NULL, rb_path2class("DtraceRecord"));
125
+ rb_iv_set(dtracerecord, "@value", v);
126
+ rb_yield(dtracerecord);
127
+ }
128
+ }
129
+ }
data/ext/dtrace_recdesc.c CHANGED
@@ -13,26 +13,3 @@ VALUE dtracerecdesc_init(VALUE self)
13
13
  return self;
14
14
  }
15
15
 
16
- /*
17
- * Return the data for this record.
18
- */
19
- VALUE dtracerecdesc_data(VALUE self)
20
- {
21
- VALUE dtraceaggdata;
22
- dtrace_recdesc_t *recdesc;
23
- dtrace_aggdata_t *aggdata;
24
-
25
- Data_Get_Struct(self, dtrace_recdesc_t, recdesc);
26
-
27
- dtraceaggdata = rb_iv_get(self, "@aggdata");
28
- Data_Get_Struct(dtraceaggdata, dtrace_aggdata_t, aggdata);
29
-
30
- if (recdesc->dtrd_size == 256) {
31
- char *c = aggdata->dtada_data + recdesc->dtrd_offset;
32
- return rb_str_new2(c);
33
- }
34
- else {
35
- uint64_t n = *((uint64_t *)(aggdata->dtada_data + recdesc->dtrd_offset));
36
- return INT2FIX(n);
37
- }
38
- }
data/ext/dtrace_util.c ADDED
@@ -0,0 +1,91 @@
1
+ /* Ruby-Dtrace
2
+ * (c) 2007 Chris Andrews <chris@nodnol.org>
3
+ */
4
+
5
+ #include "dtrace_api.h"
6
+
7
+ RUBY_EXTERN VALUE eDtraceException;
8
+
9
+ /*
10
+ * Most of this function lifted from libdtrace/common/dt_consume.c
11
+ * dt_print_bytes().
12
+ */
13
+ VALUE handle_bytedata(caddr_t addr, uint32_t nbytes)
14
+ {
15
+ /*
16
+ * If the byte stream is a series of printable characters, followed by
17
+ * a terminating byte, we print it out as a string. Otherwise, we
18
+ * assume that it's something else and just print the bytes.
19
+ */
20
+ int i, j;
21
+ char *c = addr;
22
+
23
+ VALUE robj;
24
+
25
+ if (nbytes == 0) {
26
+ return rb_str_new2("");
27
+ }
28
+
29
+ for (i = 0; i < nbytes; i++) {
30
+ /*
31
+ * We define a "printable character" to be one for which
32
+ * isprint(3C) returns non-zero, isspace(3C) returns non-zero,
33
+ * or a character which is either backspace or the bell.
34
+ * Backspace and the bell are regrettably special because
35
+ * they fail the first two tests -- and yet they are entirely
36
+ * printable. These are the only two control characters that
37
+ * have meaning for the terminal and for which isprint(3C) and
38
+ * isspace(3C) return 0.
39
+ */
40
+ if (isprint(c[i]) || isspace(c[i]) ||
41
+ c[i] == '\b' || c[i] == '\a')
42
+ continue;
43
+
44
+ if (c[i] == '\0' && i > 0) {
45
+ /*
46
+ * This looks like it might be a string. Before we
47
+ * assume that it is indeed a string, check the
48
+ * remainder of the byte range; if it contains
49
+ * additional non-nul characters, we'll assume that
50
+ * it's a binary stream that just happens to look like
51
+ * a string.
52
+ */
53
+ for (j = i + 1; j < nbytes; j++) {
54
+ if (c[j] != '\0')
55
+ break;
56
+ }
57
+
58
+ if (j != nbytes)
59
+ break;
60
+
61
+ /* It's a string */
62
+ return (rb_str_new2((char *)addr));
63
+ }
64
+
65
+ break;
66
+ }
67
+
68
+ if (i == nbytes) {
69
+ /*
70
+ * The byte range is all printable characters, but there is
71
+ * no trailing nul byte. We'll assume that it's a string.
72
+ */
73
+ char *s = malloc(nbytes + 1);
74
+ if (!s) {
75
+ rb_raise(eDtraceException, "out of memory: failed to allocate string value");
76
+ return (Qnil);
77
+ }
78
+ (void) strncpy(s, c, nbytes);
79
+ s[nbytes] = '\0';
80
+ robj = rb_str_new2(s);
81
+ free(s);
82
+ return (robj);
83
+ }
84
+
85
+ /* return byte array */
86
+ robj = rb_ary_new();
87
+ for (i = 0; i < nbytes; i++)
88
+ rb_ary_push(robj, INT2FIX(addr[i]));
89
+
90
+ return (robj);
91
+ }
data/ext/extconf.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  require 'mkmf'
2
- $CFLAGS += " -D_LONGLONG_TYPE"
2
+ $CFLAGS += " -D_LONGLONG_TYPE -g"
3
3
  have_library("dtrace", "dtrace_open")
4
4
  create_makefile("dtrace_api")
data/lib/dtrace.rb CHANGED
@@ -4,8 +4,13 @@
4
4
  #
5
5
 
6
6
  require 'dtrace_api'
7
+ require 'dtracerecord'
8
+ require 'dtraceconsumer'
9
+ require 'dtraceaggregate'
10
+ require 'dtraceprobedata'
11
+ require 'dtraceprobe'
7
12
 
8
13
  class Dtrace
9
- VERSION = '0.0.1'
14
+ VERSION = '0.0.2'
10
15
  end
11
16
 
@@ -0,0 +1,13 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class DtraceAggregate
7
+ attr_accessor :value, :tuple
8
+
9
+ def initialize
10
+ @tuple = Array.new
11
+ end
12
+
13
+ end
@@ -0,0 +1,99 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class DtraceConsumer
7
+
8
+ def initialize(t)
9
+ @t = t
10
+ @curragg = DtraceAggregate.new
11
+ end
12
+
13
+ def consume
14
+
15
+ probe_consumer = proc do |probe|
16
+ yield probe
17
+ end
18
+
19
+ rec_consumer = proc do |rec|
20
+ #yield rec
21
+ end
22
+
23
+ buf_consumer = proc do |buf|
24
+ r = buf.record
25
+ # buf records can be empty (trace();)
26
+ if r
27
+ case r.class.to_s
28
+ when DtraceRecord.to_s
29
+ yield r
30
+ when DtraceAggData.to_s
31
+ case r.aggtype
32
+ when "tuple"
33
+ @curragg.tuple << r.value
34
+ when "value"
35
+ @curragg.value = r.value
36
+ when "last"
37
+ yield @curragg
38
+ @curragg = DtraceAggregate.new
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+
45
+ @t.go
46
+ @t.buf_consumer(buf_consumer)
47
+ begin
48
+ while(true) do
49
+ @t.sleep
50
+ @t.work(probe_consumer, rec_consumer)
51
+ end
52
+ ensure
53
+ @t.stop
54
+ @t.work(probe_consumer, rec_consumer)
55
+ end
56
+
57
+ end
58
+
59
+ def consume_once
60
+
61
+ probe_consumer = proc do |probe|
62
+ probe.each_record do |r|
63
+ yield r
64
+ end
65
+ end
66
+
67
+ rec_consumer = proc do |rec|
68
+ #yield rec
69
+ end
70
+
71
+ buf_consumer = proc do |buf|
72
+ r = buf.record
73
+ # buf records can be empty (trace();)
74
+ if r
75
+ case r.class.to_s
76
+ when DtraceRecord.to_s
77
+ yield r
78
+ when DtraceAggData.to_s
79
+ case r.aggtype
80
+ when "tuple"
81
+ @curragg.tuple << r.value
82
+ when "value"
83
+ @curragg.value = r.value
84
+ when "last"
85
+ yield @curragg
86
+ @curragg = DtraceAggregate.new
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+
93
+ @t.buf_consumer(buf_consumer)
94
+ @t.stop
95
+ @t.work(probe_consumer, rec_consumer)
96
+ end
97
+
98
+ end
99
+
@@ -0,0 +1,13 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class DtraceProbe
7
+
8
+ def to_s
9
+ "#{provider}:#{mod}:#{func}:#{name}"
10
+ end
11
+
12
+ end
13
+
@@ -0,0 +1,16 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class DtraceProbeData
7
+
8
+ def records
9
+ records = Array.new
10
+ self.each_record do |rec|
11
+ records << rec
12
+ end
13
+ records
14
+ end
15
+
16
+ end
@@ -0,0 +1,8 @@
1
+ #
2
+ # Ruby-Dtrace
3
+ # (c) 2007 Chris Andrews <chris@nodnol.org>
4
+ #
5
+
6
+ class DtraceRecord
7
+ attr_accessor :value
8
+ end
@@ -3,7 +3,7 @@ require 'dtrace'
3
3
  class Dtracer
4
4
 
5
5
  def start_dtrace(pid)
6
- progtext = 'ruby$1:::function-entry{ @[strjoin(strjoin(copyinstr(arg0),"."),copyinstr(arg1))] = count(); }'
6
+ progtext = 'ruby$1:::function-entry{ @a[strjoin(strjoin(copyinstr(arg0),"."),copyinstr(arg1))] = count(); } END { printa(@a); }'
7
7
 
8
8
  begin
9
9
  @d = Dtrace.new
@@ -28,11 +28,12 @@ class Dtracer
28
28
  return {} unless @d
29
29
 
30
30
  begin
31
- @d.stop
32
- @d.aggregate_snap
33
31
  dtrace_report = Hash.new
34
- @d.each_aggregate do |agg|
35
- dtrace_report[agg[1].data] = agg[2].data
32
+ c = DtraceConsumer.new(@d)
33
+ c.consume_once do |e|
34
+ if e.respond_to? :tuple
35
+ dtrace_report[e.tuple.first] = e.value
36
+ end
36
37
  end
37
38
  rescue DtraceException => e
38
39
  puts "end: #{e.message}"