bleak_house 4.6 → 5
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/CHANGELOG +8 -61
- data/{LICENSE → LICENSE_AFL} +0 -0
- data/Manifest +22 -21
- data/README +25 -109
- data/Rakefile +101 -9
- data/init.rb +2 -0
- data/install.rb +7 -0
- data/lib/bleak_house.rb +8 -17
- data/{LICENSE_BSD → lib/bleak_house/LICENSE_BSD} +0 -0
- data/lib/bleak_house/action_controller.rb +16 -0
- data/lib/bleak_house/analyze.rb +135 -0
- data/lib/bleak_house/bleak_house.rb +43 -0
- data/lib/bleak_house/c.rb +184 -0
- data/lib/bleak_house/dispatcher.rb +20 -0
- data/lib/bleak_house/gruff_hacks.rb +62 -0
- data/lib/bleak_house/mem_logger.rb +15 -0
- data/lib/bleak_house/rake_task_redefine_task.rb +25 -0
- data/lib/bleak_house/ruby.rb +46 -0
- data/lib/bleak_house/support_methods.rb +47 -0
- data/patches/gc.c.patch +30 -0
- data/tasks/bleak_house_tasks.rake +14 -0
- data/test/unit/test_bleak_house.rb +31 -41
- metadata +81 -101
- data.tar.gz.sig +0 -0
- data/TODO +0 -10
- data/bin/bleak +0 -13
- data/bleak_house.gemspec +0 -36
- data/ext/build_ruby.rb +0 -114
- data/ext/build_snapshot.rb +0 -5
- data/ext/extconf.rb +0 -26
- data/ext/snapshot.c +0 -151
- data/ext/snapshot.h +0 -59
- data/lib/bleak_house/analyzer.rb +0 -54
- data/lib/bleak_house/hook.rb +0 -24
- data/ruby/ruby-1.8.7-p174.tar.bz2 +0 -0
- data/ruby/ruby-1.8.7.patch +0 -483
- data/test/benchmark/bench.rb +0 -16
- data/test/test_helper.rb +0 -6
- metadata.gz.sig +0 -0
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            class BleakHouse  
         | 
| 3 | 
            +
              cattr_accessor :last_request_name
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def self.set_request_name request, other = nil
         | 
| 6 | 
            +
                self.last_request_name = "#{request.parameters['controller']}/#{request.parameters['action']}/#{request.request_method}#{other}"
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def self.debug s
         | 
| 10 | 
            +
                s = "#{name.underscore}: #{s}"
         | 
| 11 | 
            +
                ::ActiveRecord::Base.logger.debug s if ::ActiveRecord::Base.logger
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
              def self.warn s
         | 
| 15 | 
            +
                s = "#{name.underscore}: #{s}"
         | 
| 16 | 
            +
                if ::ActiveRecord::Base.logger
         | 
| 17 | 
            +
                  ::ActiveRecord::Base.logger.warn s
         | 
| 18 | 
            +
                else
         | 
| 19 | 
            +
                  $stderr.puts s
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              LOGFILE = "#{RAILS_ROOT}/log/bleak_house_#{RAILS_ENV}.dump"
         | 
| 24 | 
            +
              if File.exists?(LOGFILE)
         | 
| 25 | 
            +
                File.rename(LOGFILE, "#{LOGFILE}.old") 
         | 
| 26 | 
            +
                warn "renamed old logfile"
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
              
         | 
| 29 | 
            +
              WITH_SPECIALS = false
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              MEMLOGGER = if ENV['MEMLOGGER'] !~ /ruby/i and (ENV['MEMLOGGER'] =~ /c/i or 
         | 
| 32 | 
            +
                `which ruby-bleak-house` !~ /no ruby_bleak_house/)    
         | 
| 33 | 
            +
                require 'bleak_house/c'
         | 
| 34 | 
            +
                warn "using C instrumentation"
         | 
| 35 | 
            +
                CLogger.new
         | 
| 36 | 
            +
              else
         | 
| 37 | 
            +
                require 'bleak_house/ruby'
         | 
| 38 | 
            +
                warn "using pure Ruby instrumentation"
         | 
| 39 | 
            +
                warn "(build the patched binary for speed and accuracy improvements)"
         | 
| 40 | 
            +
                RubyLogger.new
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            end
         | 
| @@ -0,0 +1,184 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require 'rubygems'
         | 
