memdump 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bb8daaffee80155c4227920118ed3793e3afe4ab
4
+ data.tar.gz: b57f30d96b9e35d8519d800cbca852ef6995d965
5
+ SHA512:
6
+ metadata.gz: 84b9700c70b49d35b7c7910e1a047d4f1fd9aba7161888090bc3190e6e983a835b9417363376c3eabe648763eb537e57d89726440f9b8ac022b0920be60bd2fd
7
+ data.tar.gz: 206a0adbfd4c9f3b8d10f21c0ad07f911384977f79dee4a22e01af9c3710ab6e430e6f4ade72a37655e24eef2137025cec050dce232d2dacb0db75d1ae8f86e3
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in memdump.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Sylvain Joyeux
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,123 @@
1
+ # Memdump
2
+
3
+ Memdump is a set of (basic) tools to create and manipulate Ruby object dumps.
4
+
5
+ Since Ruby 2.1, ObjectSpace can be dumped in a JSON file that represents all
6
+ allocated objects and their relationships. It is a gold mine of information if
7
+ you want to understand why your application has that many objects and/or a
8
+ memory leak.
9
+
10
+ Processing methods are available as a library, or using the `memdump`
11
+ command-line tool. Just run `memdump help` for a summary of operations.
12
+
13
+ **NOTE** running memdump under jruby really reduces processing times... If you're using rbenv, just do
14
+
15
+ ```
16
+ rbenv shell jruby-9.0.5.0
17
+ ```
18
+
19
+ in the shell where you run the memdump commands.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'memdump'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ $ bundle
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install memdump
36
+
37
+
38
+ ## Creating a memory dump
39
+
40
+ ### Using rbtrace
41
+
42
+ The memdump command-line tool can connect to a process where
43
+ the [excellent rbtrace](https://github.com/tmm1/rbtrace) has been required. Just
44
+ start your Ruby application with `-r rbtrace`, e.g.
45
+
46
+ ```
47
+ ruby -rrbtrace -S syskit run
48
+ ```
49
+
50
+ and find out the process PID using e.g. top or ps (in the following, I assume
51
+ that the PID is 1234)
52
+
53
+ Memory dumps are then created with
54
+
55
+ ```
56
+ memdump dump 1234 /tmp/mydump
57
+ ```
58
+
59
+ Since `dump_all` requires very long, the rbtrace client will return before the
60
+ end of the dump with `*** timed out waiting for eval response`. Check your
61
+ application's output for a line saying `sendto(14): No such file or directory
62
+ [detaching]`
63
+
64
+ Additionally, you might want to enable allocation tracing, which adds to the
65
+ dump the line/file of the point where the object got allocated but is also very
66
+ costly from a performance point of view, do
67
+
68
+ ```
69
+ memdump enable-allocation-trace 1234
70
+ ```
71
+
72
+ ### Manually
73
+
74
+ It is sometimes more beneficial to do the dumps in specific places
75
+ in your application, something the rbtrace method does not allow you to do. In
76
+ this case, create memory dumps by calling `ObjectSpace.dump_all`
77
+
78
+ ~~~ ruby
79
+ require 'objspace'
80
+ File.open('/path/to/dump/file', 'w') do |io|
81
+ ObjectSpace.dump_all(output: io)
82
+ end
83
+ ~~~
84
+
85
+ Allocation tracing is enabled with
86
+
87
+ ~~~ ruby
88
+ require 'objspace'
89
+ ObjectSpace.trace_objects_allocation_start
90
+ ~~~
91
+
92
+ ## Analyzing the dump
93
+
94
+ The first thing you will probably want to do is to run the replace-class command
95
+ on the dump. It replaces the class attribute, which in the original dump is the
96
+ reference to the class object, by the class name. This makes reading the dump a
97
+ lot easier.
98
+
99
+ ```
100
+ memdump replace-class /tmp/mydump
101
+ ```
102
+
103
+ The most basic analysis is done by running **stats**, which outputs the object
104
+ count by class. For memory leaks, the **diff** command allows you to output the
105
+ part of the graph that involves new objects (removing the
106
+ "old-and-not-referred-to-by-new")
107
+
108
+ Beyond that, I usually go back and forth between the memory dump and
109
+ [gephi](http://gephi.org), a graph analysis application. the **gml** command
110
+ allows to convert the memory dump into a graph format that gephi can import.
111
+ From there, use gephi's layouting and filtering algorithms to get an idea of the
112
+ most likely objects. Then, you can "massage" the dump using the **root_of**,
113
+ **subgraph_of** and **remove-node** commands to narrow the dump to its most useful
114
+ parts.
115
+
116
+ ## Contributing
117
+
118
+ Bug reports and pull requests are welcome on GitHub at https://github.com/doudou/memdump.
119
+
120
+ ## License
121
+
122
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
123
+
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,2 @@
1
+ require 'memdump/cli'
2
+ MemDump::CLI.start(ARGV)
@@ -0,0 +1,5 @@
1
+ require "memdump/version"
2
+
3
+ module Memdump
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,19 @@
1
+ module MemDump
2
+ def self.cleanup_references(dump)
3
+ addresses = Set.new
4
+ records = Array.new
5
+ dump.each_record do |r|
6
+ addr = (r['address'] || r['root'])
7
+ addresses << addr
8
+ records << r
9
+ end
10
+
11
+ records.each do |r|
12
+ if references = r['references']
13
+ references.delete_if { |r| !addresses.include?(r) }
14
+ end
15
+ end
16
+ records
17
+ end
18
+ end
19
+
@@ -0,0 +1,134 @@
1
+ require 'thor'
2
+ require 'pathname'
3
+ require 'memdump/json_dump'
4
+
5
+ module MemDump
6
+ class CLI < Thor
7
+ desc 'enable-allocation-trace PID', 'enable the tracing of allocations on a running process'
8
+ def enable_allocation_trace(pid)
9
+ system('rbtrace', '-p', pid.to_s, '-e', "require \"objspace\"; ObjectSpace.trace_object_allocations_start")
10
+ end
11
+
12
+ desc 'dump PID FILE', 'generate a dump file from a running process'
13
+ def dump(pid, file)
14
+ file = File.expand_path(file)
15
+ system('rbtrace', '-p', pid.to_s, '-e', "require \"objspace\"; File.open(\"#{file}\", 'w') { |io| ObjectSpace.dump_all(output: io) }")
16
+ end
17
+
18
+ desc 'diff SOURCE TARGET OUTPUT', 'generate a memory dump that contains the objects in TARGET not in SOURCE, and all their parents'
19
+ def diff(source, target, output)
20
+ require 'memdump/diff'
21
+
22
+ STDOUT.sync = true
23
+ from = MemDump::JSONDump.new(Pathname.new(source))
24
+ to = MemDump::JSONDump.new(Pathname.new(target))
25
+ records = MemDump.diff(from, to)
26
+ File.open(output, 'w') do |io|
27
+ records.each do |r|
28
+ io.puts JSON.dump(r)
29
+ end
30
+ end
31
+ end
32
+
33
+ desc 'gml DUMP GML', 'converts a memory dump into a graph in the GML format (for processing by e.g. gephi)'
34
+ def gml(dump_path, gml_path = nil)
35
+ require 'memdump/convert_to_gml'
36
+
37
+ STDOUT.sync = true
38
+ dump_path = Pathname.new(dump_path)
39
+ if gml_path
40
+ gml_path = Pathname.new(gml_path)
41
+ else
42
+ gml_path = dump_path.sub_ext('.gml')
43
+ end
44
+
45
+ dump = MemDump::JSONDump.new(dump_path)
46
+ gml_path.open('w') do |io|
47
+ MemDump.convert_to_gml(dump, io)
48
+ end
49
+ end
50
+
51
+ desc "subgraph_of DUMP ADDRESS", "traces all objects that are reachable from the given object"
52
+ option :max_depth, desc: 'depth of the subgraph to generate', type: :numeric, default: Float::INFINITY
53
+ def subgraph_of(dump, address)
54
+ require 'memdump/subgraph_of'
55
+
56
+ STDOUT.sync = true
57
+ dump = MemDump::JSONDump.new(Pathname.new(dump))
58
+ MemDump.subgraph_of(dump, address, max_depth: options[:max_depth]).each do |r|
59
+ puts JSON.dump(r)
60
+ end
61
+ end
62
+
63
+ desc "root_of DUMP ADDRESS", "traces the object with the given address to the root that's holding it alive"
64
+ def root_of(dump, address)
65
+ require 'memdump/root_of'
66
+
67
+ STDOUT.sync = true
68
+ dump = MemDump::JSONDump.new(Pathname.new(dump))
69
+ MemDump.root_of(dump, address).each do |r|
70
+ puts JSON.dump(r)
71
+ end
72
+ end
73
+
74
+ desc 'replace-class DUMP OUTPUT', 'replaces the class address by the class name'
75
+ option :add_ref, desc: 'whether a reference to the class object should be added', type: :boolean, default: false
76
+ def replace_class(dump_path, output_path = nil)
77
+ require 'memdump/replace_class_address_by_name'
78
+
79
+ STDOUT.sync = true
80
+ dump_path = Pathname.new(dump_path)
81
+ output_path =
82
+ if output_path then Pathname.new(output_path)
83
+ else dump_path
84
+ end
85
+ dump = MemDump::JSONDump.new(dump_path)
86
+ result = MemDump.replace_class_address_by_name(dump, add_reference_to_class: options[:add_ref])
87
+ output_path.open('w') do |io|
88
+ result.each do |r|
89
+ io.puts JSON.dump(r)
90
+ end
91
+ end
92
+ end
93
+
94
+ desc 'cleanup-refs DUMP OUTPUT', "removes references to deleted objects"
95
+ def cleanup_refs(dump, output)
96
+ require 'memdump/cleanup_references'
97
+
98
+ STDOUT.sync = true
99
+ dump = MemDump::JSONDump.new(Pathname.new(dump))
100
+ cleaned = MemDump.cleanup_references(dump)
101
+ Pathname.new(output).open('w') do |io|
102
+ cleaned.each do |r|
103
+ io.puts JSON.dump(r)
104
+ end
105
+ end
106
+ end
107
+
108
+ desc 'remove-node DUMP NODE', 'remove all objects that are kept alive by the given node'
109
+ def remove_node(dump, node_id)
110
+ require 'memdump/remove_node'
111
+
112
+ STDOUT.sync = true
113
+ dump = MemDump::JSONDump.new(Pathname.new(dump))
114
+ cleaned = MemDump.remove_node(dump, node_id)
115
+ cleaned.each do |r|
116
+ STDOUT.puts JSON.dump(r)
117
+ end
118
+ end
119
+
120
+ desc 'stats DUMP', 'give statistics on the objects present in the dump'
121
+ def stats(dump)
122
+ require 'pp'
123
+ require 'memdump/stats'
124
+ dump = MemDump::JSONDump.new(Pathname.new(dump))
125
+ unknown, by_type = MemDump.stats(dump)
126
+ puts "#{unknown} objects without a known type"
127
+ by_type.sort_by { |n, v| v }.reverse.each do |n, v|
128
+ puts "#{n}: #{v}"
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+
@@ -0,0 +1,47 @@
1
+ require 'set'
2
+
3
+ module MemDump
4
+ def self.convert_to_gml(dump, io)
5
+ nodes = dump.each_record.map do |row|
6
+ if row['class_address'] # transformed with replace_class_address_by_name
7
+ name = row['class']
8
+ else
9
+ name = row['struct'] || row['root'] || row['type']
10
+ end
11
+
12
+ address = row['address'] || row['root']
13
+ refs = Hash.new
14
+ if row_refs = row['references']
15
+ row_refs.each { |r| refs[r] = nil }
16
+ end
17
+
18
+ [address, refs, name]
19
+ end
20
+
21
+ io.puts "graph"
22
+ io.puts "["
23
+ known_addresses = Set.new
24
+ nodes.each do |address, refs, name|
25
+ known_addresses << address
26
+ io.puts " node"
27
+ io.puts " ["
28
+ io.puts " id #{address}"
29
+ io.puts " label \"#{name}\""
30
+ io.puts " ]"
31
+ end
32
+
33
+ nodes.each do |address, refs, _|
34
+ refs.each do |ref_address, ref_label|
35
+ io.puts " edge"
36
+ io.puts " ["
37
+ io.puts " source #{address}"
38
+ io.puts " target #{ref_address}"
39
+ if ref_label
40
+ io.puts " label \"#{ref_label}\""
41
+ end
42
+ io.puts " ]"
43
+ end
44
+ end
45
+ io.puts "]"
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ require 'set'
2
+
3
+ module MemDump
4
+ def self.diff(from, to)
5
+ from_objects = Set.new
6
+ from.each_record { |r| from_objects << (r['address'] || r['root']) }
7
+ puts "#{from_objects.size} objects found in source dump"
8
+
9
+ selected_records = Hash.new
10
+ remaining_records = Array.new
11
+ to.each_record do |r|
12
+ address = (r['address'] || r['root'])
13
+ if !from_objects.include?(address)
14
+ selected_records[address] = r
15
+ r['only_in_target'] = 1
16
+ else
17
+ remaining_records << r
18
+ end
19
+ end
20
+
21
+ total = remaining_records.size + selected_records.size
22
+ count = 0
23
+ while selected_records.size != count
24
+ count = selected_records.size
25
+ puts "#{count}/#{total} records selected so far"
26
+ remaining_records.delete_if do |r|
27
+ address = (r['address'] || r['root'])
28
+ references = r['references']
29
+
30
+ if references && references.any? { |r| selected_records.has_key?(r) }
31
+ selected_records[address] = r
32
+ end
33
+ end
34
+ end
35
+ puts "#{count}/#{total} records selected"
36
+
37
+ selected_records.each_value do |r|
38
+ if references = r['references']
39
+ references.delete_if { |a| !selected_records.has_key?(a) }
40
+ end
41
+ end
42
+ selected_records.each_value
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ require 'json'
2
+ module MemDump
3
+ class JSONDump
4
+ def initialize(filename)
5
+ @filename = filename
6
+ end
7
+
8
+ def each_record
9
+ return enum_for(__method__) if !block_given?
10
+
11
+ if @cached_entries
12
+ @cached_entries.each(&proc)
13
+ else
14
+ @filename.open do |f|
15
+ f.each_line do |line|
16
+ yield JSON.parse(line)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,43 @@
1
+ module MemDump
2
+ def self.remove_node(dump, removed_node)
3
+ remaining_records = Hash.new
4
+ non_roots = Set.new
5
+ dump.each_record do |r|
6
+ address = (r['address'] || r['root'])
7
+ remaining_records[address] = r
8
+
9
+ if refs = r['references']
10
+ refs.each do |ref_address|
11
+ non_roots << ref_address
12
+ end
13
+ end
14
+ end
15
+
16
+ roots = remaining_records.each_key.
17
+ find_all { |a| !non_roots.include?(a) }
18
+
19
+ queue = roots.dup
20
+ selected_records = Hash.new
21
+ while !queue.empty?
22
+ address = queue.shift
23
+ next if address == removed_node
24
+
25
+ if record = remaining_records.delete(address)
26
+ selected_records[address] = record
27
+ if refs = record['references']
28
+ refs.each do |ref_address|
29
+ queue << ref_address
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ selected_records.values.reverse.map do |r|
36
+ if refs = r['references']
37
+ refs.delete_if { |a| !selected_records.has_key?(a) }
38
+ end
39
+ r
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,25 @@
1
+ module MemDump
2
+ # Replace the address in the 'class' attribute by the class name
3
+ def self.replace_class_address_by_name(dump, add_reference_to_class: false)
4
+ class_names = Hash.new
5
+ dump.each_record do |row|
6
+ if row['type'] == 'CLASS' || row['type'] == 'MODULE'
7
+ class_names[row['address']] = row['name']
8
+ end
9
+ end
10
+
11
+ dump.each_record.map do |r|
12
+ if klass = r['class']
13
+ r['class'] = class_names[klass] || klass
14
+ r['class_address'] = klass
15
+ if add_reference_to_class
16
+ (r['references'] ||= Array.new) << klass
17
+ end
18
+ end
19
+ if r['type'] == 'ICLASS'
20
+ r['class'] = "I(#{r['class']})"
21
+ end
22
+ r
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module MemDump
2
+ def self.root_of(dump, root_address)
3
+ remaining_records = Array.new
4
+ selected_records = Hash.new
5
+ selected_root = root_address
6
+ dump.each_record do |r|
7
+ address = (r['address'] || r['root'])
8
+ if selected_root == address
9
+ selected_records[address] = r
10
+ selected_root = nil;
11
+ else
12
+ remaining_records << r
13
+ end
14
+ end
15
+
16
+ count = 0
17
+ while count != selected_records.size
18
+ count = selected_records.size
19
+ remaining_records.delete_if do |r|
20
+ references = r['references']
21
+ if references && references.any? { |a| selected_records.has_key?(a) }
22
+ address = (r['address'] || r['root'])
23
+ selected_records[address] = r
24
+ end
25
+ end
26
+ end
27
+
28
+ selected_records.values.reverse.each do |r|
29
+ if refs = r['references']
30
+ refs.delete_if { |a| !selected_records.has_key?(a) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,15 @@
1
+ module MemDump
2
+ def self.stats(memdump)
3
+ unknown_class = 0
4
+ by_class = Hash.new(0)
5
+ memdump.each_record do |r|
6
+ if klass = (r['class'] || r['type'] || r['root'])
7
+ by_class[klass] += 1
8
+ else
9
+ unknown_class += 1
10
+ end
11
+ end
12
+ return unknown_class, by_class
13
+ end
14
+ end
15
+
@@ -0,0 +1,26 @@
1
+ module MemDump
2
+ def self.subgraph_of(dump, root_address, max_depth: Float::INFINITY)
3
+ remaining_records = Hash.new
4
+ dump.each_record do |r|
5
+ address = (r['address'] || r['root'])
6
+ remaining_records[address] = r
7
+ end
8
+
9
+ selected_records = Hash.new
10
+ queue = [[root_address, 0]]
11
+ while !queue.empty?
12
+ address, depth = queue.shift
13
+ if record = remaining_records.delete(address)
14
+ selected_records[address] = record
15
+ if (depth < max_depth) && (refs = record['references'])
16
+ refs.each do |ref_address|
17
+ queue << [ref_address, depth + 1]
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ selected_records.values
24
+ end
25
+ end
26
+
@@ -0,0 +1,3 @@
1
+ module Memdump
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'memdump/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "memdump"
8
+ spec.version = Memdump::VERSION
9
+ spec.authors = ["Sylvain Joyeux"]
10
+ spec.email = ["sylvain.joyeux@m4x.org"]
11
+
12
+ spec.summary = %q{Tools to manipulate Ruby 2.1+ memory dumps}
13
+ spec.description = %q{}
14
+ spec.homepage = "https://github.com/doudou/memdump"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "bin"
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'thor'
23
+ spec.add_dependency 'rbtrace'
24
+ spec.add_development_dependency "bundler", "~> 1.11"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest", "~> 5.0"
27
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memdump
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sylvain Joyeux
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rbtrace
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ description: ''
84
+ email:
85
+ - sylvain.joyeux@m4x.org
86
+ executables:
87
+ - memdump
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/memdump
98
+ - lib/memdump.rb
99
+ - lib/memdump/cleanup_references.rb
100
+ - lib/memdump/cli.rb
101
+ - lib/memdump/convert_to_gml.rb
102
+ - lib/memdump/diff.rb
103
+ - lib/memdump/json_dump.rb
104
+ - lib/memdump/remove_node.rb
105
+ - lib/memdump/replace_class_address_by_name.rb
106
+ - lib/memdump/root_of.rb
107
+ - lib/memdump/stats.rb
108
+ - lib/memdump/subgraph_of.rb
109
+ - lib/memdump/version.rb
110
+ - memdump.gemspec
111
+ homepage: https://github.com/doudou/memdump
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.2.3
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Tools to manipulate Ruby 2.1+ memory dumps
135
+ test_files: []
136
+ has_rdoc: