ruby-prof 1.4.3 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +59 -9
  3. data/{README.rdoc → README.md} +2 -2
  4. data/Rakefile +4 -4
  5. data/bin/ruby-prof +100 -87
  6. data/ext/ruby_prof/rp_allocation.c +140 -85
  7. data/ext/ruby_prof/rp_allocation.h +8 -6
  8. data/ext/ruby_prof/rp_call_tree.c +502 -369
  9. data/ext/ruby_prof/rp_call_tree.h +47 -43
  10. data/ext/ruby_prof/rp_call_trees.c +16 -8
  11. data/ext/ruby_prof/rp_measure_allocations.c +10 -13
  12. data/ext/ruby_prof/rp_measure_memory.c +8 -4
  13. data/ext/ruby_prof/rp_measure_process_time.c +7 -6
  14. data/ext/ruby_prof/rp_measurement.c +147 -20
  15. data/ext/ruby_prof/rp_measurement.h +4 -1
  16. data/ext/ruby_prof/rp_method.c +142 -83
  17. data/ext/ruby_prof/rp_method.h +63 -62
  18. data/ext/ruby_prof/rp_profile.c +933 -900
  19. data/ext/ruby_prof/rp_profile.h +1 -0
  20. data/ext/ruby_prof/rp_thread.c +433 -362
  21. data/ext/ruby_prof/rp_thread.h +39 -39
  22. data/ext/ruby_prof/ruby_prof.c +0 -2
  23. data/ext/ruby_prof/ruby_prof.h +8 -0
  24. data/ext/ruby_prof/vc/ruby_prof.vcxproj +11 -8
  25. data/lib/ruby-prof/assets/call_stack_printer.html.erb +2 -1
  26. data/lib/ruby-prof/compatibility.rb +14 -0
  27. data/lib/ruby-prof/method_info.rb +8 -1
  28. data/lib/ruby-prof/printers/abstract_printer.rb +2 -1
  29. data/lib/ruby-prof/printers/call_tree_printer.rb +4 -10
  30. data/lib/ruby-prof/printers/graph_html_printer.rb +1 -1
  31. data/lib/ruby-prof/printers/multi_printer.rb +17 -17
  32. data/lib/ruby-prof/profile.rb +70 -37
  33. data/lib/ruby-prof/rack.rb +31 -21
  34. data/lib/ruby-prof/version.rb +1 -1
  35. data/lib/ruby-prof.rb +1 -1
  36. data/ruby-prof.gemspec +2 -3
  37. data/test/abstract_printer_test.rb +1 -0
  38. data/test/alias_test.rb +97 -106
  39. data/test/call_tree_builder.rb +126 -0
  40. data/test/call_tree_test.rb +94 -0
  41. data/test/call_tree_visitor_test.rb +1 -6
  42. data/test/call_trees_test.rb +6 -6
  43. data/test/{basic_test.rb → compatibility_test.rb} +8 -2
  44. data/test/duplicate_names_test.rb +5 -5
  45. data/test/dynamic_method_test.rb +24 -15
  46. data/test/enumerable_test.rb +1 -1
  47. data/test/exceptions_test.rb +2 -2
  48. data/test/exclude_methods_test.rb +3 -8
  49. data/test/exclude_threads_test.rb +4 -9
  50. data/test/fiber_test.rb +74 -8
  51. data/test/gc_test.rb +11 -9
  52. data/test/inverse_call_tree_test.rb +33 -34
  53. data/test/line_number_test.rb +37 -61
  54. data/test/marshal_test.rb +16 -3
  55. data/test/measure_allocations.rb +1 -5
  56. data/test/measure_allocations_test.rb +642 -357
  57. data/test/{measure_memory_trace_test.rb → measure_memory_test.rb} +180 -616
  58. data/test/measure_process_time_test.rb +1566 -741
  59. data/test/measure_wall_time_test.rb +179 -193
  60. data/test/measurement_test.rb +82 -0
  61. data/test/merge_test.rb +146 -0
  62. data/test/method_info_test.rb +95 -0
  63. data/test/multi_printer_test.rb +0 -5
  64. data/test/no_method_class_test.rb +1 -1
  65. data/test/pause_resume_test.rb +12 -16
  66. data/test/printer_call_stack_test.rb +2 -2
  67. data/test/printer_call_tree_test.rb +4 -4
  68. data/test/printer_flat_test.rb +1 -1
  69. data/test/printer_graph_html_test.rb +2 -2
  70. data/test/printer_graph_test.rb +2 -2
  71. data/test/printers_test.rb +14 -20
  72. data/test/printing_recursive_graph_test.rb +2 -2
  73. data/test/profile_test.rb +85 -0
  74. data/test/recursive_test.rb +374 -155
  75. data/test/scheduler.rb +363 -0
  76. data/test/singleton_test.rb +1 -1
  77. data/test/stack_printer_test.rb +5 -8
  78. data/test/start_stop_test.rb +11 -14
  79. data/test/test_helper.rb +11 -8
  80. data/test/thread_test.rb +106 -15
  81. data/test/unique_call_path_test.rb +28 -12
  82. data/test/yarv_test.rb +11 -7
  83. metadata +17 -29
  84. data/ext/ruby_prof/rp_aggregate_call_tree.c +0 -59
  85. data/ext/ruby_prof/rp_aggregate_call_tree.h +0 -13
  86. data/test/measure_allocations_trace_test.rb +0 -375
  87. data/test/temp.rb +0 -20
