rblineprof 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # rblineprof
2
+
3
+ ```
4
+ % ruby -C ext extconf.rb
5
+ % make -C ext
6
+ % ruby test.rb
7
+ | $:.unshift 'ext'
8
+ | require 'rblineprof'
9
+ |
10
+ | profile = lineprof(/./) do
11
+ 1 | 1000.times do
12
+ |
13
+ 410 | 1*2*3
14
+ 441 | 4*5*6
15
+ 1243 | 7*8*9*10*11*12*13*14*15
16
+ 380 | 2**32
17
+ 1115 | 2**128
18
+ |
19
+ | end
20
+ | end
21
+ |
22
+ | File.readlines(__FILE__).each_with_index do |line, num|
23
+ | if (sample = profile[__FILE__][num+1]) > 0
24
+ | printf "% 6d | %s", sample, line
25
+ | else
26
+ | printf " | %s", line
27
+ | end
28
+ | end
29
+ ```
30
+
31
+ ## Other profilers
32
+
33
+ * [PLine](https://github.com/soba1104/PLine) line-profiler for ruby 1.9
34
+ * pure-ruby [LineProfiler](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/18997?help-en) for ruby 1.6
data/ext/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ Makefile
2
+ *.bundle
3
+ *.o
data/ext/extconf.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile 'rblineprof'
data/ext/rblineprof.c ADDED
@@ -0,0 +1,228 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <stdbool.h>
4
+ #include <string.h>
5
+
6
+ #include <ruby.h>
7
+ #include <node.h>
8
+ #include <intern.h>
9
+ #include <st.h>
10
+ #include <re.h>
11
+
12
+ static VALUE gc_hook;
13
+
14
+ typedef struct {
15
+ char *filename;
16
+ uint64_t *lines;
17
+ long nlines;
18
+
19
+ uint64_t last_time;
20
+ long last_line;
21
+ } sourcefile_t;
22
+
23
+ static struct {
24
+ bool enabled;
25
+
26
+ // single file mode, store filename and line data directly
27
+ char *source_filename;
28
+ sourcefile_t file;
29
+
30
+ // regex mode, store file data in hash table
31
+ VALUE source_regex;
32
+ st_table *files;
33
+ sourcefile_t *last_file;
34
+ }
35
+ rblineprof = {
36
+ .enabled = false,
37
+ .source_filename = NULL,
38
+ .source_regex = Qfalse,
39
+ .files = NULL,
40
+ .last_file = NULL
41
+ };
42
+
43
+ static uint64_t
44
+ timeofday_usec()
45
+ {
46
+ struct timeval tv;
47
+ gettimeofday(&tv, NULL);
48
+ return (uint64_t)tv.tv_sec*1e6 +
49
+ (uint64_t)tv.tv_usec;
50
+ }
51
+
52
+ static void
53
+ profiler_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
54
+ {
55
+ sourcefile_t *sourcefile = NULL;
56
+
57
+ char *file = node->nd_file;
58
+ long line = nd_line(node);
59
+
60
+ if (!file) return;
61
+ if (line <= 0) return;
62
+
63
+ if (rblineprof.source_filename) { // single file mode
64
+ if (rblineprof.source_filename == file) {
65
+ sourcefile = &rblineprof.file;
66
+ sourcefile->filename = file;
67
+ } else {
68
+ return;
69
+ }
70
+
71
+ } else { // regex mode
72
+ st_lookup(rblineprof.files, (st_data_t)file, (st_data_t *)&sourcefile);
73
+
74
+ if ((VALUE)sourcefile == Qnil) // known negative match, skip
75
+ return;
76
+
77
+ if (!sourcefile) { // unknown file, check against regex
78
+ if (rb_reg_search(rblineprof.source_regex, rb_str_new2(file), 0, 0) >= 0) {
79
+ sourcefile = ALLOC_N(sourcefile_t, 1);
80
+ MEMZERO(sourcefile, sourcefile_t, 1);
81
+ sourcefile->filename = strdup(file);
82
+ st_insert(rblineprof.files, (st_data_t)sourcefile->filename, (st_data_t)sourcefile);
83
+ } else { // no match, insert Qnil to prevent regex next time
84
+ st_insert(rblineprof.files, (st_data_t)strdup(file), (st_data_t)Qnil);
85
+ return;
86
+ }
87
+ }
88
+ }
89
+
90
+ if (sourcefile) {
91
+ uint64_t now = timeofday_usec();
92
+
93
+ if (sourcefile->last_time) {
94
+ /* allocate space for per-line data the first time */
95
+ if (sourcefile->lines == NULL) {
96
+ sourcefile->nlines = sourcefile->last_line + 100;
97
+ sourcefile->lines = ALLOC_N(uint64_t, sourcefile->nlines);
98
+ MEMZERO(sourcefile->lines, uint64_t, sourcefile->nlines);
99
+ }
100
+
101
+ /* grow the per-line array if necessary */
102
+ if (sourcefile->last_line >= sourcefile->nlines) {
103
+ long prev_nlines = sourcefile->nlines;
104
+ sourcefile->nlines = sourcefile->last_line + 100;
105
+
106
+ REALLOC_N(sourcefile->lines, uint64_t, sourcefile->nlines);
107
+ MEMZERO(sourcefile->lines + prev_nlines, uint64_t, sourcefile->nlines - prev_nlines);
108
+ }
109
+
110
+ /* record the sample */
111
+ sourcefile->lines[sourcefile->last_line] += (now - sourcefile->last_time);
112
+ }
113
+
114
+ sourcefile->last_time = now;
115
+ sourcefile->last_line = line;
116
+
117
+ if (rblineprof.last_file && rblineprof.last_file != sourcefile)
118
+ rblineprof.last_file->last_time = 0;
119
+
120
+ rblineprof.last_file = sourcefile;
121
+ }
122
+ }
123
+
124
+ static int
125
+ cleanup_files(st_data_t key, st_data_t record, st_data_t arg)
126
+ {
127
+ xfree((char *)key);
128
+
129
+ sourcefile_t *sourcefile = (sourcefile_t*)record;
130
+ if (!sourcefile || (VALUE)sourcefile == Qnil) return ST_DELETE;
131
+
132
+ if (sourcefile->lines)
133
+ xfree(sourcefile->lines);
134
+ xfree(sourcefile);
135
+
136
+ return ST_DELETE;
137
+ }
138
+
139
+ static int
140
+ summarize_files(st_data_t key, st_data_t record, st_data_t arg)
141
+ {
142
+ sourcefile_t *sourcefile = (sourcefile_t*)record;
143
+ if (!sourcefile || (VALUE)sourcefile == Qnil) return ST_CONTINUE;
144
+
145
+ VALUE ret = (VALUE)arg;
146
+ VALUE ary = rb_ary_new();
147
+ long i;
148
+
149
+ for (i=0; i<sourcefile->nlines; i++)
150
+ rb_ary_store(ary, i, ULL2NUM(sourcefile->lines[i]));
151
+
152
+ rb_hash_aset(ret, rb_str_new2(sourcefile->filename), ary);
153
+
154
+ return ST_CONTINUE;
155
+ }
156
+
157
+ static VALUE
158
+ lineprof_ensure(VALUE self)
159
+ {
160
+ rb_remove_event_hook(profiler_hook);
161
+ rblineprof.enabled = false;
162
+ }
163
+
164
+ VALUE
165
+ lineprof(VALUE self, VALUE filename)
166
+ {
167
+ if (!rb_block_given_p())
168
+ rb_raise(rb_eArgError, "block required");
169
+
170
+ if (rblineprof.enabled)
171
+ rb_raise(rb_eArgError, "profiler is already enabled");
172
+
173
+ VALUE filename_class = rb_obj_class(filename);
174
+
175
+ if (filename_class == rb_cString) {
176
+ rblineprof.source_filename = rb_source_filename(StringValuePtr(filename));
177
+ } else if (filename_class == rb_cRegexp) {
178
+ rblineprof.source_regex = filename;
179
+ rblineprof.source_filename = NULL;
180
+ } else {
181
+ rb_raise(rb_eArgError, "argument must be String or Regexp");
182
+ }
183
+
184
+ // cleanup
185
+ rblineprof.last_file = NULL;
186
+ st_foreach(rblineprof.files, cleanup_files, 0);
187
+ if (rblineprof.file.lines) {
188
+ xfree(rblineprof.file.lines);
189
+ rblineprof.file.lines = NULL;
190
+ rblineprof.file.nlines = 0;
191
+ }
192
+
193
+ rblineprof.enabled = true;
194
+ rb_add_event_hook(profiler_hook, RUBY_EVENT_LINE);
195
+ rb_ensure(rb_yield, Qnil, lineprof_ensure, self);
196
+
197
+ VALUE ret = rb_hash_new();
198
+ VALUE ary = Qnil;
199
+
200
+ if (rblineprof.source_filename) {
201
+ long i;
202
+ ary = rb_ary_new();
203
+ for (i=0; i<rblineprof.file.nlines; i++)
204
+ rb_ary_store(ary, i, ULL2NUM(rblineprof.file.lines[i]));
205
+ rb_hash_aset(ret, rb_str_new2(rblineprof.source_filename), ary);
206
+ } else {
207
+ st_foreach(rblineprof.files, summarize_files, ret);
208
+ }
209
+
210
+ return ret;
211
+ }
212
+
213
+ static void
214
+ rblineprof_gc_mark()
215
+ {
216
+ if (rblineprof.enabled)
217
+ rb_gc_mark_maybe(rblineprof.source_regex);
218
+ }
219
+
220
+ void
221
+ Init_rblineprof()
222
+ {
223
+ gc_hook = Data_Wrap_Struct(rb_cObject, rblineprof_gc_mark, NULL, NULL);
224
+ rb_global_variable(&gc_hook);
225
+
226
+ rblineprof.files = st_init_strtable();
227
+ rb_define_global_function("lineprof", lineprof, 1);
228
+ }
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'rblineprof'
3
+ s.version = '0.2.0'
4
+ s.homepage = 'http://github.com/tmm1/rblineprof'
5
+
6
+ s.authors = 'Aman Gupta'
7
+ s.email = 'aman@tmm1.net'
8
+
9
+ s.files = `git ls-files`.split("\n")
10
+ s.extensions = 'ext/extconf.rb'
11
+
12
+ s.summary = 'line-profiler for ruby 1.8'
13
+ s.description = 'rblineprof shows you lines of code that are slow.'
14
+ end
data/test.rb ADDED
@@ -0,0 +1,22 @@
1
+ $:.unshift 'ext'
2
+ require 'rblineprof'
3
+
4
+ profile = lineprof(/./) do
5
+ 1000.times do
6
+
7
+ 1*2*3
8
+ 4*5*6
9
+ 7*8*9*10*11*12*13*14*15
10
+ 2**32
11
+ 2**128
12
+
13
+ end
14
+ end
15
+
16
+ File.readlines(__FILE__).each_with_index do |line, num|
17
+ if (sample = profile[__FILE__][num+1]) > 0
18
+ printf "% 6d | %s", sample, line
19
+ else
20
+ printf " | %s", line
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rblineprof
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Aman Gupta
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-09-18 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: rblineprof shows you lines of code that are slow.
22
+ email: aman@tmm1.net
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/extconf.rb
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - README.md
31
+ - ext/.gitignore
32
+ - ext/extconf.rb
33
+ - ext/rblineprof.c
34
+ - rblineprof.gemspec
35
+ - test.rb
36
+ homepage: http://github.com/tmm1/rblineprof
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ hash: 3
50
+ segments:
51
+ - 0
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.8.24
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: line-profiler for ruby 1.8
69
+ test_files: []
70
+