| 3 | 
            +
            require 'inline'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class BleakHouse
         | 
| 6 | 
            +
              class CLogger < MemLogger
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                MAX_UNIQ_TAGS = 1536 # per frame
         | 
| 9 | 
            +
                MAX_TAG_LENGTH = 192 # tag plus fully namespaced classname
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                inline do |builder|
         | 
| 12 | 
            +
                  builder.include '"node.h"' # struct RNode
         | 
| 13 | 
            +
                  builder.include '"st.h"'  # struct st_table
         | 
| 14 | 
            +
                  builder.include '"re.h"'  # struct RRegexp
         | 
| 15 | 
            +
                  builder.include '"env.h"' # various structs
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  builder.prefix <<-EOC
         | 
| 18 | 
            +
                    typedef struct RVALUE {
         | 
| 19 | 
            +
                        union {
         | 
| 20 | 
            +
                            struct {
         | 
| 21 | 
            +
                                unsigned long flags; /* always 0 for freed obj */
         | 
| 22 | 
            +
                                struct RVALUE *next;
         | 
| 23 | 
            +
                            } free;
         | 
| 24 | 
            +
                            struct RBasic  basic;
         | 
| 25 | 
            +
                            struct RObject object;
         | 
| 26 | 
            +
                            struct RClass  klass;
         | 
| 27 | 
            +
                            struct RFloat  flonum;
         | 
| 28 | 
            +
                            struct RString string;
         | 
| 29 | 
            +
                            struct RArray  array;
         | 
| 30 | 
            +
                            struct RRegexp regexp;
         | 
| 31 | 
            +
                            struct RHash   hash;
         | 
| 32 | 
            +
                            struct RData   data;
         | 
| 33 | 
            +
                            struct RStruct rstruct;
         | 
| 34 | 
            +
                            struct RBignum bignum;
         | 
| 35 | 
            +
                            struct RFile   file;
         | 
| 36 | 
            +
                            struct RNode   node;
         | 
| 37 | 
            +
                            struct RMatch  match;
         | 
| 38 | 
            +
                            struct RVarmap varmap;
         | 
| 39 | 
            +
                            struct SCOPE   scope;
         | 
| 40 | 
            +
                        } as;
         | 
| 41 | 
            +
                    } RVALUE;
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    struct heaps_slot {
         | 
| 44 | 
            +
                        void *membase;
         | 
| 45 | 
            +
                        RVALUE *slot;
         | 
| 46 | 
            +
                        int limit;
         | 
| 47 | 
            +
                    };
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    struct heaps_slot * rb_gc_heap_slots();
         | 
| 50 | 
            +
                    int rb_gc_heaps_used();
         | 
| 51 | 
            +
                    int rb_gc_heaps_length();
         | 
| 52 | 
            +
                  EOC
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  # number of struct heaps_slots used
         | 
| 55 | 
            +
                  builder.c <<-EOC
         | 
| 56 | 
            +
                    static int
         | 
| 57 | 
            +
                    heaps_used() {
         | 
| 58 | 
            +
                      return rb_gc_heaps_used();
         | 
| 59 | 
            +
                    }
         | 
| 60 | 
            +
                  EOC
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  # length of the struct heaps_slots allocated
         | 
| 63 | 
            +
                  builder.c <<-EOC
         | 
| 64 | 
            +
                    static int
         | 
| 65 | 
            +
                    heaps_length() {
         | 
| 66 | 
            +
                      return rb_gc_heaps_length();
         | 
| 67 | 
            +
                    }
         | 