@@ -1,39 +1,39 @@
1
- /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
- Please see the LICENSE file for copyright and distribution information */
3
-
4
- #ifndef __RP_THREAD__
5
- #define __RP_THREAD__
6
-
7
- #include "ruby_prof.h"
8
- #include "rp_stack.h"
9
-
10
- /* Profiling information for a thread. */
11
- typedef struct thread_data_t
12
- {
13
- // Runtime
14
- VALUE object; /* Cache to wrapped object */
15
- VALUE fiber; /* Fiber */
16
- prof_stack_t* stack; /* Stack of frames */
17
- bool trace; /* Are we tracking this thread */
18
- prof_call_tree_t* call_tree; /* The root of the call tree*/
19
- VALUE thread_id; /* Thread id */
20
- VALUE fiber_id; /* Fiber id */
21
- VALUE methods; /* Array of RubyProf::MethodInfo */
22
- st_table* method_table; /* Methods called in the thread */
23
- } thread_data_t;
24
-
25
- void rp_init_thread(void);
26
- st_table* threads_table_create(void);
27
- thread_data_t* threads_table_lookup(void* profile, VALUE fiber);
28
- thread_data_t* threads_table_insert(void* profile, VALUE fiber);
29
- void threads_table_free(st_table* table);
30
-
31
- thread_data_t* prof_get_thread(VALUE self);
32
- VALUE prof_thread_wrap(thread_data_t* thread);
33
- void prof_thread_mark(void* data);
34
-
35
- void switch_thread(void* profile, thread_data_t* thread_data, double measurement);
36
- int pause_thread(st_data_t key, st_data_t value, st_data_t data);
37
- int unpause_thread(st_data_t key, st_data_t value, st_data_t data);
38
-
39
- #endif //__RP_THREAD__
1
+ /* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
2
+ Please see the LICENSE file for copyright and distribution information */
3
+
4
+ #ifndef __RP_THREAD__
5
+ #define __RP_THREAD__
6
+
7
+ #include "ruby_prof.h"
8
+ #include "rp_stack.h"
9
+
10
+ /* Profiling information for a thread. */
11
+ typedef struct thread_data_t
12
+ {
13
+ prof_owner_t owner; /* Who owns this object */
14
+ VALUE object; /* Cache to wrapped object */
15
+ VALUE fiber; /* Fiber */
16
+ prof_stack_t* stack; /* Stack of frames */
17
+ bool trace; /* Are we tracking this thread */
18
+ prof_call_tree_t* call_tree; /* The root of the call tree*/
19
+ VALUE thread_id; /* Thread id */
20
+ VALUE fiber_id; /* Fiber id */
21
+ VALUE methods; /* Array of RubyProf::MethodInfo */
22
+ st_table* method_table; /* Methods called in the thread */
23
+ } thread_data_t;
24
+
25
+ void rp_init_thread(void);
26
+ st_table* threads_table_create(void);
27
+ thread_data_t* threads_table_lookup(void* profile, VALUE fiber);
28
+ thread_data_t* threads_table_insert(void* profile, VALUE fiber);
29
+ void threads_table_free(st_table* table);
30
+
31
+ thread_data_t* prof_get_thread(VALUE self);
32
+ VALUE prof_thread_wrap(thread_data_t* thread);
33
+ void prof_thread_mark(void* data);
34
+
35
+ void switch_thread(void* profile, thread_data_t* thread_data, double measurement);
36
+ int pause_thread(st_data_t key, st_data_t value, st_data_t data);
37
+ int unpause_thread(st_data_t key, st_data_t value, st_data_t data);
38
+
39
+ #endif //__RP_THREAD__
@@ -29,7 +29,6 @@
29
29
  #include "rp_measurement.h"
