ruby-dtrace 0.0.1 → 0.0.2

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,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}"