rblineprof 0.2.0

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.
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
+