Module: BleakHouse

Defined in:
lib/bleak_house.rb,
lib/bleak_house/hook.rb,
lib/bleak_house/analyzer.rb,
ext/extconf.rb,
ext/snapshot.c

Defined Under Namespace

Modules: Analyzer

Constant Summary collapse

LOGDIR =
File.expand_path('../../log', __FILE__)
LOGFILE =
File.join(LOGDIR, 'bleak_house.log')

Class Method Summary collapse

Class Method Details

.execute(command) ⇒ Object



14
15
16
17
18
19
# File 'ext/extconf.rb', line 14

def self.execute(command)    
  unless system(command)
    puts File.open(LOGFILE).read
    exit -1 
  end
end

.ext_snapshot(_logfile, _gc_runs) ⇒ Object

Inner method; call BleakHouse.snapshot instead.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'ext/snapshot.c', line 18

static VALUE ext_snapshot(VALUE self, VALUE _logfile, VALUE _gc_runs) {
  Check_Type(_logfile, T_STRING);
  Check_Type(_gc_runs, T_FIXNUM);

  RVALUE *obj, *obj_end;
  st_table_entry *sym;

  struct heaps_slot * heaps = rb_gc_heap_slots();
  struct st_table * sym_tbl = rb_parse_sym_tbl();

  /* see if the logfile exists already */
  FILE *logfile = fopen(StringValueCStr(_logfile), "r");
  int is_new;
  if (!(is_new = (logfile == NULL)))
    fclose(logfile);

  /* reopen for writing */
  if ((logfile = fopen(StringValueCStr(_logfile), "w")) == NULL)
    rb_raise(rb_eRuntimeError, "couldn't open snapshot file");

  int filled_slots = 0;
  int free_slots = 0;

  int i;
  int gc_runs = FIX2INT(_gc_runs);
  char * chr;

  for (i = 0; i < gc_runs; i++) {
    /* request GC run */
    rb_funcall(rb_mGC, rb_intern("start"), 0);
    rb_thread_schedule();
  }
  fprintf(logfile, "%i GC runs performed before dump\n", gc_runs);

  /* walk the heap */
  for (i = 0; i < rb_gc_heaps_used(); i++) {
    obj = heaps[i].slot;
    obj_end = obj + heaps[i].limit;
    for (; obj < obj_end; obj++) {
      if (obj->as.basic.flags) { /* always 0 for freed objects */
        filled_slots ++;

        /* write the source file*/
        if (obj->file) {
          chr = obj->file;
          if (*chr != '\0') {
            fprintf(logfile, "%s", obj->file);
          } else {
            fprintf(logfile, "__empty__");
          }
        } else {
          fprintf(logfile, "__null__");
        }

        /* write the source line */
        fprintf(logfile, ":");
        if (obj->line) {
          fprintf(logfile, "%i", obj->line);
        } else {
          fprintf(logfile, "__null__");
        }

        /* write the class */
        fprintf(logfile, ":");
        switch (TYPE(obj)) {
          case T_NONE:
              fprintf(logfile, "__none__"); break;
          case T_BLKTAG:
              fprintf(logfile, "__blktag__"); break;
          case T_UNDEF:
              fprintf(logfile, "__undef__"); break;
          case T_VARMAP:
              fprintf(logfile, "__varmap__"); break;
          case T_SCOPE:
              fprintf(logfile, "__scope__"); break;
          case T_NODE:
              fprintf(logfile, "__node__"); break;
          default:
            if (!obj->as.basic.klass) {
              fprintf(logfile, "__unknown__");
            } else {
              fprintf(logfile, rb_obj_classname((VALUE)obj));
            }
        }

        /* write newline */
        fprintf(logfile, "\n");
      } else {
        free_slots ++;
      }
    }
  }

  /* walk the symbol table */
  /* hashed = lookup_builtin("Symbol");
  for (i = 0; i < sym_tbl->num_bins; i++) {
    for (sym = sym_tbl->bins[i]; sym != 0; sym = sym->next) {
      fprintf(logfile, "%i,%lu\n", hashed + 1, sym->record);
    }
  } */

  fprintf(logfile, "%i filled\n", filled_slots);
  fprintf(logfile, "%i free\n", free_slots);
  fclose(logfile);

  return Qnil;
}

.heaps_lengthObject

Number of allocated heaps_slots



13
14
15
# File 'ext/snapshot.c', line 13

static VALUE heaps_length(VALUE self) {
  return INT2FIX(rb_gc_heaps_length());
}

.heaps_usedObject

Number of filled heaps_slots



8
9
10
# File 'ext/snapshot.c', line 8

static VALUE heaps_used(VALUE self) {
  return INT2FIX(rb_gc_heaps_used());
}

.hook(gc_runs = 3) ⇒ Object

The body of the exit handler and SIGUSR2 trap. It writes a snapshot to a dumpfile named after the current Process.pid.



4
5
6
7
8
9
10
11
# File 'lib/bleak_house/hook.rb', line 4

def self.hook(gc_runs = 3)
  @count ||= 0
  filename = "/tmp/bleak.%s.%03i.dump" % [Process.pid,@count]
  STDERR.puts "** BleakHouse: working..."
  BleakHouse.snapshot(filename, gc_runs)
  STDERR.puts "** BleakHouse: complete\n** Bleakhouse: Run 'bleak #{filename}' to analyze."
  @count += 1
end

.snapshot(logfile, gc_runs = 3) ⇒ Object

Walk the live, instrumented objects on the heap and write them to logfile. Accepts an optional number of GC runs to perform before dumping the heap.



19
20
21
# File 'lib/bleak_house.rb', line 19

def self.snapshot(logfile, gc_runs = 3)
  ext_snapshot(logfile, gc_runs)
end

.write_to_log(message) ⇒ Object



10
11
12
# File 'ext/extconf.rb', line 10

def self.write_to_log(message)
  File.open(LOGFILE, 'a') { |f| f.puts message }
end