From bb3b2961c7bf80e14f28d794809b116f8bfc08ea Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Fri, 30 May 2014 16:50:44 -0500 Subject: [PATCH] Overly simple db generation -- takes much too long with much not being stored --- Gemfile | 5 ++ README.md | 3 + bin/alienist | 5 +- lib/alienist/model/java_field.rb | 5 +- lib/alienist/model/java_static.rb | 5 +- lib/alienist/parser.rb | 29 ++++----- lib/alienist/snapshot/sequel_snapshot.rb | 75 ++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 19 deletions(-) create mode 100644 Gemfile create mode 100644 lib/alienist/snapshot/sequel_snapshot.rb diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..1fa1942 --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +# -*- ruby -*- + +source "http://gems.github.com" +source "http://rubygems.org" +gem 'sequel' diff --git a/README.md b/README.md index 887c77c..e87696d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # Alienist - Java Heap Memory Analyzer +Random crap for funz: +select count() from instances where class_id = (select id from classes where name = "org.jruby.RubyString"); +6342 diff --git a/bin/alienist b/bin/alienist index 77f526f..e3b0925 100755 --- a/bin/alienist +++ b/bin/alienist @@ -3,6 +3,7 @@ require 'optparse' require 'alienist/parser' require 'alienist/reader' +require 'alienist/snapshot/sequel_snapshot' debug = 0 @@ -15,7 +16,9 @@ end opts.parse!(ARGV) File.open(ARGV.shift, "rb") do |io| - dump = Alienist::Parser.new Alienist::Reader.new(io, debug), debug + reader = Alienist::Reader.new(io, debug) + snapshot = Alienist::SequelSnapshot.new + dump = Alienist::Parser.new reader, snapshot, debug dump.parse # p dump end diff --git a/lib/alienist/model/java_field.rb b/lib/alienist/model/java_field.rb index 2a88d7c..54dad2a 100644 --- a/lib/alienist/model/java_field.rb +++ b/lib/alienist/model/java_field.rb @@ -1,6 +1,9 @@ module Alienist class JavaField - def initialize(*r) + attr_reader :id, :name, :type + + def initialize(id, name, type) + @id, @name, @type = id, name, type end end end diff --git a/lib/alienist/model/java_static.rb b/lib/alienist/model/java_static.rb index 58011ef..ea87148 100644 --- a/lib/alienist/model/java_static.rb +++ b/lib/alienist/model/java_static.rb @@ -1,6 +1,9 @@ module Alienist class JavaStatic - def initialize(*r) + attr_reader :field, :value + + def initialize(field, value) + @field, @value = field, value end end end diff --git a/lib/alienist/parser.rb b/lib/alienist/parser.rb index cef4eb5..181565f 100644 --- a/lib/alienist/parser.rb +++ b/lib/alienist/parser.rb @@ -23,8 +23,8 @@ class Parser GC_ROOT_NATIVE_STACK, GC_ROOT_STICKY_CLASS = 0x04, 0x05 GC_ROOT_THREAD_BLOCK, GC_ROOT_MONITOR_USED = 0x06, 0x07 - def initialize(io, debug=0) - @io, @debug = io, debug + def initialize(io, snapshot, debug=0) + @io, @snapshot, @debug = io, snapshot, debug @names = Hash.new { |hash, k| "unresolved class #{k}" } @names[0] = "" # For 0 id @class_name_from_id = {} @@ -99,7 +99,7 @@ def read_heap_dump(amount) case(type) when GC_ROOT_UNKNOWN then puts "Subloading GC_ROOT_UNKNOWN" if @debug > 0 - add_root @io.read_id, 0, UNKNOWN, "" + @snapshot.add_root @io.read_id, 0, UNKNOWN, "" when GC_ROOT_THREAD_OBJ then puts "Subloading GC_ROOT_THREAD_OBJ" if @debug > 0 id, thread_seq, stack_seq = @io.read_id, @io.read_int, @io.read_int @@ -107,7 +107,7 @@ def read_heap_dump(amount) when GC_ROOT_JNI_GLOBAL then puts "Subloading GC_ROOT_JNI_GLOBAL" if @debug > 0 id, _ = @io.read_id, @io.read_id # ignored global_ref_id - add_root @io.read_id, 0, NATIVE_STATIC, "" + @snapshot.add_root @io.read_id, 0, NATIVE_STATIC, "" when GC_ROOT_JNI_LOCAL then puts "Subloading GC_ROOT_JNI_LOCAL" if @debug > 0 id, thread_seq, depth = @io.read_id, @io.read_int, @io.read_int @@ -156,8 +156,8 @@ def load_class class_name_id = @io.read_id name = @names[class_name_id].gsub('/', '.') - @class_name_from_id[class_name_id] = name - puts "ID: #{class_name_id}, NAME: #{name}" if @debug > 3 + @class_name_from_id[class_id] = name + puts "ID: #{class_name_id}, NAME: #{name}"# if @debug > 3 @class_name_from_serial[class_id] = name end @@ -179,7 +179,10 @@ def read_primitive_array_dump def read_instance_dump read_section do |id, serial| class_id, bytes_following = @io.read_id, @io.read_int - @io.skip_bytes bytes_following, "instance_dump" # FIXME: Process + @io.skip_bytes bytes_following, "instance_dump" + # FIXME: Process Unclear if perhaps I should process greedily + # OR save offset and keep io open OR save blob in DB as part of this + @snapshot.add_instance(id, serial, class_id, bytes_following) end end @@ -195,7 +198,7 @@ def read_class_dump statics = read_static_fields(@io.read_unsigned_short) fields = read_fields(@io.read_unsigned_short) - add_class id, name, super_id, classloader_id, signers_id, + @snapshot.add_class id, name, super_id, classloader_id, signers_id, protection_domain_id, statics, fields, instance_size end end @@ -211,7 +214,7 @@ def read_static_fields(count) type, _ = signature_for type_id field_name = @names[name_id] value = read_value_for(type) - statics << JavaStatic.new(JavaField.new(field_name, type), value) + statics << JavaStatic.new(JavaField.new(name_id, field_name, type), value) end end end @@ -221,7 +224,7 @@ def read_fields(count) count.times do |i| # process all static fields name_id, type_id = @io.read_id, @io.read_byte type, _ = signature_for type_id - fields << JavaField.new(@names[name_id], type) + fields << JavaField.new(name_id, @names[name_id], type) end end end @@ -246,12 +249,6 @@ def skip_constant_pool_entries(count) end end - def add_class(*r) - end - - def add_root(*r) - end - def signature_for(id) return TYPES[id], TYPE_SIZES[id] end diff --git a/lib/alienist/snapshot/sequel_snapshot.rb b/lib/alienist/snapshot/sequel_snapshot.rb new file mode 100644 index 0000000..be7240b --- /dev/null +++ b/lib/alienist/snapshot/sequel_snapshot.rb @@ -0,0 +1,75 @@ +require 'sequel' + +module Alienist + class SequelSnapshot + def initialize(db_string="jdbc:sqlite:db") + @db = Sequel.connect(db_string) + create_schema + end + + def add_instance(id, serial, class_id, bytes_following) + @db[:instances].insert(id: id, class_id: class_id) + end + + def add_root(*r) + end + + def add_class(id, name, super_id, classloader_id, signers_id, + protection_domain_id, static_fields, fields, instance_size) + @db[:classes].insert(id: id, name: name, super_id: super_id, + classloader_id: classloader_id, + signers_id: signers_id, + protection_domain_id: protection_domain_id, + instance_size: instance_size) + + # static_fields.each do |field| + # @db[:static_fields].insert(id: field.field.id, class_id: id, + # name: field.field.name, + # type: field.field.type) + # end + + # fields.each do |field| + # @db[:fields].insert(id: field.id, class_id: id, + # name: field.name, type: field.type) + # end + end + + + def create_schema + @db.drop_table :classes if @db.table_exists?(:classes) + @db.drop_table :fields if @db.table_exists?(:fields) + @db.drop_table :static_fields if @db.table_exists?(:static_fields) + @db.drop_table :instances if @db.table_exists?(:instances) + + @db.create_table(:instances) do + primary_key :id + Int :class_id, null: false + end + + @db.create_table(:fields) do + primary_key :id + Int :class_id, null: false + String :name, null: false + String :type, null: false + end + + @db.create_table(:static_fields) do + primary_key :id + Int :class_id, null: false + String :name, null: false + String :type, null: false + end + # FIXME: need to store value and deal with heap, object_ref, and value + + @db.create_table(:classes) do + primary_key :id + String :name, null: false + Int :super_id, null: false + Int :classloader_id, null: false + Int :signers_id, null: false + Int :protection_domain_id, null: false + Int :instance_size, null: false + end + end + end +end