30
30
  #include "rp_method.h"
31
31
  #include "rp_call_tree.h"
32
- #include "rp_aggregate_call_tree.h"
33
32
  #include "rp_call_trees.h"
34
33
  #include "rp_profile.h"
35
34
  #include "rp_stack.h"
@@ -43,7 +42,6 @@ void Init_ruby_prof()
43
42
 
44
43
  rp_init_allocation();
45
44
  rp_init_call_tree();
46
- rp_init_aggregate_call_tree();
47
45
  rp_init_call_trees();
48
46
  rp_init_measure();
49
47
  rp_init_method_info();
@@ -23,4 +23,12 @@ extern VALUE mProf;
23
23
  // This method is not exposed in Ruby header files - at least not as of Ruby 2.6.3 :(
24
24
  extern size_t rb_obj_memsize_of(VALUE);
25
25
 
26
+ typedef enum
27
+ {
28
+ OWNER_UNKNOWN = 0,
29
+ OWNER_RUBY = 1,
30
+ OWNER_C = 2
31
+ } prof_owner_t;
32
+
33
+
26
34
  #endif //__RUBY_PROF_H__
@@ -29,19 +29,21 @@
29
29
  <ConfigurationType>DynamicLibrary</ConfigurationType>
30
30
  <UseDebugLibraries>true</UseDebugLibraries>
31
31
  <CharacterSet>Unicode</CharacterSet>
32
+ <PlatformToolset>v143</PlatformToolset>
32
33
  </PropertyGroup>
33
34
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
34
35
  <ConfigurationType>DynamicLibrary</ConfigurationType>
35
36
  <UseDebugLibraries>false</UseDebugLibraries>
36
37
  <WholeProgramOptimization>true</WholeProgramOptimization>
37
38
  <CharacterSet>Unicode</CharacterSet>
39
+ <PlatformToolset>v143</PlatformToolset>
38
40
  </PropertyGroup>
39
41
  <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
40
- <PlatformToolset>v142</PlatformToolset>
42
+ <PlatformToolset>v143</PlatformToolset>
41
43
  <ConfigurationType>DynamicLibrary</ConfigurationType>
42
44
  </PropertyGroup>
43
45
  <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
44
- <PlatformToolset>v142</PlatformToolset>
46
+ <PlatformToolset>v143</PlatformToolset>
45
47
  </PropertyGroup>
46
48
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
47
49
  <ImportGroup Label="ExtensionSettings">
@@ -64,7 +66,7 @@
64
66
  </PropertyGroup>
65
67
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
66
68
  <TargetExt>.so</TargetExt>
67
- <OutDir>..\..\..\lib\</OutDir>
69
+ <OutDir>$(SolutionDir)\..</OutDir>
68
70
  </PropertyGroup>
69
71
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
70
72
  <ClCompile>
@@ -102,17 +104,20 @@
102
104
  </ItemDefinitionGroup>
103
105
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
104
106
  <ClCompile>
