leak_profiler 0.1.3 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 062abe3c1684ea42ac45baabb2b11ab19624fd461af76ee89aff981d0dec4cb4
4
- data.tar.gz: e048e567ceae885b4ac91ff8b72b611144e131c9462ba1e33589c12e1fd392bc
3
+ metadata.gz: 6881c79a0e59209edf1ad4152128c9efafa002cff34f7d30708ef3c81507a577
4
+ data.tar.gz: 94671293bf106f7563b74c2370efc4c254d66c84ed59559d5bba029fcd1613d8
5
5
  SHA512:
6
- metadata.gz: f023bc3f7a64638d2e63d7a9262a59b61b16459c22dca2d1a1bb5a3c846deb0758ffd525f504863107f0d3a1611f243fea0f55f6e2cdfbb801c8e75b51492b0d
7
- data.tar.gz: 47fe07ea97905182c7656f9557efee004906a15ffcd8e56d465ad39eb3fe57cfc7878e3cc094b6779d6fb33dc143dfcc1b420c55cbbeb8fc2cea3f8ed03950c0
6
+ metadata.gz: e7490843212bda8d157a90bc64f4681cdc9a00f164de5edd9a166579d5d7801ce912fb6b70ed8f01e1a48a9aa3925ccff77ff81bbfcff8e6393c8f444f98dbb8
7
+ data.tar.gz: fcf967d7ae7a4c3e09832bb7352e738c7e454370a4b504e191036f080fe51129c67b4e4afff22638e38145f5e9e8e4d4aa8ed4c44aee3f373e87f28960f848d9
data/.rubocop_todo.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-03-31 02:21:42 UTC using RuboCop version 1.75.1.
3
+ # on 2025-04-26 03:09:17 UTC using RuboCop version 1.75.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 3
9
+ # Offense count: 4
10
10
  # Configuration parameters: AllowedConstants.
11
11
  Style/Documentation:
12
12
  Exclude:
@@ -14,9 +14,10 @@ Style/Documentation:
14
14
  - 'test/**/*'
15
15
  - 'lib/leak_profiler/allocations.rb'
16
16
  - 'lib/leak_profiler/leak_profiler.rb'
17
+ - 'lib/leak_profiler/memory_memsize.rb'
17
18
  - 'lib/leak_profiler/memory_usage.rb'
18
19
 
19
- # Offense count: 4
20
+ # Offense count: 6
20
21
  # Configuration parameters: AllowedMethods, RequireForNonPublicMethods.
21
22
  Style/DocumentationMethod:
22
23
  Exclude:
@@ -24,4 +25,5 @@ Style/DocumentationMethod:
24
25
  - 'test/**/*'
25
26
  - 'lib/leak_profiler/allocations.rb'
26
27
  - 'lib/leak_profiler/leak_profiler.rb'
28
+ - 'lib/leak_profiler/memory_memsize.rb'
27
29
  - 'lib/leak_profiler/memory_usage.rb'
data/README.md CHANGED
@@ -27,7 +27,7 @@ LeakProfiler.new.report.report_rss
27
27
  # ... your code that may have memory leaks ...
28
28
  ```
29
29
 
30
- ### `LeakProfiler#new`
30
+ ### `LeakProfiler.new`
31
31
  * Arguments:
32
32
  * `output_dir` (default `./leak_profiler`): Specify the output directory for report.
33
33
 
@@ -64,7 +64,8 @@ Referrers ----------------------------------------------------------------------
64
64
  * `interval` (default `30`): The interval in seconds for report.
65
65
  * `max_allocations` (default `10`): Outputs the specified number of objects that use a lot of memory.
66
66
  * `max_referrers` (default `3`): Outputs the number of references in order of the amount of memory used.
67
- * `report` (defalut `nil`): Specify the logger object if you want to use custom logger.
67
+ * `logger` (defalut `nil`): Specify the logger object if you want to use custom logger.
68
+ * `filename` (defalut `nil`): Specify the filename if you want to use custom filename.
68
69
 
69
70
  > [!WARNING]
70
71
  > This uses `ObjectSpace.allocation_sourcefile` method to measurement.
@@ -83,11 +84,29 @@ elapsed [sec],memory usage (rss) [MB]
83
84
 
84
85
  * Arguments:
85
86
  * `interval` (default `1`): The interval in seconds for report.
87
+ * `filename` (defalut `nil`): Specify the filename if you want to use custom filename.
86
88
 
87
89
  > [!WARNING]
