mnemonic 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 11af3b3be9c8bbfc7f2f894153f15d43fd17f24f
4
+ data.tar.gz: 1ab4eebad3666c8375fcf688de6103fc431b4c90
5
+ SHA512:
6
+ metadata.gz: 591b470a1fc840c66068c6f697c82cddc267126c7aa3ecfccc00a5d3086d50556d864028434ba27f9cedbc4228c87d493978c1631658097ed06a206a43c8c458
7
+ data.tar.gz: 972c5ff51b863f6b5c6f88860b7da359d649ddf21c36d0e499c076ea4621b4164cd99c087218433761fa2219fcaf6f0668aca2a16beaef5df5425f84081045b5
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /vendor/bundle
26
+ /lib/bundler/man/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ # Gemfile.lock
31
+ # .ruby-version
32
+ # .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mnemonic.gemspec
4
+ gemspec
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mnemonic (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ coderay (1.1.0)
10
+ diff-lcs (1.2.5)
11
+ method_source (0.8.2)
12
+ pry (0.10.3)
13
+ coderay (~> 1.1.0)
14
+ method_source (~> 0.8.1)
15
+ slop (~> 3.4)
16
+ rake (10.4.2)
17
+ rspec (3.4.0)
18
+ rspec-core (~> 3.4.0)
19
+ rspec-expectations (~> 3.4.0)
20
+ rspec-mocks (~> 3.4.0)
21
+ rspec-core (3.4.0)
22
+ rspec-support (~> 3.4.0)
23
+ rspec-expectations (3.4.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.4.0)
26
+ rspec-mocks (3.4.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.4.0)
29
+ rspec-support (3.4.0)
30
+ slop (3.6.0)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ bundler (~> 1.10)
37
+ mnemonic!
38
+ pry
39
+ rake (~> 10.0)
40
+ rspec
41
+
42
+ BUNDLED WITH
43
+ 1.10.6
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Alexey Gaziev
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,47 @@
1
+ # mnemonic - The best tool to find leakage
2
+
3
+ If you know (or even just think) that your application is leaking, but you having hard times with figuring out
4
+ what exactly is leaking, when and why – this tool will help you to figure this out.
5
+
6
+ <a href="https://evilmartians.com/?utm_source=gon">
7
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
8
+ </a>
9
+
10
+ ## How it works
11
+
12
+ Сейчас у мнемоника есть два режима работы –
13
+
14
+ 1. Полуавтоматический, когда вы добавляете в нужных вам местах триггеры мнемоника.
15
+ В этом режиме мнемоник будет выводить или сохранять данные каждый раз, когда
16
+ будет выполняться код триггера.
17
+
18
+ 2. Автоматический, когда вы проксируете ваш логгер через прокси мнемоника.
19
+ В этом режиме мнемоник будет выводить или сохранять данные каждый раз,
20
+ когда вызывается логгер.
21
+
22
+ ## Formats
23
+
24
+ Мнемоник поддерживает три формата вывода данных:
25
+
26
+ 1. Pretty - красиво отформаттированный текстовый вывод данных:
27
+ скриншот
28
+
29
+ 2. JSON - данные представлены в виде JSON-массива, удобно для дальнейшего анализа при
30
+ помощи внешних инструментов
31
+ пример формата
32
+
33
+ 3. CSV - данные представлены в формате CSV, можно воспользоваться любым инструментом
34
+ с поддержкой импорта из csv, таким как Google Sheets и тп.
35
+ пример формата
36
+
37
+ Если вам не хватает какого-либо формата – мы будем очень рады пулл реквесту или
38
+ ишью с описанием недостающего формата.
39
+
40
+ ## What and how you can track with mnemonic
41
+
42
+ Мы постарались добавить в этот инструмент все метрики, которые когда-либо
43
+ помогали нам в расследовании наших утечек. Если мы упустили что-то важное - мы
44
+ будем очень благодарны за пулл реквест или ишью с описанием метрики.
45
+
46
+ На данный момент инструмент позволяет следить за следующими показателями:
47
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "mnemonic"
5
+
6
+ require "pry"
7
+ Pry.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ mnemonic = Mnemonic.new do |config|
2
+ config.gc_stat :minor_gc_count, :major_gc_count
3
+ end
4
+
5
+ mnemonic.attach_pretty(STDOUT)
6
+
7
+ # 5 major GCs!
8
+ 10.times do |i|
9
+ GC.start if i.odd?
10
+ mnemonic.trigger!(i.odd? ? 'major!' : 'no major')
11
+ end
12
+
13
+ mnemonic.detach_all
@@ -0,0 +1,23 @@
1
+ class X
2
+ end
3
+
4
+ mnemonic = Mnemonic.new do |config|
5
+ config.time_milliseconds
6
+ config.instances_count(X)
7
+ config.instances_size(X)
8
+ end
9
+
10
+ mnemonic.attach_csv(STDOUT, extra: true)
11
+
12
+ 10.times do
13
+ X.new
14
+ mnemonic.trigger!(:without_gc)
15
+ end
16
+
17
+ 10.times do
18
+ X.new
19
+ GC.start
20
+ mnemonic.trigger!(:with_gc)
21
+ end
22
+
23
+ mnemonic.detach_all
@@ -0,0 +1,30 @@
1
+ require 'mnemonic/logger_proxy'
2
+
3
+ file_name = './tmp.log'
4
+ logger = Mnemonic::LoggerProxy.new(Logger.new(file_name)) do |config|
5
+ config.objects_count(:T_HASH)
6
+ config.objects_size(:T_HASH)
7
+ config.objects_count(:T_ARRAY)
8
+ config.objects_size(:T_ARRAY)
9
+ end
10
+
11
+ 10.times do
12
+ {}; {[] => []}; [] # +3 arrays, +2 hashes
13
+ logger.info 'hello with logging enabled!'
14
+ end
15
+
16
+ logger.disable_mnemonic!
17
+
18
+ 10.times do
19
+ logger.info 'hello with logging disabled!'
20
+ end
21
+
22
+ logger.enable_mnemonic!
23
+
24
+ 10.times do
25
+ logger.info 'hello with logging re-enabled!'
26
+ end
27
+
28
+
29
+
30
+ puts File.read(file_name)
@@ -0,0 +1,13 @@
1
+ mnemonic = Mnemonic.new do |config|
2
+ config.rss
3
+ end
4
+
5
+ mnemonic.attach_pretty(STDOUT)
6
+
7
+ # 5 major GCs!
8
+ 10.times do |i|
9
+ '!' * 1_000_000
10
+ mnemonic.trigger!
11
+ end
12
+
13
+ mnemonic.detach_all
@@ -0,0 +1,39 @@
1
+ file_name = './tmp.csv'
2
+
3
+ mnemonic = Mnemonic.new do |config|
4
+ config.time_milliseconds
5
+ config.objects_count(:T_HASH)
6
+ config.objects_size(:T_HASH)
7
+ end
8
+
9
+ mnemonic.attach_csv(file_name)
10
+
11
+ 100.times do
12
+ Hash.new
13
+ mnemonic.trigger!
14
+ end
15
+
16
+ mnemonic.detach_all
17
+
18
+ puts File.read(file_name)
19
+
20
+ puts
21
+ puts "WITH EXTRA COLUMN"
22
+ puts
23
+
24
+ mnemonic = Mnemonic.new do |config|
25
+ config.time_milliseconds
26
+ config.objects_count(:T_HASH)
27
+ config.objects_size(:T_HASH)
28
+ end
29
+
30
+ mnemonic.attach_csv(file_name, extra: true)
31
+
32
+ 100.times do |i|
33
+ Hash.new
34
+ mnemonic.trigger!(2 ** i)
35
+ end
36
+
37
+ mnemonic.detach_all
38
+
39
+ puts File.read(file_name)
@@ -0,0 +1,17 @@
1
+ file_name = './tmp.json'
2
+
3
+ mnemonic = Mnemonic.new do |config|
4
+ config.time_milliseconds
5
+ config.objects_count(:T_HASH, :T_ARRAY)
6
+ end
7
+
8
+ mnemonic.attach_json(file_name)
9
+
10
+ 10.times do
11
+ {}; {[] => []}; [] # +3 arrays, +2 hashes
12
+ mnemonic.trigger!
13
+ end
14
+
15
+ mnemonic.detach_all
16
+
17
+ puts JSON.pretty_generate(JSON.load(File.read(file_name)))
@@ -0,0 +1,74 @@
1
+ require 'objspace'
2
+ require 'monitor.rb'
3
+ require 'set'
4
+
5
+ class Mnemonic
6
+ require 'mnemonic/version'
7
+ require 'mnemonic/config'
8
+ require 'mnemonic/metric'
9
+ require 'mnemonic/sink'
10
+ require 'mnemonic/util'
11
+
12
+ include MonitorMixin
13
+
14
+ attr_reader :root_metrics, :metrics, :metric_names
15
+
16
+ def initialize
17
+ super
18
+ config = Mnemonic::Config.new
19
+ yield config
20
+
21
+ @root_metrics = config.metrics.map do |metric|
22
+ metric.klass.new(*metric.args)
23
+ end
24
+
25
+ @metrics = @root_metrics.flat_map do |metric|
26
+ metric.to_enum(:each_submetric).to_a
27
+ end
28
+
29
+ @root_metrics.each(&:start!)
30
+ @metric_names = @metrics.map(&:name)
31
+ @sinks = Set.new
32
+ end
33
+
34
+ def trigger!(extra = nil)
35
+ synchronize do
36
+ @root_metrics.each(&:refresh!)
37
+ @sinks.each { |s| s.drop!(extra) }
38
+ end
39
+ end
40
+
41
+ def attach_csv(*args)
42
+ attach(Sink::CSV, *args)
43
+ end
44
+
45
+ def attach_json(*args)
46
+ attach(Sink::JSON, *args)
47
+ end
48
+
49
+ def attach_pretty(*args)
50
+ attach(Sink::Pretty, *args)
51
+ end
52
+
53
+ def attach(sink_class, *args, &block)
54
+ synchronize do
55
+ sink_class.new(self, *args, &block).tap do |sink|
56
+ @sinks << sink
57
+ end
58
+ end
59
+ end
60
+
61
+ def detach(sink)
62
+ synchronize do
63
+ sink.close
64
+ @sinks.delete sink
65
+ end
66
+ end
67
+
68
+ def detach_all
69
+ synchronize do
70
+ @sinks.each(&:close)
71
+ @sinks.clear
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,62 @@
1
+ class Mnemonic
2
+ class Config
3
+ MetricDescription = Struct.new(:klass, :args)
4
+
5
+ attr_reader :metrics
6
+
7
+ def initialize
8
+ @metrics = []
9
+ end
10
+
11
+ def add_metric(klass, args=nil)
12
+ @metrics << MetricDescription.new(klass, args)
13
+ end
14
+
15
+ def gc_stat(*stat_names)
16
+ add_metric Metric::GCStat, stat_names
17
+ end
18
+
19
+ def objects_count(*object_types)
20
+ add_metric Metric::ObjectsCount, object_types
21
+ end
22
+
23
+ def objects_size(*object_types)
24
+ add_metric Metric::ObjectsSize, object_types
25
+ end
26
+
27
+ def instances_count(*target_klass_names)
28
+ target_klass_names.each do |klass_name|
29
+ add_metric Metric::InstancesCount, klass_name
30
+ end
31
+ end
32
+
33
+ def instances_size(*target_klass_names)
34
+ target_klass_names.each do |klass_name|
35
+ add_metric Metric::InstancesSize, klass_name
36
+ end
37
+ end
38
+
39
+ def time_seconds
40
+ add_metric Metric::TimeSeconds
41
+ end
42
+
43
+ def time_milliseconds
44
+ add_metric Metric::TimeMilliseconds
45
+ end
46
+
47
+ def time
48
+ add_metric Metric::Time
49
+ end
50
+
51
+ def rss
52
+ # TODO: finish!
53
+ klass = case Util::OS.type
54
+ when :linux
55
+ Metric::RSS::ProcFS
56
+ else
57
+ Metric::RSS::PS
58
+ end
59
+ add_metric klass
60
+ end
61
+ end
62
+ end