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 +34 -0
- data/ext/.gitignore +3 -0
- data/ext/extconf.rb +2 -0
- data/ext/rblineprof.c +228 -0
- data/rblineprof.gemspec +14 -0
- data/test.rb +22 -0
- metadata +70 -0
    
        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
    
    
    
        data/ext/extconf.rb
    ADDED
    
    
    
        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 | 
            +
            }
         | 
    
        data/rblineprof.gemspec
    ADDED
    
    | @@ -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 | 
            +
             |