88
90
  > This uses this uses `ps` command for measurement.
89
91
  > So, this is not supported Windows platform.
90
92
 
93
+
94
+ ### `LeakProfiler#report_memsize`
95
+ This method outputs `ObjectSpace.memsize_of_all` values with CSV format, like:
96
+
97
+ ```
98
+ elapsed [sec],ObjectSpace.memsize_of_all values [MB]
99
+ 0,11.435378074645996
100
+ 1,12.78317642211914
101
+ 2,11.785511016845703
102
+ 3,11.785999298095703
103
+ 4,11.786487579345703
104
+ ```
105
+
106
+ * Arguments:
107
+ * `interval` (default `1`): The interval in seconds for report.
108
+ * `filename` (defalut `nil`): Specify the filename if you want to use custom filename.
109
+
91
110
  ## Contributing
92
111
 
93
112
  Bug reports and pull requests are welcome on GitHub at https://github.com/Watson1978/leak_profiler.
data/Rakefile CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/gem_tasks'
4
- require 'rubocop/rake_task'
4
+ require 'rake/testtask'
5
5
 
6
- RuboCop::RakeTask.new
6
+ task default: :test
7
7
 
8
- task default: :rubocop
8
+ Rake::TestTask.new do |task|
9
+ task.pattern = 'test/test_*.rb'
10
+ end
@@ -4,6 +4,8 @@ require 'objspace'
4
4
 
5
5
  class LeakProfiler
6
6
  class Allocations
7
+ attr_reader :thread
8
+
7
9
  def initialize(logger:, interval:, max_allocations:, max_referrers:)
8
10
  @logger = logger
9
11
  @interval = interval
@@ -12,7 +14,7 @@ class LeakProfiler
12
14
  end
13
15
 
14
16
  def report
15
- Thread.start do
17
+ @thread = Thread.start do
16
18
  loop do
17
19
  allocations = {}
18
20
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'allocations'
4
+ require_relative 'memory_memsize'
4
5
  require_relative 'memory_usage'
5
6
  require 'fileutils'
6
7
  require 'logger'
@@ -8,20 +9,39 @@ require 'logger'
8
9
  class LeakProfiler
9
10
  def initialize(output_dir: './leak_profiler')
10
11
  @output_dir = output_dir
12
+ @threads = []
11
13
 
12
14
  FileUtils.mkdir_p(@output_dir)
13
15
  end
14
16
 
15
- def report(interval: 30, max_allocations: 10, max_referrers: 3, logger: nil)
16
- logger ||= Logger.new(File.join(@output_dir, "leak_profiler-#{Process.pid}.log"))
17
- LeakProfiler::Allocations.new(logger: logger, interval: interval, max_allocations: max_allocations, max_referrers: max_referrers).report
17
+ def report(interval: 30, max_allocations: 10, max_referrers: 3, logger: nil, filename: nil)
18
+ filename ||= "leak_profiler-#{Process.pid}.log"
19
+ logger ||= Logger.new(File.join(@output_dir, filename))
20
+ profiler = LeakProfiler::Allocations.new(logger: logger, interval: interval, max_allocations: max_allocations, max_referrers: max_referrers)
21
+ profiler.report
22
+ @threads << profiler.thread
18
23
 
19
24
  self
20
25
  end
21
26
 
22
- def report_rss(interval: 1)
23
- LeakProfiler::MemoryUsage.new(output_dir: @output_dir, interval: interval).report
27
+ def report_rss(interval: 1, filename: nil)
28
+ profiler = LeakProfiler::MemoryUsage.new(output_dir: @output_dir, interval: interval, filename: filename)
29
+ profiler.report
30
+ @threads << profiler.thread
24
31
 
25
32
  self
26
33
  end
34
+
35
+ def report_memsize(interval: 1, filename: nil)
36
+ profiler = LeakProfiler::MemoryMemsize.new(output_dir: @output_dir, interval: interval, filename: filename)
37
+ profiler.report
38
+ @threads << profiler.thread
39
+
40
+ self
41
+ end
42
+
43
+ def shutdown
44
+ @threads.each(&:kill)
45
+ @threads.each(&:join)
46
+ end
27
47
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'objspace'
4
+
5
+ class LeakProfiler
6
+ class MemoryMemsize
7
+ attr_reader :thread
8
+
9
+ def initialize(output_dir:, interval:, filename: nil)
10
+ @output_dir = output_dir
11
+ @interval = interval
12
+ @filename = filename || "memory-memsize-#{Process.pid}.csv"
13
+ end
14
+
15
+ def report
16
+ @thread = Thread.start do
17
+ i = 0
18
+ File.open(File.expand_path(File.join(@output_dir, @filename)), 'w') do |f|
19
+ f.puts('elapsed [sec],ObjectSpace.memsize_of_all values [MB]')
20
+
21
+ loop do
22
+ memsize = Float(ObjectSpace.memsize_of_all) / (1024 * 1024)
23
+
24
+ f.puts("#{i},#{memsize}")
25
+ i += @interval
26
+ sleep(@interval)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -2,9 +2,12 @@
2
2
 