| 68 | 
            +
                  EOC
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  OBJ_TYPES = ["T_NIL",
         | 
| 71 | 
            +
                              "T_OBJECT",
         | 
| 72 | 
            +
                              "T_CLASS",
         | 
| 73 | 
            +
                              "T_ICLASS",
         | 
| 74 | 
            +
                              "T_MODULE",
         | 
| 75 | 
            +
                              "T_FLOAT",
         | 
| 76 | 
            +
                              "T_STRING",
         | 
| 77 | 
            +
                              "T_REGEXP",
         | 
| 78 | 
            +
                              "T_ARRAY",
         | 
| 79 | 
            +
                              "T_FIXNUM",
         | 
| 80 | 
            +
                              "T_HASH",
         | 
| 81 | 
            +
                              "T_STRUCT",
         | 
| 82 | 
            +
                              "T_BIGNUM",
         | 
| 83 | 
            +
                              "T_FILE",
         | 
| 84 | 
            +
                              "T_TRUE",
         | 
| 85 | 
            +
                              "T_FALSE",
         | 
| 86 | 
            +
                              "T_DATA",
         | 
| 87 | 
            +
                              "T_SYMBOL",
         | 
| 88 | 
            +
                              "T_MATCH"]
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  RAW_TYPES = ["T_NONE",
         | 
| 91 | 
            +
                              "T_BLKTAG",
         | 
| 92 | 
            +
                              "T_UNDEF",
         | 
| 93 | 
            +
                              "T_VARMAP",
         | 
| 94 | 
            +
                              "T_SCOPE",
         | 
| 95 | 
            +
                              "T_NODE"]
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  # writes a frame to a file
         | 
| 98 | 
            +
                  builder.c <<-EOC
         | 
| 99 | 
            +
                    static void
         | 
