allocation_stats 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ce3ad497b9d8343b112d199d186f508a22e68515
4
+ data.tar.gz: 1f323cfd6854e10f26b66eb8f592cb8b577a88a7
5
+ SHA512:
6
+ metadata.gz: 55b43fd195b3540b872af0c05e5a4b10d3f94fce8678dc00b7b7325d6eebb7b12e688caf858a8f9424d2e6bcc98347385a4807e840c3c95fc4759f501dfde9ec
7
+ data.tar.gz: 946cd053f108b4f6548b5991fa37d620cf2d5ae3df176a4a5cd8c9ec0a4a52ca6036f8abd67e28f3868dbf36ac6dd658c018ace0d1da761814ed1cbbde35aa4e
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ doc
2
+ .yardoc
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ -m markdown - README.markdown LICENSE
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ # Licensed under the Apache License, Version 2.0, found in the LICENSE file.
3
+
4
+ source "https://rubygems.org"
5
+
6
+ group :development do
7
+ gem "yard"
8
+ end
9
+
10
+ group :test do
11
+ gem "rspec"
12
+ gem "pry"
13
+
14
+ gem "yajl-ruby", "= 1.1.0"
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ coderay (1.0.9)
5
+ diff-lcs (1.2.4)
6
+ method_source (0.8.2)
7
+ pry (0.9.12.2)
8
+ coderay (~> 1.0.5)
9
+ method_source (~> 0.8)
10
+ slop (~> 3.4)
11
+ rspec (2.13.0)
12
+ rspec-core (~> 2.13.0)
13
+ rspec-expectations (~> 2.13.0)
14
+ rspec-mocks (~> 2.13.0)
15
+ rspec-core (2.13.1)
16
+ rspec-expectations (2.13.0)
17
+ diff-lcs (>= 1.1.3, < 2.0)
18
+ rspec-mocks (2.13.1)
19
+ slop (3.4.6)
20
+ yajl-ruby (1.1.0)
21
+ yard (0.8.7)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ pry
28
+ rspec
29
+ yajl-ruby (= 1.1.0)
30
+ yard
data/LICENSE ADDED
@@ -0,0 +1,196 @@
1
+ # @markup rdoc
2
+
3
+ Apache License
4
+ Version 2.0, January 2004
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction, and
12
+ distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright
15
+ owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all other entities
18
+ that control, are controlled by, or are under common control with that entity.
19
+ For the purposes of this definition, "control" means (i) the power, direct or
20
+ indirect, to cause the direction or management of such entity, whether by
21
+ contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
25
+ permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications, including
28
+ but not limited to software source code, documentation source, and configuration
29
+ files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical transformation or
32
+ translation of a Source form, including but not limited to compiled object code,
33
+ generated documentation, and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or Object form, made
36
+ available under the License, as indicated by a copyright notice that is included
37
+ in or attached to the work (an example is provided in the Appendix below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object form, that
40
+ is based on (or derived from) the Work and for which the editorial revisions,
41
+ annotations, elaborations, or other modifications represent, as a whole, an
42
+ original work of authorship. For the purposes of this License, Derivative Works
43
+ shall not include works that remain separable from, or merely link (or bind by
44
+ name) to the interfaces of, the Work and Derivative Works thereof.
45
+
46
+ "Contribution" shall mean any work of authorship, including the original version
47
+ of the Work and any modifications or additions to that Work or Derivative Works
48
+ thereof, that is intentionally submitted to Licensor for inclusion in the Work
49
+ by the copyright owner or by an individual or Legal Entity authorized to submit
50
+ on behalf of the copyright owner. For the purposes of this definition,
51
+ "submitted" means any form of electronic, verbal, or written communication sent
52
+ to the Licensor or its representatives, including but not limited to
53
+ communication on electronic mailing lists, source code control systems, and
54
+ issue tracking systems that are managed by, or on behalf of, the Licensor for
55
+ the purpose of discussing and improving the Work, but excluding communication
56
+ that is conspicuously marked or otherwise designated in writing by the copyright
57
+ owner as "Not a Contribution."
58
+
59
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
60
+ of whom a Contribution has been received by Licensor and subsequently
61
+ incorporated within the Work.
62
+
63
+ 2. Grant of Copyright License.
64
+
65
+ Subject to the terms and conditions of this License, each Contributor hereby
66
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
67
+ irrevocable copyright license to reproduce, prepare Derivative Works of,
68
+ publicly display, publicly perform, sublicense, and distribute the Work and such
69
+ Derivative Works in Source or Object form.
70
+
71
+ 3. Grant of Patent License.
72
+
73
+ Subject to the terms and conditions of this License, each Contributor hereby
74
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
75
+ irrevocable (except as stated in this section) patent license to make, have
76
+ made, use, offer to sell, sell, import, and otherwise transfer the Work, where
77
+ such license applies only to those patent claims licensable by such Contributor
78
+ that are necessarily infringed by their Contribution(s) alone or by combination
79
+ of their Contribution(s) with the Work to which such Contribution(s) was
80
+ submitted. If You institute patent litigation against any entity (including a
81
+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82
+ Contribution incorporated within the Work constitutes direct or contributory
83
+ patent infringement, then any patent licenses granted to You under this License
84
+ for that Work shall terminate as of the date such litigation is filed.
85
+
86
+ 4. Redistribution.
87
+
88
+ You may reproduce and distribute copies of the Work or Derivative Works thereof
89
+ in any medium, with or without modifications, and in Source or Object form,
90
+ provided that You meet the following conditions:
91
+
92
+ You must give any other recipients of the Work or Derivative Works a copy of
93
+ this License; and
94
+ You must cause any modified files to carry prominent notices stating that You
95
+ changed the files; and
96
+ You must retain, in the Source form of any Derivative Works that You distribute,
97
+ all copyright, patent, trademark, and attribution notices from the Source form
98
+ of the Work, excluding those notices that do not pertain to any part of the
99
+ Derivative Works; and
100
+ If the Work includes a "NOTICE" text file as part of its distribution, then any
101
+ Derivative Works that You distribute must include a readable copy of the
102
+ attribution notices contained within such NOTICE file, excluding those notices
103
+ that do not pertain to any part of the Derivative Works, in at least one of the
104
+ following places: within a NOTICE text file distributed as part of the
105
+ Derivative Works; within the Source form or documentation, if provided along
106
+ with the Derivative Works; or, within a display generated by the Derivative
107
+ Works, if and wherever such third-party notices normally appear. The contents of
108
+ the NOTICE file are for informational purposes only and do not modify the
109
+ License. You may add Your own attribution notices within Derivative Works that
110
+ You distribute, alongside or as an addendum to the NOTICE text from the Work,
111
+ provided that such additional attribution notices cannot be construed as
112
+ modifying the License.
113
+ You may add Your own copyright statement to Your modifications and may provide
114
+ additional or different license terms and conditions for use, reproduction, or
115
+ distribution of Your modifications, or for any such Derivative Works as a whole,
116
+ provided Your use, reproduction, and distribution of the Work otherwise complies
117
+ with the conditions stated in this License.
118
+
119
+ 5. Submission of Contributions.
120
+
121
+ Unless You explicitly state otherwise, any Contribution intentionally submitted
122
+ for inclusion in the Work by You to the Licensor shall be under the terms and
123
+ conditions of this License, without any additional terms or conditions.
124
+ Notwithstanding the above, nothing herein shall supersede or modify the terms of
125
+ any separate license agreement you may have executed with Licensor regarding
126
+ such Contributions.
127
+
128
+ 6. Trademarks.
129
+
130
+ This License does not grant permission to use the trade names, trademarks,
131
+ service marks, or product names of the Licensor, except as required for
132
+ reasonable and customary use in describing the origin of the Work and
133
+ reproducing the content of the NOTICE file.
134
+
135
+ 7. Disclaimer of Warranty.
136
+
137
+ Unless required by applicable law or agreed to in writing, Licensor provides the
138
+ Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
139
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
140
+ including, without limitation, any warranties or conditions of TITLE,
141
+ NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
142
+ solely responsible for determining the appropriateness of using or
143
+ redistributing the Work and assume any risks associated with Your exercise of
144
+ permissions under this License.
145
+
146
+ 8. Limitation of Liability.
147
+
148
+ In no event and under no legal theory, whether in tort (including negligence),
149
+ contract, or otherwise, unless required by applicable law (such as deliberate
150
+ and grossly negligent acts) or agreed to in writing, shall any Contributor be
151
+ liable to You for damages, including any direct, indirect, special, incidental,
152
+ or consequential damages of any character arising as a result of this License or
153
+ out of the use or inability to use the Work (including but not limited to
154
+ damages for loss of goodwill, work stoppage, computer failure or malfunction, or
155
+ any and all other commercial damages or losses), even if such Contributor has
156
+ been advised of the possibility of such damages.
157
+
158
+ 9. Accepting Warranty or Additional Liability.
159
+
160
+ While redistributing the Work or Derivative Works thereof, You may choose to
161
+ offer, and charge a fee for, acceptance of support, warranty, indemnity, or
162
+ other liability obligations and/or rights consistent with this License. However,
163
+ in accepting such obligations, You may act only on Your own behalf and on Your
164
+ sole responsibility, not on behalf of any other Contributor, and only if You
165
+ agree to indemnify, defend, and hold each Contributor harmless for any liability
166
+ incurred by, or claims asserted against, such Contributor by reason of your
167
+ accepting any such warranty or additional liability.
168
+
169
+ END OF TERMS AND CONDITIONS
170
+
171
+ APPENDIX: How to apply the Apache License to your work
172
+
173
+ To apply the Apache License to your work, attach the following boilerplate
174
+ notice, with the fields enclosed by brackets "{}" replaced with your own
175
+ identifying information. (Don't include the brackets!) The text should be
176
+ enclosed in the appropriate comment syntax for the file format. We also
177
+ recommend that a file or class name and description of purpose be included on
178
+ the same "printed page" as the copyright notice for easier identification within
179
+ third-party archives.
180
+
181
+ Copyright {yyyy} {name of copyright owner}
182
+
183
+ Licensed under the Apache License, Version 2.0 (the "License");
184
+ you may not use this file except in compliance with the License.
185
+ You may obtain a copy of the License at
186
+
187
+ http://www.apache.org/licenses/LICENSE-2.0
188
+
189
+ Unless required by applicable law or agreed to in writing, software
190
+ distributed under the License is distributed on an "AS IS" BASIS,
191
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
192
+ See the License for the specific language governing permissions and
193
+ limitations under the License.
194
+
195
+
196
+
data/README.markdown ADDED
@@ -0,0 +1,377 @@
1
+ Introduction
2
+ ============
3
+
4
+ AllocationStats is a rubygem that makes use of Ruby 2.1's new abilities to trace
5
+ Ruby object allocations (MRI only). Ruby 2.1's new
6
+ `ObjectSpace.trace_object_allocations` methods give only raw information, and
7
+ are not immediately useful for anything beyond micro profiling. The data must be
8
+ aggregated!
9
+
10
+ AllocationStats collects all of the allocation information generated by
11
+ `ObjectSpace.trace_object_allocations`, then provides mechanisms for filtering,
12
+ grouping, and sorting the allocation information.
13
+
14
+ Tabular Output examples
15
+ -----------------------
16
+
17
+ It is very easy to get some simple statistics out of AllocationStats.
18
+ Wrap some code with `AllocationStats.trace` and print out a listing of all of the
19
+ new object allocation information.
20
+
21
+ As an example, lets look at `examples/my_class.rb`:
22
+
23
+ ```ruby
24
+ class MyClass
25
+ def my_method
26
+ @hash = {1 => "foo", 2 => "bar"}
27
+ end
28
+ end
29
+ ```
30
+
31
+ And use that class in a bit of ad-hoc Ruby:
32
+
33
+ ```
34
+ $ ruby -r ./lib/allocation_stats -r ./allocation
35
+ stats = AllocationStats.trace { MyClass.new.my_method }
36
+ puts stats.allocations(alias_paths: true).to_text
37
+ ^D
38
+ sourcefile sourceline class_path method_id memsize class
39
+ ------------------- ---------- ---------- --------- ------- -------
40
+ <PWD>/allocation.rb 4 MyClass my_method 0 String
41
+ <PWD>/allocation.rb 3 MyClass my_method 192 Hash
42
+ <PWD>/allocation.rb 3 MyClass my_method 0 String
43
+ <PWD>/allocation.rb 3 MyClass my_method 0 String
44
+ <PWD>/allocation.rb 3 MyClass my_method 0 String
45
+ - 1 Class new 0 MyClass
46
+ ```
47
+
48
+ (I've used `alias_paths: true` above for readability. This way, the `sourcefile`
49
+ column is not crazy long, using the full file path on my filesystem.)
50
+
51
+ (This full example is found in `examples/trace_my_class_raw.rb`)
52
+
53
+ We can also group allocations by one or more attributes, and get an aggregate
54
+ count. Below, we group allocations by source file, source line, and class:
55
+
56
+ ```
57
+ $ ruby -r ./lib/allocation_stats -r ./allocation
58
+ stats = AllocationStats.trace { MyClass.new.my_method }
59
+ puts stats.allocations(alias_paths: true).group_by(:sourcefile, :sourceline, :class).to_text
60
+ ^D
61
+ sourcefile sourceline class count
62
+ ------------------- ---------- ------- -----
63
+ <PWD>/allocation.rb 4 String 1
64
+ <PWD>/allocation.rb 3 Hash 1
65
+ <PWD>/allocation.rb 3 String 3
66
+ - 1 MyClass 1
67
+ ```
68
+
69
+ (This full example is found in `examples/trace_my_class_group_by.rb`)
70
+
71
+
72
+ More on `trace_object_allocations()`
73
+ ---------------------------------------------
74
+
75
+ To start at the beginning: Ruby 2.1 will be released with a new feature that
76
+ enables one to trace object allocations. Through
77
+ `ObjectSpace.trace_object_allocations`, one can trace the following properties
78
+ for any new object allocation:
79
+
80
+ * source file
81
+ * source line
82
+ * class path
83
+ * method ID
84
+ * generation
85
+
86
+ Let's see this in action:
87
+
88
+ ```
89
+ $ cat examples/trace_object_allocations.rb
90
+ require 'objspace'
91
+
92
+ ObjectSpace.trace_object_allocations do
93
+ a = [2,3,5,7,11,13,17,19,23,29,31]
94
+ puts ObjectSpace.allocation_sourcefile(a)
95
+ puts ObjectSpace.allocation_sourceline(a)
96
+ end
97
+ $ ruby ./examples/trace_object_allocations.rb
98
+ allocations.rb
99
+ 4
100
+ ```
101
+
102
+ Example from the specs
103
+ ----------------------
104
+
105
+ ```ruby
106
+ existing_array = [1,2,3,4,5]
107
+
108
+ stats = AllocationStats.trace do
109
+ new_string = "stringy string"
110
+ another_string = "another string"
111
+ an_array = [1,1,2,3,5,8,13,21,34,55]
112
+ a_foreign_string = allocate_a_string_from_spec_helper
113
+ end
114
+
115
+ results = stats.allocations.group_by(:@sourcefile, :class).to_a
116
+ ```
117
+
118
+ We've grouped all of the traced allocations by the **source file** that the
119
+ allocation occurred in, and the **class of the object** that was allocated. The
120
+ list of allocations can be "transformed" in a number of ways (including
121
+ the above `#group_by`), so the transformations must be ultimately resolved by calling
122
+ `#to_a` (similar to ActiveRecord relations). The result is the following Hash of (sourcefile, class) tuple keys and
123
+ AllocationStats::Allocation values:
124
+
125
+ ```ruby
126
+ {
127
+ [".../spec/spec_helper.rb", String]
128
+ =>
129
+ [#<AllocationStats::Allocation:0x007f132ac3f160
130
+ @object="a string from spec_helper",
131
+ @memsize=0,
132
+ @sourcefile=".../spec/spec_helper.rb",
133
+ @sourceline=14,
134
+ @class_path="Object",
135
+ @method_id=:allocate_a_string_from_spec_helper>
136
+ ],
137
+
138
+ [".../spec/allocation_stats_spec.rb", Array]
139
+ =>
140
+ [#<AllocationStats::Allocation:0x007f132ac3e968
141
+ @object=[1, 1, 2, 3, 5, 8, 13, 21, 34, 55],
142
+ @memsize=80,
143
+ @sourcefile=".../spec/allocation_stats_spec.rb",
144
+ @sourceline=78,
145
+ @class_path=nil,
146
+ @method_id=nil>
147
+ ],
148
+
149
+ [".../spec/allocation_stats_spec.rb", String]
150
+ =>
151
+ [ #<AllocationStats::Allocation:0x007f132ac3e0d0
152
+ @object="another string",
153
+ @memsize=0,
154
+ @sourcefile=".../spec/allocation_stats_spec.rb",
155
+ @sourceline=77,
156
+ @class_path=nil,
157
+ @method_id=nil>,
158
+ #<AllocationStats::Allocation:0x007f132ac3d838
159
+ @object="stringy string",
160
+ @memsize=0,
161
+ @sourcefile=".../spec/allocation_stats_spec.rb",
162
+ @sourceline=76,
163
+ @class_path=nil,
164
+ @method_id=nil>
165
+ ]
166
+ }
167
+ ```
168
+
169
+ (I've manually inserted the ellipses.)
170
+
171
+ You can see that there are three different groups:
172
+
173
+ * `[spec_helper.rb, String]`
174
+ * `[allocation_stats_spec.rb, Array]`
175
+ * `[object_space_stats.rb, String]`
176
+
177
+ Only one allocation belongs to each of the first two groups, and two allocations
178
+ make up the second group.
179
+
180
+ A little slower
181
+ ---------------
182
+
183
+ Let's look at this example a little slower. Firstly, let's look at how we
184
+ collect object allocations using AllocationStats:
185
+
186
+ ```ruby
187
+ stats = AllocationStats.trace do
188
+ new_string = "stringy string"
189
+ another_string = "another string"
190
+ an_array = [1,1,2,3,5,8,13,21,34,55]
191
+ a_foreign_string = allocate_a_string_from_spec_helper
192
+ end
193
+ ```
194
+
195
+ Stats are collected by running a block through `AllocationStats.trace`. This is
196
+ largely just a thin wrapper around `trace_object_allocations()`. You are handed
197
+ back your new AllocationStats, which essentially just holds all of the
198
+ allocation information, accessible via `#allocations`. Let's look at the next
199
+ line to see how we can pull useful information out:
200
+
201
+ ```ruby
202
+ results = stats.allocations.group_by(:@sourcefile, :class).to_a
203
+ ```
204
+
205
+ If you are used to chaining ActiveRecord relations, some of this might look
206
+ familiar to you: `stats.allocations` will hand you back an {AllocationsProxy}
207
+ object, designed to hold the various transformations that you wish to run the
208
+ allocations through. AllocationsProxy uses the Command pattern to store up
209
+ transformations before they will actually be applied. In this example, we only
210
+ make one transformation: `group_by(:@sourcefile, :class)`. This method just
211
+ returns the same AllocationsProxy object back, so that transformations can be
212
+ chained. The final call that will execute the transformations is `#to_a`
213
+ (aliased to `#all`, just like ActiveRecord).
214
+
215
+ Psych Example
216
+ -------------
217
+
218
+ Let's look at an example with more varied allocations, using Ruby's Psych. This
219
+ one is found in `examples/trace_psych_keys.rb`:
220
+
221
+ ```ruby
222
+ stats = AllocationStats.trace do
223
+ y = YAML.dump(["one string", "two string"]) # lots of objects from Rbconfig::CONFIG["rubylibdir"]
224
+ end
225
+
226
+ stats.allocations.group_by(:sourcefile, :class).all.keys.each { |key| puts key.inspect }
227
+
228
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", String]
229
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Array]
230
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", MatchData]
231
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Method]
232
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/nodes/node.rb", Array]
233
+ ["(eval)", Psych::Nodes::Sequence]
234
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/tree_builder.rb", Psych::Nodes::Document]
235
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/tree_builder.rb", Psych::Nodes::Stream]
236
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Proc]
237
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", RubyVM::Env]
238
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Hash]
239
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Psych::Visitors::YAMLTree::Registrar]
240
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Psych::Visitors::YAMLTree]
241
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/scalar_scanner.rb", Hash]
242
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Psych::ScalarScanner]
243
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/class_loader.rb", Hash]
244
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Psych::ClassLoader]
245
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/tree_builder.rb", Array]
246
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb", Psych::TreeBuilder]
247
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych.rb", Hash]
248
+ ["examples/trace_psych_inspect.rb", Array]
249
+ ["examples/trace_psych_inspect.rb", String]
250
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/emitter.rb", String]
251
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/emitter.rb", Array]
252
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/emitter.rb", RubyVM::InstructionSequence]
253
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/visitor.rb", String]
254
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/visitor.rb", MatchData]
255
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/visitor.rb", Regexp]
256
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/visitors/emitter.rb", Psych::Emitter]
257
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/nodes/node.rb", Psych::Visitors::Emitter]
258
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/nodes/node.rb", StringIO]
259
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/nodes/node.rb", String]
260
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/tree_builder.rb", Psych::Nodes::Scalar]
261
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/scalar_scanner.rb", String]
262
+ ["/usr/local/rbenv/versions/2.1.0-dev/lib/ruby/2.1.0/psych/scalar_scanner.rb", MatchData]
263
+ ```
264
+
265
+ Again, it is difficult to find useful information from these results without
266
+ aggregating. Let's do that (`examples/trace_psych_group_by`):
267
+
268
+ ```ruby
269
+ stats = AllocationStats.trace do
270
+ y = YAML.dump(["one string", "two string"]) # lots of objects from Rbconfig::CONFIG["rubylibdir"]
271
+ end
272
+
273
+ puts stats.allocations(alias_paths: true).group_by(:sourcefile, :class).to_text
274
+
275
+ sourcefile class count
276
+ ---------------------------------------- ------------------------------------ -----
277
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Array 12
278
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb String 20
279
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb MatchData 3
280
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Method 5
281
+ <RUBYLIBDIR>/psych/nodes/node.rb Array 3
282
+ (eval) Psych::Nodes::Sequence 1
283
+ <RUBYLIBDIR>/psych/tree_builder.rb Psych::Nodes::Document 1
284
+ <RUBYLIBDIR>/psych/tree_builder.rb Psych::Nodes::Stream 1
285
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Proc 1
286
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb RubyVM::Env 1
287
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Hash 3
288
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Psych::Visitors::YAMLTree::Registrar 1
289
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Psych::Visitors::YAMLTree 1
290
+ <RUBYLIBDIR>/psych/scalar_scanner.rb Hash 2
291
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Psych::ScalarScanner 1
292
+ <RUBYLIBDIR>/psych/class_loader.rb Hash 1
293
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Psych::ClassLoader 1
294
+ <RUBYLIBDIR>/psych/tree_builder.rb Array 1
295
+ <RUBYLIBDIR>/psych/visitors/yaml_tree.rb Psych::TreeBuilder 1
296
+ <RUBYLIBDIR>/psych.rb Hash 1
297
+ ./examples/trace_psych_raw.rb Array 1
298
+ ./examples/trace_psych_raw.rb String 2
299
+ <RUBYLIBDIR>/psych/visitors/emitter.rb String 29
300
+ <RUBYLIBDIR>/psych/visitors/emitter.rb Array 3
301
+ <RUBYLIBDIR>/psych/visitors/emitter.rb RubyVM::InstructionSequence 1
302
+ <RUBYLIBDIR>/psych/visitors/visitor.rb String 38
303
+ <RUBYLIBDIR>/psych/visitors/visitor.rb MatchData 5
304
+ <RUBYLIBDIR>/psych/visitors/visitor.rb Regexp 1
305
+ <RUBYLIBDIR>/psych/visitors/emitter.rb Psych::Emitter 1
306
+ <RUBYLIBDIR>/psych/nodes/node.rb Psych::Visitors::Emitter 1
307
+ <RUBYLIBDIR>/psych/nodes/node.rb StringIO 1
308
+ <RUBYLIBDIR>/psych/nodes/node.rb String 3
309
+ <RUBYLIBDIR>/psych/tree_builder.rb Psych::Nodes::Scalar 2
310
+ <RUBYLIBDIR>/psych/scalar_scanner.rb String 5
311
+ <RUBYLIBDIR>/psych/scalar_scanner.rb MatchData 2
312
+ ```
313
+
314
+ Burn One
315
+ ========
316
+
317
+ If you find a lot of allocations in `kernel_require.rb` or a lot of allocations
318
+ of `RubyVM::InstructuinSequences`, you can "burn" one or more calls to your
319
+ block with the `burn` keyword. For example:
320
+ `AllocationStats.new(burn: 3).trace{ ... }` will first call the block 3 times,
321
+ without tracing allocations, before calling the block a 4th time, tracing
322
+ allocations.
323
+
324
+ The API
325
+ =======
326
+
327
+ So what methods are available on that AllocationsProxy thing? So far, the API
328
+ consists of:
329
+
330
+ * `#group_by`
331
+ * `#from` takes one String argument, which will matched against the
332
+ allocation filename.
333
+ * `#not_from` is the opposite of `#from`.
334
+ * `#from_pwd` will filter the allocations down to those originating from `pwd`
335
+ (e.g. allocations originating from "my project")
336
+ * `#where` accepts a hash of faux attribute keys. For example,
337
+
338
+ ```ruby
339
+ allocations.where(class: String)
340
+ ```
341
+
342
+ It does not yet accept lambdas as values, which _would_ enable
343
+ ActiveRecord-4-like calls, like
344
+
345
+ ```ruby
346
+ allocations.where(class: Array, size: ->(size) { size > 10 }
347
+ ```
348
+ * `#bytes`, which has an inconsistent definition, I think... TODO
349
+
350
+ What are faux attributes?
351
+ -------------------------
352
+
353
+ Valid values for `#group_by` and `#where` include:
354
+ * instance variables on each `Allocation`. These include `:sourcefile`,
355
+ `:sourceline`, etc.
356
+ * methods available on the objects that were allocated. These include things
357
+ like `:class`, or even `:size` if you know you only have objects that respond
358
+ to `:size`.
359
+ * Allocation helper methods that return something special about the allocated
360
+ object. Right now this just includes `:class_plus`.
361
+
362
+ I'm calling these things that you can group by or filter by, "faux attributes."
363
+
364
+ What is `class_plus`?
365
+ ---------------------
366
+
367
+ References
368
+ ==========
369
+
370
+ This new feature was inspired by work that @tmm1 did at GitHub, as
371
+ described in
372
+ [this post](https://github.com/blog/1489-hey-judy-don-t-make-it-bad). It was
373
+ proposed as a feature in Ruby Core by @tmm1 in
374
+ [Ruby issue #8107](http://bugs.ruby-lang.org/issues/8107), and @ko1 wrote it
375
+ into MRI. He introduces the feature in his Ruby Kaigi 2013 presentation, on
376
+ slides 29 through 33
377
+ [[pdf](http://www.atdot.net/~ko1/activities/RubyKaigi2013-ko1.pdf)].