105
- <AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-2.7.2vc\include\ruby-2.7.0\x64-mswin64_140;C:\msys64\usr\local\ruby-2.7.2vc\include\ruby-2.7.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
107
+ <AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-3.2.1-vc\include\ruby-3.2.0\x64-mswin64_140;C:\msys64\usr\local\ruby-3.2.1-vc\include\ruby-3.2.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
106
108
  <Optimization>Disabled</Optimization>
107
109
  <PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
108
110
  <WarningLevel>Level3</WarningLevel>
109
111
  </ClCompile>
110
112
  <Link>
111
- <AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-2.7.2vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
112
- <AdditionalDependencies>x64-vcruntime140-ruby270.lib;%(AdditionalDependencies)</AdditionalDependencies>
113
+ <AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-3.2.1-vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
114
+ <AdditionalDependencies>x64-vcruntime140-ruby320.lib;%(AdditionalDependencies)</AdditionalDependencies>
113
115
  <ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
114
116
  <SubSystem>Console</SubSystem>
115
117
  </Link>
118
+ <ProjectReference>
119
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
120
+ </ProjectReference>
116
121
  </ItemDefinitionGroup>
117
122
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
118
123
  <ClCompile>
@@ -125,7 +130,6 @@
125
130
  </Link>
126
131
  </ItemDefinitionGroup>
127
132
  <ItemGroup>
128
- <ClInclude Include="..\rp_aggregate_call_tree.h" />
129
133
  <ClInclude Include="..\rp_allocation.h" />
130
134
  <ClInclude Include="..\rp_call_tree.h" />
131
135
  <ClInclude Include="..\rp_call_trees.h" />
@@ -137,7 +141,6 @@
137
141
  <ClInclude Include="..\ruby_prof.h" />
138
142
  </ItemGroup>
139
143
  <ItemGroup>
140
- <ClCompile Include="..\rp_aggregate_call_tree.c" />
141
144
  <ClCompile Include="..\rp_allocation.c" />
142
145
  <ClCompile Include="..\rp_call_tree.c" />
143
146
  <ClCompile Include="..\rp_call_trees.c" />
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
5
5
  <title>ruby-prof call tree</title>
6
- <style type="text/css">
6
+ <style>
7
7
  body {
8
8
  font-size: 70%;
9
9
  padding: 0;
@@ -19,6 +19,7 @@
19
19
  margin-bottom: 0px;
20
20
  padding-left: 0px;
21
21
  list-style-type: none;
22
+ font-weight: normal;
22
23
  }
23
24
 