| 100 | 
            +
                    VALUE snapshot(VALUE path, VALUE tag, VALUE _specials) {
         | 
| 101 | 
            +
                      Check_Type(path, T_STRING);
         | 
| 102 | 
            +
                      Check_Type(tag, T_STRING);
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                      RVALUE *p, *pend;
         | 
| 105 | 
            +
                      struct heaps_slot * heaps = rb_gc_heap_slots();
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                      int specials = RTEST(_specials);
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                      FILE *obj_log = fopen(StringValueCStr(path), "r");
         | 
| 110 | 
            +
                      int is_new;
         | 
| 111 | 
            +
                      if (!(is_new = (obj_log == NULL)))
         | 
| 112 | 
            +
                        fclose(obj_log);
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      if ((obj_log = fopen(StringValueCStr(path), "a+")) == NULL)
         | 
| 115 | 
            +
                        rb_raise(rb_eRuntimeError, "couldn't open snapshot file");
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                      if (is_new) 
         | 
| 118 | 
            +
                        fprintf(obj_log, \"---\\n");
         | 
| 119 | 
            +
                      fprintf(obj_log, \"- - %i\\n", time(0));
         | 
| 120 | 
            +
                      VALUE mem = rb_funcall(self, rb_intern("mem_usage"), 0);
         | 
| 121 | 
            +
                      fprintf(obj_log, \"  - :\\"memory usage/swap\\": %i\\n", NUM2INT(RARRAY_PTR(mem)[0]));
         | 
| 122 | 
            +
                      fprintf(obj_log, \"    :\\"memory usage/real\\": %i\\n", NUM2INT(RARRAY_PTR(mem)[1]));
         | 
| 123 | 
            +
                      
         | 
| 124 | 
            +
                      /* haha */
         | 
| 125 | 
            +
                      char tags[#{MAX_UNIQ_TAGS}][#{MAX_TAG_LENGTH}];
         | 
| 126 | 
            +
                      char current_tag[2048];
         | 
| 127 | 
            +
                      int counts[#{MAX_UNIQ_TAGS}];
         | 
| 128 | 
            +
                      int current_pos = 0;
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                      int i, j;
         | 
| 131 | 
            +
                      for (i = 0; i < rb_gc_heaps_used(); i++) {
         | 
| 132 | 
            +
                        p = heaps[i].slot;
         | 
| 133 | 
            +
                        pend = p + heaps[i].limit;
         | 
| 134 | 
            +
                        for (; p < pend; p++) {
         | 
| 135 | 
            +
                          if (p->as.basic.flags) { /* always 0 for freed objects */
         | 
| 136 | 
            +
                            sprintf(current_tag, "");
         | 
| 137 | 
            +
                            switch (TYPE(p)) {
         | 
| 138 | 
            +
                              #{RAW_TYPES.map do |type|
         | 
| 139 | 
            +
                                  "case #{type}:
         | 
| 140 | 
            +
                                      if (specials)
         | 
| 141 | 
            +
                                        sprintf(current_tag , \"%s::::_#{type[2..-1].downcase}\", StringValueCStr(tag));
         | 
| 142 | 
            +
                                      break;
         | 
| 143 | 
            +
                                  "
         | 
| 144 | 
            +
                                end.flatten.join}
         | 
| 145 | 
            +
                              default:
         | 
| 146 | 
            +
                                if (!p->as.basic.klass) {
         | 
| 147 | 
            +
                                  sprintf(current_tag , "%s::::_unknown", StringValueCStr(tag));
         | 
| 148 | 
            +
                                } else {
         | 
| 149 | 
            +
                                  sprintf(current_tag , "%s::::%s", StringValueCStr(tag), rb_obj_classname((VALUE)p));
         | 
| 150 | 
            +
                                }
         | 
| 151 | 
            +
                            }
         | 
| 152 | 
            +
                            if (strlen(current_tag) > #{MAX_TAG_LENGTH})
         | 
| 153 | 
            +
                              rb_raise(rb_eRuntimeError, "tag + classname too big; increase MAX_TAG_LENGTH (#{MAX_TAG_LENGTH})");
         | 
| 154 | 
            +
                            if (strcmp(current_tag, "")) {
         | 
| 155 | 
            +
                              for (j = 0; j < current_pos; j++) {
         | 
| 156 | 
            +
                                if (!strcmp(tags[j], current_tag)) {
         | 
| 157 | 
            +
                                  counts[j] ++;
         | 
| 158 | 
            +
                                  break;
         | 
| 159 | 
            +
                                }
         | 
| 160 | 
            +
                              }
         | 
| 161 | 
            +
                              if (j == current_pos) {
         | 
| 162 | 
            +
                                /* found a new one */
         | 
| 163 | 
            +
                                if (current_pos == #{MAX_UNIQ_TAGS})
         | 
| 164 | 
            +
                                  rb_raise(rb_eRuntimeError, "exhausted tag array; increase MAX_UNIQ_TAGS (#{MAX_UNIQ_TAGS})");
         | 
| 165 | 
            +
                                sprintf(tags[current_pos], current_tag);
         | 
| 166 | 
            +
                                counts[current_pos] = 1;
         | 
| 167 | 
            +
                                current_pos ++;
         | 
| 168 | 
            +
                              }
         | 
| 169 | 
            +
                            }
         | 
| 170 | 
            +
                          }
         | 
| 171 | 
            +
                        }
         | 
| 172 | 
            +
                      }
         | 
| 173 | 
            +
                      
         | 
| 174 | 
            +
                      for (j = 0; j < current_pos; j++) {
         | 
| 175 | 
            +
                        fprintf(obj_log, "    :\\"%s\\": %i\\n", tags[j], counts[j]);
         | 
| 176 | 
            +
                      }
         | 
| 177 | 
            +
                      fclose(obj_log);
         | 
| 178 | 
            +
                      return Qtrue;
         | 
| 179 | 
            +
                    }
         | 
| 180 | 
            +
                  EOC
         | 
| 181 | 
            +
                end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              end
         | 
| 184 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            # crazyness
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class Dispatcher
         | 
| 5 | 
            +
              class << self
         | 
| 6 | 
            +
                def prepare_application_with_bleak_house
         | 
| 7 | 
            +
                  prepare_application_without_bleak_house
         | 
| 8 | 
            +
                  BleakHouse::MEMLOGGER.snapshot(BleakHouse::LOGFILE, 'core rails', BleakHouse::WITH_SPECIALS)
         | 
| 9 | 
            +
                  GC.start
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
                alias_method_chain :prepare_application, :bleak_house
         | 
| 12 | 
            +
                
         | 
| 13 | 
            +
                def reset_after_dispatch_with_bleak_house
         | 
| 14 | 
            +
                  BleakHouse::MEMLOGGER.snapshot(BleakHouse::LOGFILE, BleakHouse.last_request_name || 'unknown', BleakHouse::WITH_SPECIALS)
         | 
| 15 | 
            +
                  GC.start
         | 
| 16 | 
            +
                  reset_after_dispatch_without_bleak_house
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                alias_method_chain :reset_after_dispatch, :bleak_house
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,62 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            class Gruff::Base
         | 
| 3 | 
            +
              
         | 
| 4 | 
            +
              def draw_legend
         | 
| 5 | 
            +
                @legend_labels = @data.collect {|item| item[DATA_LABEL_INDEX] }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                legend_square_width = @legend_box_size # small square with color of this item
         | 
| 8 | 
            +
                legend_left = 10
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                current_x_offset = legend_left
         | 
| 11 | 
            +
                current_y_offset =  TOP_MARGIN + TITLE_MARGIN + @title_caps_height + LEGEND_MARGIN
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                debug { @d.line 0.0, current_y_offset, @raw_columns, current_y_offset }
         | 
| 14 | 
            +
                                                              
         | 
| 15 | 
            +
                @legend_labels.each_with_index do |legend_label, index|      
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  next if index > MAX_LEGENDS
         | 
| 18 | 
            +
                  legend_label = "Some not shown" if index == MAX_LEGENDS
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Draw label
         | 
| 21 | 
            +
                  @d.fill = @font_color
         | 
| 22 | 
            +
                  @d.font = @font if @font
         | 
| 23 | 
            +
                  @d.pointsize = scale_fontsize(@legend_font_size)
         | 
| 24 | 
            +
                  @d.stroke('transparent')
         | 
| 25 | 
            +
                  @d.font_weight = NormalWeight
         | 
| 26 | 
            +
                  @d.gravity = WestGravity
         | 
| 27 | 
            +
                  @d = @d.annotate_scaled( @base_image, 
         | 
| 28 | 
            +
                                    @raw_columns, 1.0,
         | 
| 29 | 
            +
                                    current_x_offset + (legend_square_width * 1.7), current_y_offset, 
         | 
| 30 | 
            +
                                    legend_label.to_s, @scale)
         | 
| 31 | 
            +
                  
         | 
| 32 | 
            +
                  if index < MAX_LEGENDS
         | 
| 33 | 
            +
                  # Now draw box with color of this dataset
         | 
| 34 | 
            +
                    @d = @d.stroke('transparent')
         | 
| 35 | 
            +
                    @d = @d.fill @data[index][DATA_COLOR_INDEX]
         | 
| 36 | 
            +
                    @d = @d.rectangle(current_x_offset, 
         | 
| 37 | 
            +
                                      current_y_offset - legend_square_width / 2.0, 
         | 
| 38 | 
            +
                                      current_x_offset + legend_square_width, 
         | 
| 39 | 
            +
                                      current_y_offset + legend_square_width / 2.0)
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  @d.pointsize = @legend_font_size
         | 
| 43 | 
            +
                  metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
         | 
| 44 | 
            +
                  current_y_offset += metrics.height * 1.05
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
                @color_index = 0
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
              
         | 
| 49 | 
            +
              alias :setup_graph_measurements_without_top_margin :setup_graph_measurements
         | 
| 50 | 
            +
              def setup_graph_measurements  
         | 
| 51 | 
            +
                setup_graph_measurements_without_top_margin
         | 
| 52 | 
            +
                @graph_height += NEGATIVE_TOP_MARGIN
         | 
| 53 | 
            +
                @graph_top -= NEGATIVE_TOP_MARGIN    
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
              
         | 
| 56 | 
            +
              alias :clip_value_if_greater_than_without_size_hacks :clip_value_if_greater_than
         | 
| 57 | 
            +
              def clip_value_if_greater_than(arg1, arg2)
         | 
| 58 | 
            +
                 arg2 = arg2 / 2 if arg1 == @columns / (@norm_data.first[1].size * 2.5)
         | 
| 59 | 
            +
                 clip_value_if_greater_than_without_size_hacks(arg1, arg2)
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
              
         | 
| 62 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            class BleakHouse
         | 
| 3 | 
            +
              class MemLogger
         | 
| 4 | 
            +
                def snapshot
         | 
| 5 | 
            +
                  raise "abstract method; please require 'ruby' or 'c'"
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                def mem_usage
         | 
| 9 | 
            +
                  a = `ps -o vsz,rss -p #{Process.pid}`.split(/\s+/)[-2..-1].map{|el| el.to_i}
         | 
| 10 | 
            +
                  [a.first - a.last, a.last]
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
             | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            # http://www.bigbold.com/snippets/posts/show/2032
         | 
| 3 | 
            +
            module Rake
         | 
| 4 | 
            +
              module TaskManager
         | 
| 5 | 
            +
                def redefine_task(task_class, args, &block)
         | 
| 6 | 
            +
                  task_name, deps = resolve_args(args)
         | 
| 7 | 
            +
                  task_name = task_class.scope_name(@scope, task_name)
         | 
| 8 | 
            +
                  deps = [deps] unless deps.respond_to?(:to_ary)
         | 
| 9 | 
            +
                  deps = deps.collect {|d| d.to_s }
         | 
| 10 | 
            +
                  task = @tasks[task_name.to_s] = task_class.new(task_name, self)
         | 
| 11 | 
            +
                  task.application = self
         | 
| 12 | 
            +
                  task.add_comment(@last_comment)
         | 
| 13 | 
            +
                  @last_comment = nil
         | 
| 14 | 
            +
                  task.enhance(deps, &block)
         | 
| 15 | 
            +
                  task
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
              class Task
         | 
| 19 | 
            +
                class << self
         | 
| 20 | 
            +
                  def redefine_task(args, &block)
         | 
| 21 | 
            +
                    Rake.application.redefine_task(self, args, &block)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            require 'yaml'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class BleakHouse
         | 
| 5 | 
            +
              class RubyLogger < MemLogger
         | 
| 6 | 
            +
                SWAP = :"memory usage/swap"
         | 
| 7 | 
            +
                RSS = :"memory usage/real"  
         | 
| 8 | 
            +
                SEEN = {}
         | 
| 9 | 
            +
                CURRENT = {}
         | 
| 10 | 
            +
                TAGS = Hash.new(0)
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                def snapshot(path, tag, _specials)
         | 
| 13 | 
            +
                  CURRENT.clear
         | 
| 14 | 
            +
                  ObjectSpace.each_object do |obj|
         | 
| 15 | 
            +
                    CURRENT[obj_id = obj._bleak_house_object_id] = true
         | 
| 16 | 
            +
                    unless SEEN[obj_id]
         | 
| 17 | 
            +
                      # symbols will rapidly stabilize
         | 
| 18 | 
            +
                      SEEN[obj_id] = "#{tag}::::#{obj._bleak_house_class}".to_sym 
         | 
| 19 | 
            +
                      TAGS[SEEN[obj_id]] += 1
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  SEEN.keys.each do |obj_id|
         | 
| 23 | 
            +
                    TAGS[SEEN.delete(obj_id)] -= 1 unless CURRENT[obj_id]          
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                  CURRENT.clear
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  TAGS[SWAP], TAGS[RSS] = mem_usage
         | 
| 28 | 
            +
                                  
         | 
| 29 | 
            +
                  write(path)  
         | 
| 30 | 
            +
                end        
         | 
| 31 | 
            +
                
         | 
| 32 | 
            +
                def write(path)
         | 
| 33 | 
            +
                  exists = File.exist? path
         | 
| 34 | 
            +
                  File.open(path, 'a+') do |log|
         | 
| 35 | 
            +
                    dump = YAML.dump([[Time.now.to_i, TAGS]])
         | 
| 36 | 
            +
                    log.write(exists ? dump[5..-1] : dump)
         | 
| 37 | 
            +
                  end      
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            class Object
         | 
| 44 | 
            +
              alias :_bleak_house_object_id :object_id
         | 
| 45 | 
            +
              alias :_bleak_house_class  :class
         | 
| 46 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            class Array
         | 
| 3 | 
            +
              alias :time :first
         | 
| 4 | 
            +
              alias :data :last
         | 
| 5 | 
            +
              
         | 
| 6 | 
            +
              def sum
         | 
| 7 | 
            +
                inject(0) {|s, x| x + s}
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
              
         | 
| 10 | 
            +
              def to_i
         | 
| 11 | 
            +
                self.map{|s| s.to_i}
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            class Dir
         | 
| 17 | 
            +
              def self.descend path, &block
         | 
| 18 | 
            +
                path = path.split("/") unless path.is_a? Array
         | 
| 19 | 
            +
                top = (path.shift or ".")
         | 
| 20 | 
            +
                Dir.mkdir(top) unless File.exists? top
         | 
| 21 | 
            +
                Dir.chdir(top) do
         | 
| 22 | 
            +
                  if path.any?
         | 
| 23 | 
            +
                    descend path, &block
         | 
| 24 | 
            +
                  else
         | 
| 25 | 
            +
                    block.call
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            class String
         | 
| 32 | 
            +
              def to_filename
         | 
| 33 | 
            +
                self.downcase.gsub(/[^\w\d\-]/, '_')
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            class NilClass
         | 
| 38 | 
            +
              def +(op)
         | 
| 39 | 
            +
                self.to_i + op
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             
         | 
| 43 | 
            +
            class Symbol
         | 
| 44 | 
            +
              def =~ regex
         | 
| 45 | 
            +
                self.to_s =~ regex
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         |