ruby-prof 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +532 -0
  3. data/LICENSE +25 -0
  4. data/README.rdoc +5 -0
  5. data/Rakefile +110 -0
  6. data/bin/ruby-prof +380 -0
  7. data/bin/ruby-prof-check-trace +45 -0
  8. data/ext/ruby_prof/extconf.rb +36 -0
  9. data/ext/ruby_prof/rp_allocation.c +279 -0
  10. data/ext/ruby_prof/rp_allocation.h +31 -0
  11. data/ext/ruby_prof/rp_call_info.c +271 -0
  12. data/ext/ruby_prof/rp_call_info.h +35 -0
  13. data/ext/ruby_prof/rp_measure_allocations.c +52 -0
  14. data/ext/ruby_prof/rp_measure_memory.c +42 -0
  15. data/ext/ruby_prof/rp_measure_process_time.c +67 -0
  16. data/ext/ruby_prof/rp_measure_wall_time.c +62 -0
  17. data/ext/ruby_prof/rp_measurement.c +230 -0
  18. data/ext/ruby_prof/rp_measurement.h +50 -0
  19. data/ext/ruby_prof/rp_method.c +630 -0
  20. data/ext/ruby_prof/rp_method.h +70 -0
  21. data/ext/ruby_prof/rp_profile.c +895 -0
  22. data/ext/ruby_prof/rp_profile.h +37 -0
  23. data/ext/ruby_prof/rp_stack.c +196 -0
  24. data/ext/ruby_prof/rp_stack.h +56 -0
  25. data/ext/ruby_prof/rp_thread.c +337 -0
  26. data/ext/ruby_prof/rp_thread.h +36 -0
  27. data/ext/ruby_prof/ruby_prof.c +48 -0
  28. data/ext/ruby_prof/ruby_prof.h +17 -0
  29. data/ext/ruby_prof/vc/ruby_prof.sln +31 -0
  30. data/ext/ruby_prof/vc/ruby_prof.vcxproj +143 -0
  31. data/lib/ruby-prof.rb +52 -0
  32. data/lib/ruby-prof/assets/call_stack_printer.html.erb +713 -0
  33. data/lib/ruby-prof/assets/call_stack_printer.png +0 -0
  34. data/lib/ruby-prof/assets/graph_printer.html.erb +356 -0
  35. data/lib/ruby-prof/call_info.rb +57 -0
  36. data/lib/ruby-prof/call_info_visitor.rb +38 -0
  37. data/lib/ruby-prof/compatibility.rb +109 -0
  38. data/lib/ruby-prof/exclude_common_methods.rb +198 -0
  39. data/lib/ruby-prof/measurement.rb +14 -0
  40. data/lib/ruby-prof/method_info.rb +90 -0
  41. data/lib/ruby-prof/printers/abstract_printer.rb +127 -0
  42. data/lib/ruby-prof/printers/call_info_printer.rb +51 -0
  43. data/lib/ruby-prof/printers/call_stack_printer.rb +182 -0
  44. data/lib/ruby-prof/printers/call_tree_printer.rb +151 -0
  45. data/lib/ruby-prof/printers/dot_printer.rb +132 -0
  46. data/lib/ruby-prof/printers/flat_printer.rb +52 -0
  47. data/lib/ruby-prof/printers/graph_html_printer.rb +63 -0
  48. data/lib/ruby-prof/printers/graph_printer.rb +114 -0
  49. data/lib/ruby-prof/printers/multi_printer.rb +127 -0
  50. data/lib/ruby-prof/profile.rb +33 -0
  51. data/lib/ruby-prof/rack.rb +171 -0
  52. data/lib/ruby-prof/task.rb +147 -0
  53. data/lib/ruby-prof/thread.rb +35 -0
  54. data/lib/ruby-prof/version.rb +3 -0
  55. data/lib/unprof.rb +10 -0
  56. data/ruby-prof.gemspec +58 -0
  57. data/test/abstract_printer_test.rb +26 -0
  58. data/test/alias_test.rb +129 -0
  59. data/test/basic_test.rb +129 -0
  60. data/test/call_info_visitor_test.rb +31 -0
  61. data/test/duplicate_names_test.rb +32 -0
  62. data/test/dynamic_method_test.rb +53 -0
  63. data/test/enumerable_test.rb +21 -0
  64. data/test/exceptions_test.rb +24 -0
  65. data/test/exclude_methods_test.rb +146 -0
  66. data/test/exclude_threads_test.rb +53 -0
  67. data/test/fiber_test.rb +73 -0
  68. data/test/gc_test.rb +96 -0
  69. data/test/line_number_test.rb +161 -0
  70. data/test/marshal_test.rb +119 -0
  71. data/test/measure_allocations.rb +30 -0
  72. data/test/measure_allocations_test.rb +385 -0
  73. data/test/measure_allocations_trace_test.rb +385 -0
  74. data/test/measure_memory_trace_test.rb +756 -0
  75. data/test/measure_process_time_test.rb +849 -0
  76. data/test/measure_times.rb +54 -0
  77. data/test/measure_wall_time_test.rb +459 -0
  78. data/test/multi_printer_test.rb +71 -0
  79. data/test/no_method_class_test.rb +15 -0
  80. data/test/parser_timings.rb +24 -0
  81. data/test/pause_resume_test.rb +166 -0
  82. data/test/prime.rb +56 -0
  83. data/test/printer_call_stack_test.rb +28 -0
  84. data/test/printer_call_tree_test.rb +31 -0
  85. data/test/printer_flat_test.rb +68 -0
  86. data/test/printer_graph_html_test.rb +60 -0
  87. data/test/printer_graph_test.rb +41 -0
  88. data/test/printers_test.rb +141 -0
  89. data/test/printing_recursive_graph_test.rb +81 -0
  90. data/test/rack_test.rb +157 -0
  91. data/test/recursive_test.rb +210 -0
  92. data/test/singleton_test.rb +38 -0
  93. data/test/stack_printer_test.rb +64 -0
  94. data/test/start_stop_test.rb +109 -0
  95. data/test/test_helper.rb +24 -0
  96. data/test/thread_test.rb +144 -0
  97. data/test/unique_call_path_test.rb +190 -0
  98. data/test/yarv_test.rb +56 -0
  99. metadata +191 -0