24
25
  li {
@@ -96,4 +96,18 @@ module RubyProf
96
96
  def self.ensure_not_running!
97
97
  raise(RuntimeError, "RubyProf is already running") if running?
98
98
  end
99
+
100
+ class << self
101
+ extend Gem::Deprecate
102
+ deprecate :measure_mode, "Profile#measure_mode", 2023, 6
103
+ deprecate :measure_mode=, "Profile#measure_mode=", 2023, 6
104
+ deprecate :exclude_threads, "Profile#exclude_threads", 2023, 6
105
+ deprecate :exclude_threads=, "Profile#initialize", 2023, 6
106
+ deprecate :start, "Profile#start", 2023, 6
107
+ deprecate :pause, "Profile#pause", 2023, 6
108
+ deprecate :stop, "Profile#stop", 2023, 6
109
+ deprecate :resume, "Profile#resume", 2023, 6
110
+ deprecate :running?, "Profile#running?", 2023, 6
111
+ deprecate :profile, "Profile.profile", 2023, 6
112
+ end
99
113
  end
@@ -52,7 +52,14 @@ module RubyProf
52
52
  self.total_time - self.self_time - self.wait_time
53
53
  end
54
54
 
55
- # :enddoc:
55
+ def eql?(other)
56
+ self.hash == other.hash
57
+ end
58
+
59
+ def ==(other)
60
+ self.eql?(other)
61
+ end
62
+
56
63
  def <=>(other)
57
64
  if other.nil?
58
65
  -1
@@ -50,7 +50,7 @@ module RubyProf
50
50
  # options - Hash of print options. Note that each printer can
51
51
  # define its own set of options.
52
52
  #
53
- # :min_percent - Number 0 to 100 that specifes the minimum
53
+ # :min_percent - Number 0 to 100 that specifies the minimum
54
54
  # %self (the methods self time divided by the
55
55
  # overall total time) that a method must take
56
56
  # for it to be printed out in the report.
@@ -131,6 +131,7 @@ module RubyProf
131
131
 
132
132
  * MyObject#test - An instance method "test" of the class "MyObject"
133
133
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
134
+
134
135
  EOT
135
136
  end
136
137
  end
@@ -27,7 +27,7 @@ module RubyProf
27
27
 
28
28
  def determine_event_specification_and_value_scale
29
29
  @event_specification = "events: "
30
- case RubyProf.measure_mode
30
+ case @result.measure_mode
31
31
  when RubyProf::PROCESS_TIME
32
32
  @value_scale = RubyProf::CLOCKS_PER_SEC
33
33
  @event_specification << 'process_time'
@@ -68,8 +68,6 @@ module RubyProf
68
68
 
69
69
  def print_threads
70
70
  remove_subsidiary_files_from_previous_profile_runs
71
- # TODO: merge fibers of a given thread here, instead of relying
72
- # on the profiler to merge fibers.
73
71
  @result.threads.each do |thread|
74
72
  print_thread(thread)
75
73
  end
@@ -100,21 +98,17 @@ module RubyProf
100
98
  true
101
99
  end
102
100
 
103
- def base_name
104
- @options[:profile] || "profile"
105
- end
106
-
107
101
  def remove_subsidiary_files_from_previous_profile_runs
108
- pattern = [base_name, "callgrind.out", $$, "*"].join(".")
102
+ pattern = ["callgrind.out", $$, "*"].join(".")
109
103
  files = Dir.glob(File.join(path, pattern))
110
104
  FileUtils.rm_f(files)
111
105
  end
112
106
 
113
107
  def file_name_for_thread(thread)
114
108
  if thread.fiber_id == Fiber.current.object_id
115
- [base_name, "callgrind.out", $$].join(".")
109
+ ["callgrind.out", $$].join(".")
116
110
  else
117
- [base_name, "callgrind.out", $$, thread.fiber_id].join(".")
111
+ ["callgrind.out", $$, thread.fiber_id].join(".")
118
112
  end
119
113
  end
120
114
 
@@ -29,7 +29,7 @@ module RubyProf
29
29
  end
30
30
 
31
31
  # Creates a link to a method. Note that we do not create
32
- # links to methods which are under the min_perecent
32
+ # links to methods which are under the min_percent
33
33
  # specified by the user, since they will not be
34
34
  # printed out.
35
35
  def create_link(thread, overall_time, method)
@@ -6,16 +6,16 @@ module RubyProf
6
6
  # profile, a call stack profile and a graph profile.
7
7
  class MultiPrinter
8
8
  def initialize(result, printers = [:flat, :graph_html])
9
- @flat_printer = FlatPrinter.new(result) if printers.include?(:flat)
9
+ @flat_printer = printers.include?(:flat) ? FlatPrinter.new(result) : nil
10
10
 
11
- @graph_printer = GraphPrinter.new(result) if printers.include?(:graph)
12
- @graph_html_printer = GraphHtmlPrinter.new(result) if printers.include?(:graph_html)
11
+ @graph_printer = printers.include?(:graph) ? GraphPrinter.new(result) : nil
12
+ @graph_html_printer = printers.include?(:graph_html) ? GraphHtmlPrinter.new(result) : nil
13
13
 
14
- @tree_printer = CallTreePrinter.new(result) if printers.include?(:tree)
15
- @call_info_printer = CallInfoPrinter.new(result) if printers.include?(:call_tree)
14
+ @tree_printer = printers.include?(:tree) ? CallTreePrinter.new(result) : nil
15
+ @call_info_printer = printers.include?(:call_tree) ? CallInfoPrinter.new(result) : nil
16
16
 
17
- @stack_printer = CallStackPrinter.new(result) if printers.include?(:stack)
18
- @dot_printer = DotPrinter.new(result) if printers.include?(:dot)
17
+ @stack_printer = printers.include?(:stack) ? CallStackPrinter.new(result) : nil
18
+ @dot_printer = printers.include?(:dot) ? DotPrinter.new(result) : nil
19
19
  end
20
20
 
21
21
  def self.needs_dir?
@@ -28,7 +28,7 @@ module RubyProf
28
28
  def print(options)
29
29
  validate_print_params(options)
30
30
 
31
- @profile = options.delete(:profile) || "profile"
31
+ @file_name = options.delete(:profile) || "profile"
32
32
  @directory = options.delete(:path) || File.expand_path(".")
33
33
 
34
34
  print_to_flat(options) if @flat_printer
@@ -44,36 +44,36 @@ module RubyProf
44
44
 
45
45
  # the name of the flat profile file
46
46
  def flat_report
47
- "#{@directory}/#{@profile}.flat.txt"
47
+ "#{@directory}/#{@file_name}.flat.txt"
48
48
  end
49
49
 
50
50
  # the name of the graph profile file
51
51
  def graph_report
52
- "#{@directory}/#{@profile}.graph.txt"
52
+ "#{@directory}/#{@file_name}.graph.txt"
53
53
  end
54
54
 
55
55
  def graph_html_report
56
- "#{@directory}/#{@profile}.graph.html"
56
+ "#{@directory}/#{@file_name}.graph.html"
57
57
  end
58
58
 
59
59
  # the name of the callinfo profile file
60
60
  def call_info_report
61
- "#{@directory}/#{@profile}.call_tree.txt"
61
+ "#{@directory}/#{@file_name}.call_tree.txt"
62
62
  end
63
63
 
64
64
  # the name of the callgrind profile file
65
65
  def tree_report
66
- "#{@directory}/#{@profile}.callgrind.out.#{$$}"
66
+ "#{@directory}/#{@file_name}.callgrind.out.#{$$}"
67
67
  end
68
68
 
69
69
  # the name of the call stack profile file
70
70
  def stack_report
71
- "#{@directory}/#{@profile}.stack.html"
71
+ "#{@directory}/#{@file_name}.stack.html"
72
72
  end
73
73
 
74
74
  # the name of the call stack profile file
75
75
  def dot_report
76
- "#{@directory}/#{@profile}.dot"
76
+ "#{@directory}/#{@file_name}.dot"
77
77
  end
78
78
 
79
79
  def print_to_flat(options)
@@ -101,12 +101,12 @@ module RubyProf
101
101
  end
102
102
 
103
103
  def print_to_tree(options)
104
- @tree_printer.print(options.merge(:path => @directory, :profile => @profile))
104
+ @tree_printer.print(options.merge(:path => @directory, :profile => @file_name))
105
105
  end
106
106
 
107
107
  def print_to_stack(options)
108
108
  File.open(stack_report, "wb") do |file|
109
- @stack_printer.print(file, options.merge(:graph => "#{@profile}.graph.html"))
109
+ @stack_printer.print(file, options.merge(:graph => "#{@file_name}.graph.html"))
110
110
  end
111
111
  end
112
112
 
@@ -1,37 +1,70 @@
1
- # encoding: utf-8
2
-
3
- require 'ruby-prof/exclude_common_methods'
4
-
5
- module RubyProf
6
- class Profile
7
- # :nodoc:
8
- def measure_mode_string
9
- case self.measure_mode
10
- when WALL_TIME
11
- "wall_time"
12
- when PROCESS_TIME
13
- "process_time"
14
- when ALLOCATIONS
15
- "allocations"
16
- when MEMORY
17
- "memory"
18
- end
19
- end
20
-
21
- # Hides methods that, when represented as a call graph, have
22
- # extremely large in and out degrees and make navigation impossible.
23
- def exclude_common_methods!
24
- ExcludeCommonMethods.apply!(self)
25
- end
26
-
27
- def exclude_methods!(mod, *method_or_methods)
28
- [method_or_methods].flatten.each do |name|
29
- exclude_method!(mod, name)
30
- end
31
- end
32
-
33
- def exclude_singleton_methods!(mod, *method_or_methods)
34
- exclude_methods!(mod.singleton_class, *method_or_methods)
35
- end
36
- end
37
- end
1
+ # encoding: utf-8
2
+
3
+ require 'ruby-prof/exclude_common_methods'
4
+
5
+ module RubyProf
6
+ class Profile
7
+ def measure_mode_string
8
+ case self.measure_mode
9
+ when WALL_TIME
10
+ "wall_time"
11
+ when PROCESS_TIME
12
+ "process_time"
13
+ when ALLOCATIONS
14
+ "allocations"
15
+ when MEMORY
16
+ "memory"
17
+ end
18
+ end
19
+
20
+ # Hides methods that, when represented as a call graph, have
21
+ # extremely large in and out degrees and make navigation impossible.
22
+ def exclude_common_methods!
23
+ ExcludeCommonMethods.apply!(self)
24
+ end
25
+
26
+ def exclude_methods!(mod, *method_names)
27
+ [method_names].flatten.each do |method_name|
28
+ exclude_method!(mod, method_name)
29
+ end
30
+ end
31
+
32
+ def exclude_singleton_methods!(mod, *method_names)
33
+ exclude_methods!(mod.singleton_class, *method_names)
34
+ end
35
+
36
+ # call-seq:
37
+ # merge! -> self
38
+ #
39
+ # Merges RubyProf threads whose root call_trees reference the same target method. This is useful
40
+ # when profiling code that uses a main thread/fiber to distribute work to multiple workers.
41
+ # If there are tens or hundreds of workers, viewing results per worker thread/fiber can be
42
+ # overwhelming. Using +merge!+ will combine the worker times together into one result.
43
+ #
44
+ # Note the reported time will be much greater than the actual wall time. For example, if there
45
+ # are 10 workers that each run for 5 seconds, merged results will show one thread that
46
+ # ran for 50 seconds.
47
+ #
48
+ def merge!
49
+ # First group threads by their root call tree target (method). If the methods are
50
+ # different than there is nothing to merge
51
+ grouped = threads.group_by do |thread|
52
+ thread.call_tree.target
53
+ end
54
+
55
+ # For each target, get the first thread. Then loop over the remaining threads,
56
+ # and merge them into the first one and ten delete them. So we will be left with
57
+ # one thread per target.
58
+ grouped.each do |target, threads|
59
+ thread = threads.shift
60
+ threads.each do |other_thread|
61
+ thread.merge!(other_thread)
62
+ remove_thread(other_thread)
63
+ end
64
+ thread
65
+ end
66
+
67
+ self
68
+ end
69
+ end
70
+ end
@@ -6,7 +6,6 @@ module Rack
6
6
  def initialize(app, options = {})
7
7
  @app = app
8
8
  @options = options
9
- @options[:min_percent] ||= 1
10
9
 
11
10
  @tmpdir = options[:path] || Dir.tmpdir
12
11
  FileUtils.mkdir_p(@tmpdir)
@@ -26,14 +25,19 @@ module Rack
26
25
  if should_profile?(request.path)
27
26
  begin
28
27
  result = nil
29
- data = ::RubyProf::Profile.profile(profiling_options) do
28
+ profile = ::RubyProf::Profile.profile(profiling_options) do
30
29
  result = @app.call(env)
31
30
  end
32
31
 
32
+ if @options[:merge_fibers]
33
+ profile.merge!
34
+ end
35
+
36
+
33
37
  path = request.path.gsub('/', '-')
34
38
  path.slice!(0)
35
39
 
36
- print(data, path)
40
+ print(profile, path)
37
41
  result
38
42
  end
39
43
  else
@@ -54,39 +58,45 @@ module Rack
54
58
  end
55
59
 
56
60
  def profiling_options
57
- options = {}
58
- options[:measure_mode] = ::RubyProf.measure_mode
59
- options[:exclude_threads] =
60
- if @options[:ignore_existing_threads]
61
- Thread.list.select{|t| t != Thread.current}
62
- else
63
- ::RubyProf.exclude_threads
64
- end
65
- if @options[:request_thread_only]
66
- options[:include_threads] = [Thread.current]
61
+ result = {}
62
+ result[:measure_mode] = @options[:measure_mode] || ::RubyProf::WALL_TIME
63
+ result[:track_allocations] = @options[:track_allocations] || false
64
+ result[:exclude_common] = @options[:exclude_common] || false
65
+
66
+ if @options[:ignore_existing_threads]
67
+ result[:exclude_threads] = Thread.list.select {|thread| thread != Thread.current}
67
68
  end
68
- if @options[:merge_fibers]
69
- options[:merge_fibers] = true
69
+
70
+ if @options[:request_thread_only]
71
+ result[:include_threads] = [Thread.current]
70
72
  end
71
- options
73
+
74
+ result
75
+ end
76
+
77
+ def print_options
78
+ result = {}
79
+ result[:min_percent] = @options[:min_percent] || 1
80
+ result[:sort_method] = @options[:sort_method] || :total_time
81
+ result
72
82
  end
73
83
 
74
- def print(data, path)
84
+ def print(profile, path)
75
85
  @printer_klasses.each do |printer_klass, base_name|
76
- printer = printer_klass.new(data)
86
+ printer = printer_klass.new(profile)
77
87
 
78
88
  if base_name.respond_to?(:call)
79
89
  base_name = base_name.call
80
90
  end
81
91
 
82
92
  if printer_klass == ::RubyProf::MultiPrinter
83
- printer.print(@options.merge(:profile => "#{path}-#{base_name}"))
93
+ printer.print(print_options.merge(:profile => "#{path}-#{base_name}"))
84
94
  elsif printer_klass == ::RubyProf::CallTreePrinter
85
- printer.print(@options.merge(:profile => "#{path}-#{base_name}"))
95
+ printer.print(print_options.merge(:profile => "#{path}-#{base_name}"))
86
96
  else
87
97
  file_name = ::File.join(@tmpdir, "#{path}-#{base_name}")
88
98
  ::File.open(file_name, 'wb') do |file|
89
- printer.print(file, @options)
99
+ printer.print(file, print_options)
90
100
  end
91
101
  end
92
102
  end
@@ -1,3 +1,3 @@
1
1
  module RubyProf
2
- VERSION = "1.4.3"
2
+ VERSION = "1.6.3"
3
3
  end
data/lib/ruby-prof.rb CHANGED
@@ -6,7 +6,7 @@ begin
6
6
  version = Gem::Version.new(RUBY_VERSION)
7
7
  require "#{version.segments[0..1].join('.')}/ruby_prof.so"
8
8
  rescue LoadError
9
- require_relative "../ext/ruby_prof/ruby_prof.so"
9
+ require "ruby_prof.so"
10
10
  end
11
11
 
12
12
  require 'ruby-prof/version'
data/ruby-prof.gemspec CHANGED
@@ -36,7 +36,7 @@ EOF
36
36
  spec.files = Dir['CHANGES',
37
37
  'LICENSE',
38
38
  'Rakefile',
39
- 'README.rdoc',
39
+ 'README.md',
40
40
  'ruby-prof.gemspec',
41
41
  'bin/ruby-prof',
42
42
  'bin/ruby-prof-check-trace',
@@ -56,10 +56,9 @@ EOF
56
56
  'test/*.rb']
57
57
 
58
58
  spec.test_files = Dir["test/test_*.rb"]
59
- spec.required_ruby_version = '>= 2.5.0'
59
+ spec.required_ruby_version = '>= 2.7.0'
60
60
  spec.date = Time.now.strftime('%Y-%m-%d')
61
61
  spec.homepage = 'https://github.com/ruby-prof/ruby-prof'
62
62
  spec.add_development_dependency('minitest')
63
63
  spec.add_development_dependency('rake-compiler')
64
- spec.add_development_dependency('rdoc')
65
64
  end
@@ -5,6 +5,7 @@ require File.expand_path('../test_helper', __FILE__)
5
5
 
6
6
  class AbstractPrinterTest < TestCase
7
7
  def setup
8
+ super
8
9
  @result = {}
9
10
  @printer = RubyProf::AbstractPrinter.new(@result)
10
11
  @options = {}