3
3
  class LeakProfiler
4
4
  class MemoryUsage
5
- def initialize(output_dir:, interval:)
5
+ attr_reader :thread
6
+
7
+ def initialize(output_dir:, interval:, filename: nil)
6
8
  @output_dir = output_dir
7
9
  @interval = interval
10
+ @filename = filename || "memory-usage-#{Process.pid}.csv"
8
11
  end
9
12
 
10
13
  def report
@@ -12,9 +15,9 @@ class LeakProfiler
12
15
 
13
16
  pid = Process.pid
14
17
 
15
- Thread.new do
18
+ @thread = Thread.start do
16
19
  i = 0
17
- File.open(File.expand_path(File.join(@output_dir, "memory-usage-#{pid}.csv")), 'w') do |f|
20
+ File.open(File.expand_path(File.join(@output_dir, @filename)), 'w') do |f|
18
21
  f.puts('elapsed [sec],memory usage (rss) [MB]')
19
22
 
20
23
  loop do
@@ -0,0 +1,10 @@
1
+ class LeakProfiler
2
+ def initialize: (?output_dir: String) -> void
3
+
4
+ # TODO: Use valid type for logger
5
+ def report: (?interval: Integer, ?max_allocations: Integer, ?max_referrers: Integer, ?logger: Object, ?filename: String) -> self
6
+
7
+ def report_rss: (?interval: Integer, ?filename: String) -> self
8
+
9
+ def report_memsize: (?interval: Integer, ?filename: String) -> self
10
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'leak_profiler'
4
+ require 'minitest/autorun'
5
+
6
+ class LeakProfilerTest < Minitest::Test
7
+ def test_report
8
+ output_dir = './tmp'
9
+ profiler = LeakProfiler.new(output_dir: output_dir)
10
+
11
+ profiler.report(interval: 1, max_allocations: 5, max_referrers: 2, filename: 'test_report.log')
12
+ sleep(2)
13
+ profiler.shutdown
14
+
15
+ assert(File.exist?(File.join(output_dir, 'test_report.log')))
16
+ end
17
+
18
+ def test_report_rss
19
+ output_dir = './tmp'
20
+ profiler = LeakProfiler.new(output_dir: output_dir)
21
+
22
+ profiler.report_rss(interval: 1, filename: 'test_report_rss.csv')
23
+ sleep(2)
24
+ profiler.shutdown
25
+
26
+ assert(File.exist?(File.join(output_dir, 'test_report_rss.csv')))
27
+ end
28
+
29
+ def test_report_memsize
30
+ output_dir = './tmp'
31
+ profiler = LeakProfiler.new(output_dir: output_dir)
32
+
33
+ profiler.report_memsize(interval: 1, filename: 'test_report_memsize.csv')
34
+ sleep(2)
35
+ profiler.shutdown
36
+
37
+ assert(File.exist?(File.join(output_dir, 'test_report_memsize.csv')))
38
+ end
39
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leak_profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Watson
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: logger
@@ -38,7 +38,10 @@ files:
38
38
  - lib/leak_profiler.rb
39
39
  - lib/leak_profiler/allocations.rb
40
40
  - lib/leak_profiler/leak_profiler.rb
41
+ - lib/leak_profiler/memory_memsize.rb
41
42
  - lib/leak_profiler/memory_usage.rb
43
+ - sig/leak_profiler.rbs
44
+ - test/test_leak_profiler.rb
42
45
  homepage: https://github.com/Watson1978/leak_profiler
43
46
  licenses:
44
47
  - MIT
@@ -60,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
63
  - !ruby/object:Gem::Version
61
64
  version: '0'
62
65
  requirements: []
63
- rubygems_version: 3.6.2
66
+ rubygems_version: 3.6.7
64
67
  specification_version: 4
65
68
  summary: A simple profiler for Ruby to detect memory leak.
66
69
  test_files: []