@@ -0,0 +1,36 @@
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
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
+
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 switch_thread(void *profile, thread_data_t *thread_data, double measurement);
30
+ void threads_table_free(st_table *table);
31
+ VALUE prof_thread_wrap(thread_data_t *thread);
32
+ void prof_thread_mark(void *data);
33
+ int pause_thread(st_data_t key, st_data_t value, st_data_t data);
34
+ int unpause_thread(st_data_t key, st_data_t value, st_data_t data);
35
+
36
+ #endif //__RP_THREAD__
@@ -0,0 +1,48 @@
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
+ /* ruby-prof tracks the time spent executing every method in ruby programming.
5
+ The main players are:
6
+
7
+ profile_t - This represents 1 profile.
8
+ thread_data_t - Stores data about a single thread.
9
+ prof_stack_t - The method call stack in a particular thread
10
+ prof_method_t - Profiling information about each method
11
+ prof_call_info_t - Keeps track a method's callers and callees.
12
+
13
+ The final result is an instance of a profile object which has a hash table of
14
+ thread_data_t, keyed on the thread id. Each thread in turn has a hash table
15
+ of prof_method_t, keyed on the method id. A hash table is used for quick
16
+ look up when doing a profile. However, it is exposed to Ruby as an array.
17
+
18
+ Each prof_method_t has two hash tables, parent and children, of prof_call_info_t.
19
+ These objects keep track of a method's callers (who called the method) and its
20
+ callees (who the method called). These are keyed the method id, but once again,
21
+ are exposed to Ruby as arrays. Each prof_call_into_t maintains a pointer to the
22
+ caller or callee method, thereby making it easy to navigate through the call
23
+ hierarchy in ruby - which is very helpful for creating call graphs.
24
+ */
25
+
26
+ #include "ruby_prof.h"
27
+
28
+ #include "rp_allocation.h"
29
+ #include "rp_measurement.h"
30
+ #include "rp_method.h"
31
+ #include "rp_call_info.h"
32
+ #include "rp_profile.h"
33
+ #include "rp_stack.h"
34
+ #include "rp_thread.h"
35
+
36
+ VALUE mProf;
37
+
38
+ void Init_ruby_prof()
39
+ {
40
+ mProf = rb_define_module("RubyProf");
41
+
42
+ rp_init_allocation();
43
+ rp_init_call_info();
44
+ rp_init_measure();
45
+ rp_init_method_info();
46
+ rp_init_profile();
47
+ rp_init_thread();
48
+ }
@@ -0,0 +1,17 @@
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 __RUBY_PROF_H__
5
+ #define __RUBY_PROF_H__
6
+
7
+ #include <ruby.h>
8
+ #include <ruby/debug.h>
9
+ #include <stdio.h>
10
+ #include <stdbool.h>
11
+
12
+ extern VALUE mProf;
13
+
14
+ // This method is not exposed in Ruby header files - at least not as of Ruby 2.6.3 :(
15
+ extern size_t rb_obj_memsize_of(VALUE);
16
+
17
+ #endif //__RUBY_PROF_H__
@@ -0,0 +1,31 @@
1
+ 
2
+ Microsoft Visual Studio Solution File, Format Version 12.00
3
+ # Visual Studio Version 16
4
+ VisualStudioVersion = 16.0.28803.452
5
+ MinimumVisualStudioVersion = 10.0.40219.1
6
+ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ruby_prof", "ruby_prof.vcxproj", "{6B4978F4-3B5F-4D38-81A8-069EC28CC069}"
7
+ EndProject
8
+ Global
9
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
10
+ Debug|x64 = Debug|x64
11
+ Debug|x86 = Debug|x86
12
+ Release|x64 = Release|x64
13
+ Release|x86 = Release|x86
14
+ EndGlobalSection
15
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
16
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x64.ActiveCfg = Debug|x64
17
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x64.Build.0 = Debug|x64
18
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x86.ActiveCfg = Debug|Win32
19
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x86.Build.0 = Debug|Win32
20
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x64.ActiveCfg = Release|x64
21
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x64.Build.0 = Release|x64
22
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x86.ActiveCfg = Release|Win32
23
+ {6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x86.Build.0 = Release|Win32
24
+ EndGlobalSection
25
+ GlobalSection(SolutionProperties) = preSolution
26
+ HideSolutionNode = FALSE
27
+ EndGlobalSection
28
+ GlobalSection(ExtensibilityGlobals) = postSolution
29
+ SolutionGuid = {9E7746F2-2467-4738-9746-4472B5CBF1DA}
30
+ EndGlobalSection
31
+ EndGlobal
@@ -0,0 +1,143 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <ItemGroup Label="ProjectConfigurations">
4
+ <ProjectConfiguration Include="Debug|Win32">
5
+ <Configuration>Debug</Configuration>
6
+ <Platform>Win32</Platform>
7
+ </ProjectConfiguration>
8
+ <ProjectConfiguration Include="Debug|x64">
9
+ <Configuration>Debug</Configuration>
10
+ <Platform>x64</Platform>
11
+ </ProjectConfiguration>
12
+ <ProjectConfiguration Include="Release|Win32">
13
+ <Configuration>Release</Configuration>
14
+ <Platform>Win32</Platform>
15
+ </ProjectConfiguration>
16
+ <ProjectConfiguration Include="Release|x64">
17
+ <Configuration>Release</Configuration>
18
+ <Platform>x64</Platform>
19
+ </ProjectConfiguration>
20
+ </ItemGroup>
21
+ <PropertyGroup Label="Globals">
22
+ <ProjectGuid>{6B4978F4-3B5F-4D38-81A8-069EC28CC069}</ProjectGuid>
23
+ <Keyword>Win32Proj</Keyword>
24
+ <RootNamespace>ruby_prof</RootNamespace>
25
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
26
+ </PropertyGroup>
27
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
28
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
29
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
30
+ <UseDebugLibraries>true</UseDebugLibraries>
31
+ <CharacterSet>Unicode</CharacterSet>
32
+ </PropertyGroup>
33
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
34
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
35
+ <UseDebugLibraries>false</UseDebugLibraries>
36
+ <WholeProgramOptimization>true</WholeProgramOptimization>
37
+ <CharacterSet>Unicode</CharacterSet>
38
+ </PropertyGroup>
39
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
40
+ <PlatformToolset>v142</PlatformToolset>
41
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
42
+ </PropertyGroup>
43
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
44
+ <PlatformToolset>v142</PlatformToolset>
45
+ </PropertyGroup>
46
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
47
+ <ImportGroup Label="ExtensionSettings">
48
+ </ImportGroup>
49
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
50
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
51
+ </ImportGroup>
52
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
53
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
54
+ </ImportGroup>
55
+ <PropertyGroup Label="UserMacros" />
56
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
57
+ <LinkIncremental>true</LinkIncremental>
58
+ <OutDir>..\..\..\lib\2.0</OutDir>
59
+ <TargetExt>.so</TargetExt>
60
+ <TargetName>ruby_prof</TargetName>
61
+ </PropertyGroup>
62
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
63
+ <LinkIncremental>false</LinkIncremental>
64
+ </PropertyGroup>
65
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
66
+ <TargetExt>.so</TargetExt>
67
+ <OutDir>..\</OutDir>
68
+ </PropertyGroup>
69
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
70
+ <ClCompile>
71
+ <PrecompiledHeader>
72
+ </PrecompiledHeader>
73
+ <WarningLevel>Level3</WarningLevel>
74
+ <Optimization>Disabled</Optimization>
75
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;RUBY_PROF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
76
+ <AdditionalIncludeDirectories>C:\MinGW\local\ruby200vc\include\ruby-2.0.0;C:\MinGW\local\ruby200vc\include\ruby-2.0.0\i386-mswin32_100;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
77
+ </ClCompile>
78
+ <Link>
79
+ <SubSystem>Windows</SubSystem>
80
+ <GenerateDebugInformation>true</GenerateDebugInformation>
81
+ <AdditionalLibraryDirectories>C:\MinGW\local\ruby200vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
82
+ <AdditionalDependencies>msvcr100-ruby200.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
83
+ <ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
84
+ </Link>
85
+ </ItemDefinitionGroup>
86
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
87
+ <ClCompile>
88
+ <WarningLevel>Level3</WarningLevel>
89
+ <PrecompiledHeader>
90
+ </PrecompiledHeader>
91
+ <Optimization>MaxSpeed</Optimization>
92
+ <FunctionLevelLinking>true</FunctionLevelLinking>
93
+ <IntrinsicFunctions>true</IntrinsicFunctions>
94
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;RUBY_PROF_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
95
+ </ClCompile>
96
+ <Link>
97
+ <SubSystem>Windows</SubSystem>
98
+ <GenerateDebugInformation>true</GenerateDebugInformation>
99
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
100
+ <OptimizeReferences>true</OptimizeReferences>
101
+ </Link>
102
+ </ItemDefinitionGroup>
103
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
104
+ <ClCompile>
105
+ <AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-2.6.5vc\include\ruby-2.6.0\x64-mswin64_140;C:\msys64\usr\local\ruby-2.6.5vc\include\ruby-2.6.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
106
+ <Optimization>Disabled</Optimization>
107
+ <PreprocessorDefinitions>HAVE_RB_TRACEARG_CALLEE_ID;%(PreprocessorDefinitions)</PreprocessorDefinitions>
108
+ </ClCompile>
109
+ <Link>
110
+ <AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-2.6.5vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
111
+ <AdditionalDependencies>x64-vcruntime140-ruby260.lib;%(AdditionalDependencies)</AdditionalDependencies>
112
+ <ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
113
+ </Link>
114
+ </ItemDefinitionGroup>
115
+ <ItemGroup>
116
+ <ClInclude Include="..\rp_allocation.h" />
117
+ <ClInclude Include="..\rp_call_info.h" />
118
+ <ClInclude Include="..\rp_measurement.h" />
119
+ <ClInclude Include="..\rp_method.h" />
120
+ <ClInclude Include="..\rp_profile.h" />
121
+ <ClInclude Include="..\rp_stack.h" />
122
+ <ClInclude Include="..\rp_thread.h" />
123
+ <ClInclude Include="..\ruby_prof.h" />
124
+ <ClInclude Include="..\version.h" />
125
+ </ItemGroup>
126
+ <ItemGroup>
127
+ <ClCompile Include="..\rp_allocation.c" />
128
+ <ClCompile Include="..\rp_call_info.c" />
129
+ <ClCompile Include="..\rp_measurement.c" />
130
+ <ClCompile Include="..\rp_measure_allocations.c" />
131
+ <ClCompile Include="..\rp_measure_memory.c" />
132
+ <ClCompile Include="..\rp_measure_process_time.c" />
133
+ <ClCompile Include="..\rp_measure_wall_time.c" />
134
+ <ClCompile Include="..\rp_method.c" />
135
+ <ClCompile Include="..\rp_profile.c" />
136
+ <ClCompile Include="..\rp_stack.c" />
137
+ <ClCompile Include="..\rp_thread.c" />
138
+ <ClCompile Include="..\ruby_prof.c" />
139
+ </ItemGroup>
140
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
141
+ <ImportGroup Label="ExtensionTargets">
142
+ </ImportGroup>
143
+ </Project>
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ # Load the C-based binding.
4
+ begin
5
+ RUBY_VERSION =~ /(\d+\.\d+\.\d+)/
6
+ require "#{$1}/ruby_prof.so"
7
+ rescue LoadError
8
+ require "ruby_prof.so"
9
+ end
10
+
11
+ require 'ruby-prof/version'
12
+ require 'ruby-prof/call_info'
13
+ require 'ruby-prof/compatibility'
14
+ require 'ruby-prof/measurement'
15
+ require 'ruby-prof/method_info'
16
+ require 'ruby-prof/profile'
17
+ require 'ruby-prof/rack'
18
+ require 'ruby-prof/thread'
19
+
20
+ module RubyProf
21
+ autoload :CallInfoVisitor, 'ruby-prof/call_info_visitor'
22
+
23
+ autoload :AbstractPrinter, 'ruby-prof/printers/abstract_printer'
24
+ autoload :CallInfoPrinter, 'ruby-prof/printers/call_info_printer'
25
+ autoload :CallStackPrinter, 'ruby-prof/printers/call_stack_printer'
26
+ autoload :CallTreePrinter, 'ruby-prof/printers/call_tree_printer'
27
+ autoload :DotPrinter, 'ruby-prof/printers/dot_printer'
28
+ autoload :FlatPrinter, 'ruby-prof/printers/flat_printer'
29
+ autoload :GraphHtmlPrinter, 'ruby-prof/printers/graph_html_printer'
30
+ autoload :GraphPrinter, 'ruby-prof/printers/graph_printer'
31
+ autoload :MultiPrinter, 'ruby-prof/printers/multi_printer'
32
+
33
+ # :nodoc:
34
+ # Checks if the user specified the clock mode via
35
+ # the RUBY_PROF_MEASURE_MODE environment variable
36
+ def self.figure_measure_mode
37
+ case ENV["RUBY_PROF_MEASURE_MODE"]
38
+ when "wall", "wall_time"
39
+ RubyProf.measure_mode = RubyProf::WALL_TIME
40
+ when "allocations"
41
+ RubyProf.measure_mode = RubyProf::ALLOCATIONS
42
+ when "memory"
43
+ RubyProf.measure_mode = RubyProf::MEMORY
44
+ when "process", "process_time"
45
+ RubyProf.measure_mode = RubyProf::PROCESS_TIME
46
+ else
47
+ # the default is defined in the measure_mode reader
48
+ end
49
+ end
50
+ end
51
+
52
+ RubyProf::figure_measure_mode
@@ -0,0 +1,713 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
5
+ <title>ruby-prof call tree</title>
6
+ <style type="text/css">
7
+ body {
8
+ font-size: 70%;
9
+ padding: 0;
10
+ margin: 5px;
11
+ margin-right: 0px;
12
+ margin-left: 0px;
13
+ background: #ffffff;
14
+ }
15
+
16
+ ul {
17
+ margin-left: 0px;
18
+ margin-top: 0px;
19
+ margin-bottom: 0px;
20
+ padding-left: 0px;
21
+ list-style-type: none;
22
+ }
23
+
24
+ li {
25
+ margin-left: 11px;
26
+ padding: 0px;
27
+ white-space: nowrap;
28
+ border-top: 1px solid #cccccc;
29
+ border-left: 1px solid #cccccc;
30
+ border-bottom: none;
31
+ }
32
+
33
+ .thread {
34
+ margin-left: 11px;
35
+ background: #708090;
36
+ padding-top: 3px;
37
+ padding-left: 12px;
38
+ padding-bottom: 2px;
39
+ border-left: 1px solid #CCCCCC;
40
+ border-top: 1px solid #CCCCCC;
41
+ font-weight: bold;
42
+ }
43
+
44
+ .hidden {
45
+ display: none;
46
+ width: 0px;
47
+ height: 0px;
48
+ margin: 0px;
49
+ padding: 0px;
50
+ border-style: none;
51
+ }
52
+
53
+ .color01 {
54
+ background: #adbdeb
55
+ }
56
+
57
+ .color05 {
58
+ background: #9daddb
59
+ }
60
+
61
+ .color0 {
62
+ background: #8d9dcb
63
+ }
64
+
65
+ .color1 {
66
+ background: #89bccb
67
+ }
68
+
69
+ .color2 {
70
+ background: #56e3e7
71
+ }
72
+
73
+ .color3 {
74
+ background: #32cd70
75
+ }
76
+
77
+ .color4 {
78
+ background: #a3d53c
79
+ }
80
+
81
+ .color5 {
82
+ background: #c4cb34
83
+ }
84
+
85
+ .color6 {
86
+ background: #dcb66d
87
+ }
88
+
89
+ .color7 {
90
+ background: #cda59e
91
+ }
92
+
93
+ .color8 {
94
+ background: #be9d9c
95
+ }
96
+
97
+ .color9 {
98
+ background: #cf947a
99
+ }
100
+
101
+ #commands {
102
+ font-size: 10pt;
103
+ padding: 10px;
104
+ margin-left: 11px;
105
+ margin-bottom: 0px;
106
+ margin-top: 0px;
107
+ background: #708090;
108
+ border-top: 1px solid #cccccc;
109
+ border-left: 1px solid #cccccc;
110
+ border-bottom: none;
111
+ }
112
+
113
+ #titlebar {
114
+ font-size: 10pt;
115
+ padding: 10px;
116
+ margin-left: 11px;
117
+ margin-bottom: 0px;
118
+ margin-top: 10px;
119
+ background: #8090a0;
120
+ border-top: 1px solid #cccccc;
121
+ border-left: 1px solid #cccccc;
122
+ border-bottom: none;
123
+ }
124
+
125
+ #help {
126
+ font-size: 10pt;
127
+ padding: 10px;
128
+ margin-left: 11px;
129
+ margin-bottom: 0px;
130
+ margin-top: 0px;
131
+ background: #8090a0;
132
+ display: none;
133
+ border-top: 1px solid #cccccc;
134
+ border-left: 1px solid #cccccc;
135
+ border-bottom: none;
136
+ }
137
+
138
+ #sentinel {
139
+ height: 400px;
140
+ margin-left: 11px;
141
+ background: #8090a0;
142
+ border-top: 1px solid #cccccc;
143
+ border-left: 1px solid #cccccc;
144
+ border-bottom: none;
145
+ }
146
+
147
+ input {
148
+ margin-left: 10px;
149
+ }
150
+
151
+ .toggle {
152
+ background: url(data:image/png;base64,<%= base64_image %>) no-repeat left center;
153
+ float: left;
154
+ width: 9px;
155
+ height: 9px;
156
+ margin: 2px 1px 1px 1px;
157
+ }
158
+
159
+ .toggle.minus {
160
+ background-position: -9px 0;
161
+ }
162
+
163
+ .toggle.plus {
164
+ background-position: -18px 0;
165
+ }
166
+ </style>
167
+
168
+ <script type="text/javascript">
169
+ function rootNode()
170
+ {
171
+ return currentThread
172
+ }
173
+
174
+ function showUL(node, show)
175
+ {
176
+ Array.prototype.forEach.call(node.childNodes, function(child)
177
+ {
178
+ if (child.nodeName == 'LI')
179
+ toggle(child, show)
180
+ })
181
+ }
182
+
183
+ function findUlChild(li)
184
+ {
185
+ var ul = li.childNodes[2]
186
+ while (ul && ul.nodeName != "UL")
187
+ {
188
+ ul = ul.nextSibling
189
+ }
190
+ return ul
191
+ }
192
+
193
+ function isLeafNode(li)
194
+ {
195
+ var element = li.querySelector('a')
196
+ return element.classList.contains('empty')
197
+ }
198
+
199
+ function toggle(li, show)
200
+ {
201
+ if (isLeafNode(li))
202
+ return
203
+
204
+ var img = li.firstChild
205
+ img.className = 'toggle '
206
+ img.className += show ? 'minus' : 'plus'
207
+
208
+ var ul = findUlChild(li)
209
+ if (ul)
210
+ {
211
+ ul.style.display = show ? 'block' : 'none'
212
+ showUL(ul, true)
213
+ }
214
+ }
215
+
216
+ function toggleLI(li)
217
+ {
218
+ var img = li.firstChild
219
+ if (img.className.indexOf("minus") > -1)
220
+ toggle(li, false)
221
+ else
222
+ {
223
+ if (img.className.indexOf("plus") > -1)
224
+ toggle(li, true)
225
+ }
226
+ }
227
+
228
+ function aboveThreshold(text, threshold)
229
+ {
230
+ var match = text.match(/\d+[.,]\d+%/)
231
+ if (!match)
232
+ {
233
+ return true
234
+ }
235
+ else
236
+ {
237
+ var value = parseFloat(match[0].replace(/,/, '.'))
238
+ return value >= threshold
239
+ }
240
+ }
241
+
242
+ function setThresholdLI(li, threshold)
243
+ {
244
+ var a = li.querySelector('a')
245
+ var span = li.querySelector('span')
246
+ var ul = li.querySelector('ul')
247
+
248
+ var visible = aboveThreshold(span.textContent, threshold) ? 1 : 0
249
+
250
+ var count = 0
251
+ if (ul)
252
+ {
253
+ count = setThresholdUL(ul, threshold)
254
+ }
255
+
256
+ if (count > 0)
257
+ {
258
+ a.className = 'toggle minus'
259
+ }
260
+ else
261
+ {
262
+ a.className = 'toggle empty'
263
+ }
264
+
265
+ if (visible)
266
+ {
267
+ li.style.display = 'block'
268
+ } else
269
+ {
270
+ li.style.display = 'none'
271
+ }
272
+ return visible
273
+ }
274
+
275
+ function setThresholdUL(node, threshold)
276
+ {
277
+ var count = 0
278
+ Array.prototype.forEach.call(node.childNodes, function(child)
279
+ {
280
+ if (child.nodeName == 'LI')
281
+ count = count + setThresholdLI(child, threshold)
282
+ })
283
+
284
+ var visible = (count > 0) ? 1 : 0
285
+ if (visible)
286
+ {
287
+ node.style.display = 'block'
288
+ } else
289
+ {
290
+ node.style.display = 'none'
291
+ }
292
+ return visible
293
+ }
294
+
295
+ function toggleChildren(img, event)
296
+ {
297
+ event.cancelBubble = true
298
+ if (img.className.indexOf('empty') > -1)
299
+ return
300
+
301
+ var minus = (img.className.indexOf('minus') > -1)
302
+
303
+ if (minus)
304
+ {
305
+ img.className = 'toggle plus'
306
+ } else
307
+ img.className = 'toggle minus'
308
+
309
+ var li = img.parentNode
310
+ var ul = findUlChild(li)
311
+ if (ul)
312
+ {
313
+ if (minus)
314
+ ul.style.display = 'none'
315
+ else
316
+ ul.style.display = 'block'
317
+ }
318
+ if (minus)
319
+ moveSelectionIfNecessary(li)
320
+ }
321
+
322
+ function showChildren(li)
323
+ {
324
+ var img = li.firstChild
325
+ if (img.className.indexOf('empty') > -1)
326
+ return
327
+ img.className = 'toggle minus'
328
+
329
+ var ul = findUlChild(li)
330
+ if (ul)
331
+ {
332
+ ul.style.display = 'block'
333
+ }
334
+ }
335
+
336
+ function setThreshold()
337
+ {
338
+ var tv = document.getElementById("threshold").value
339
+ if (tv.match(/[0-9]+([.,][0-9]+)?/))
340
+ {
341
+ var f = parseFloat(tv.replace(/,/, '.'))
342
+ var threads = document.getElementsByName("thread")
343
+ var l = threads.length
344
+ for (var i = 0; i < l; i++)
345
+ {
346
+ setThresholdUL(threads[i], f)
347
+ }
348
+ var p = selectedNode
349
+ while (p && p.style.display == 'none')
350
+ p = p.parentNode.parentNode
351
+ if (p && p.nodeName == "LI")
352
+ selectNode(p)
353
+ } else
354
+ {
355
+ alert("Please specify a decimal number as threshold value!")
356
+ }
357
+ }
358
+
359
+ function expandAll(event)
360
+ {
361
+ toggleAll(event, true)
362
+ }
363
+
364
+ function collapseAll(event)
365
+ {
366
+ toggleAll(event, false)
367
+ selectNode(rootNode(), null)
368
+ }
369
+
370
+ function toggleAll(event, show)
371
+ {
372
+ event.cancelBubble = true
373
+ var threads = document.getElementsByName("thread")
374
+ var l = threads.length
375
+ for (var i = 0; i < l; i++)
376
+ {
377
+ showUL(threads[i], show)
378
+ }
379
+ }
380
+
381
+ function toggleHelp(node)
382
+ {
383
+ var help = document.getElementById("help")
384
+ if (node.value == "Show Help")
385
+ {
386
+ node.value = "Hide Help"
387
+ help.style.display = 'block'
388
+ } else
389
+ {
390
+ node.value = "Show Help"
391
+ help.style.display = 'none'
392
+ }
393
+ }
394
+
395
+ var selectedNode = null
396
+ var selectedColor = null
397
+ var selectedThread = null
398
+
399
+ function descendentOf(a, b)
400
+ {
401
+ while (a != b && b != null)
402
+ b = b.parentNode
403
+ return (a == b)
404
+ }
405
+
406
+ function moveSelectionIfNecessary(node)
407
+ {
408
+ if (descendentOf(node, selectedNode))
409
+ selectNode(node, null)
410
+ }
411
+
412
+ function selectNode(node, event)
413
+ {
414
+ if (event)
415
+ {
416
+ event.cancelBubble = true
417
+ thread = findThread(node)
418
+ selectThread(thread)
419
+ }
420
+ if (selectedNode)
421
+ {
422
+ selectedNode.style.background = selectedColor
423
+ }
424
+ selectedNode = node
425
+ selectedColor = node.style.background
426
+ selectedNode.style.background = "red"
427
+ selectedNode.scrollIntoView()
428
+ window.scrollBy(0, -400)
429
+ }
430
+
431
+ function moveUp()
432
+ {
433
+ move(selectedNode.previousSibling)
434
+ }
435
+
436
+ function moveDown()
437
+ {
438
+ move(selectedNode.nextSibling)
439
+ }
440
+
441
+ function move(p)
442
+ {
443
+ while (p && p.style.display == 'none')
444
+ p = p.nextSibling
445
+ if (p && p.nodeName == "LI")
446
+ {
447
+ selectNode(p, null)
448
+ }
449
+ }
450
+
451
+ function moveLeft()
452
+ {
453
+ var p = selectedNode.parentNode.parentNode
454
+ if (p && p.nodeName == "LI")
455
+ {
456
+ selectNode(p, null)
457
+ }
458
+ }
459
+
460
+ function moveRight()
461
+ {
462
+ if (!isLeafNode(selectedNode))
463
+ {
464
+ showChildren(selectedNode)
465
+ var ul = findUlChild(selectedNode)
466
+ if (ul)
467
+ {
468
+ selectNode(ul.firstChild, null)
469
+ }
470
+ }
471
+ }
472
+
473
+ function moveForward()
474
+ {
475
+ if (isLeafNode(selectedNode))
476
+ {
477
+ var p = selectedNode
478
+ while ((p.nextSibling == null || p.nextSibling.style.display == 'none') && p.nodeName == "LI")
479
+ {
480
+ p = p.parentNode.parentNode
481
+ }
482
+ if (p.nodeName == "LI")
483
+ selectNode(p.nextSibling, null)
484
+ } else
485
+ {
486
+ moveRight()
487
+ }
488
+ }
489
+
490
+ function isExpandedNode(li)
491
+ {
492
+ var img = li.firstChild
493
+ return (img.className.indexOf('minus') > -1)
494
+ }
495
+
496
+ function moveBackward()
497
+ {
498
+ var p = selectedNode
499
+ var q = p.previousSibling
500
+ while (q != null && q.style.display == 'none')
501
+ q = q.previousSibling
502
+ if (q == null)
503
+ {
504
+ p = p.parentNode.parentNode
505
+ } else
506
+ {
507
+ while (!isLeafNode(q) && isExpandedNode(q))
508
+ {
509
+ q = findUlChild(q).lastChild
510
+ while (q.style.display == 'none')
511
+ q = q.previousSibling
512
+ }
513
+ p = q
514
+ }
515
+ if (p.nodeName == "LI")
516
+ selectNode(p, null)
517
+ }
518
+
519
+ function moveHome()
520
+ {
521
+ selectNode(currentThread)
522
+ }
523
+
524
+ var currentThreadIndex = null
525
+
526
+ function findThread(node)
527
+ {
528
+ while (node && !node.parentNode.nodeName.match(/BODY|DIV/g))
529
+ {
530
+ node = node.parentNode
531
+ }
532
+ return node.firstChild
533
+ }
534
+
535
+ function selectThread(node)
536
+ {
537
+ var threads = document.getElementsByName("thread")
538
+ currentThread = node
539
+ for (var i = 0; i < threads.length; i++)
540
+ {
541
+ if (threads[i] == currentThread.parentNode)
542
+ currentThreadIndex = i
543
+ }
544
+ }
545
+
546
+ function nextThread()
547
+ {
548
+ var threads = document.getElementsByName("thread")
549
+ if (currentThreadIndex == threads.length - 1)
550
+ currentThreadIndex = 0
551
+ else
552
+ currentThreadIndex += 1
553
+ currentThread = threads[currentThreadIndex].firstChild
554
+ selectNode(currentThread, null)
555
+ }
556
+
557
+ function previousThread()
558
+ {
559
+ var threads = document.getElementsByName("thread")
560
+ if (currentThreadIndex == 0)
561
+ currentThreadIndex = threads.length - 1
562
+ else
563
+ currentThreadIndex -= 1
564
+ currentThread = threads[currentThreadIndex].firstChild
565
+ selectNode(currentThread, null)
566
+ }
567
+
568
+ function switchThread(node, event)
569
+ {
570
+ event.cancelBubble = true
571
+ selectThread(node.nextSibling.firstChild)
572
+ selectNode(currentThread, null)
573
+ }
574
+
575
+ function handleKeyEvent(event)
576
+ {
577
+ var code = event.charCode ? event.charCode : event.keyCode
578
+ var str = String.fromCharCode(code)
579
+ switch (str)
580
+ {
581
+ case "a":
582
+ moveLeft()
583
+ break
584
+ case "s":
585
+ moveDown()
586
+ break
587
+ case "d":
588
+ moveRight()
589
+ break
590
+ case "w":
591
+ moveUp()
592
+ break
593
+ case "f":
594
+ moveForward()
595
+ break
596
+ case "b":
597
+ moveBackward()
598
+ break
599
+ case "x":
600
+ toggleChildren(selectedNode.firstChild, event)
601
+ break
602
+ case "*":
603
+ toggleLI(selectedNode)
604
+ break
605
+ case "n":
606
+ nextThread()
607
+ break
608
+ case "h":
609
+ moveHome()
610
+ break
611
+ case "p":
612
+ previousThread()
613
+ break
614
+ }
615
+ }
616
+
617
+ document.onkeypress = function (event)
618
+ {
619
+ handleKeyEvent(event)
620
+ }
621
+
622
+ window.onload = function ()
623
+ {
624
+ var images = document.querySelectorAll(".toggle")
625
+ for (var i = 0; i < images.length; i++)
626
+ {
627
+ var img = images[i]
628
+ img.onclick = function (event)
629
+ {
630
+ toggleChildren(this, event)
631
+ return false
632
+ }
633
+ }
634
+ var divs = document.getElementsByTagName("div")
635
+ for (i = 0; i < divs.length; i++)
636
+ {
637
+ var div = divs[i]
638
+ if (div.className == "thread")
639
+ div.onclick = function (event)
640
+ {
641
+ switchThread(this, event)
642
+ }
643
+ }
644
+ var lis = document.getElementsByTagName("li")
645
+ for (var i = 0; i < lis.length; i++)
646
+ {
647
+ lis[i].onclick = function (event)
648
+ {
649
+ selectNode(this, event)
650
+ }
651
+ }
652
+
653
+ var threads = document.getElementsByName("thread")
654
+ currentThreadIndex = 0
655
+ currentThread = threads[0].querySelector('li')
656
+ selectNode(currentThread, null)
657
+ }
658
+ </script>
659
+
660
+ <% @overall_time = @result.threads.reduce(0) do |val, thread|
661
+ val += thread.total_time
662
+ end %>
663
+ </head>
664
+ <body>
665
+ <div style="display: inline-block;">
666
+ <div id="titlebar">
667
+ Call tree for application <strong><%= application %> <%= arguments %></strong><br/> Generated on <%= Time.now %>
668
+ with options <%= @options.inspect %><br/>
669
+ </div>
670
+ <div id="commands">
671
+ <span style="font-size: 11pt; font-weight: bold;">Threshold:</span>
672
+ <input value="1.0" size="3" id="threshold" type="text">
673
+ <input value="Apply" onclick="setThreshold();" type="submit">
674
+ <input value="Expand All" onclick="expandAll(event);" type="submit">
675
+ <input value="Collapse All" onclick="collapseAll(event);" type="submit">
676
+ <input value="Show Help" onclick="toggleHelp(this);" type="submit">
677
+ </div>
678
+ <ul style="display: none;" id="help">
679
+ <li>* indicates recursively called methods</li>
680
+ <li>Enter a decimal value <i>d</i> into the threshold field and click "Apply" to hide all nodes marked with time
681
+ values lower than <em>d</em>.
682
+ </li>
683
+ <li>Click on "Expand All" for full tree expansion.</li>
684
+ <li>Click on "Collapse All" to show only top level nodes.</li>
685
+ <li>Use a, s, d, w as in Quake or Urban Terror to navigate the tree.</li>
686
+ <li>Use f and b to navigate the tree in preorder forward and backwards.</li>
687
+ <li>Use x to toggle visibility of a subtree.</li>
688
+ <li>Use * to expand/collapse a whole subtree.</li>
689
+ <li>Use h to navigate to thread root.</li>
690
+ <li>Use n and p to navigate between threads.</li>
691
+ <li>Click on background to move focus to a subtree.</li>
692
+ </ul>
693
+
694
+ <% @result.threads.each do |thread| %>
695
+ <% thread_percent = 100 * (thread.total_time / @overall_time)
696
+ thread_info = "#{"%4.2f%%" % thread_percent} ~ #{@overall_time}" %>
697
+ <div class="thread">
698
+ <span>Thread: <%= thread.id %>, Fiber: <%= thread.fiber_id %> (<%= thread_info %>)</span>
699
+ <ul name="thread">
700
+ <% call_infos = thread.root_methods.map(&:callers).flatten %>
701
+ <% call_infos.each do |call_info| %>
702
+ <% visited = Set.new
703
+ output = StringIO.new('')
704
+ print_stack(output, visited, call_info, call_info.total_time) %>
705
+ <%= output.string %>
706
+ <% end %>
707
+ </ul>
708
+ </div>
709
+ <% end %>
710
+ <div id="sentinel"></div>
711
+ </div>
712
+ </body>
713
+ </html>