ruby-prof 1.3.1-x64-mingw32 → 1.4.0-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +12 -0
- data/Rakefile +2 -3
- data/ext/ruby_prof/extconf.rb +0 -30
- data/ext/ruby_prof/rp_call_trees.c +2 -2
- data/ext/ruby_prof/rp_measure_allocations.c +1 -1
- data/ext/ruby_prof/rp_measure_process_time.c +1 -1
- data/ext/ruby_prof/rp_measure_wall_time.c +1 -1
- data/ext/ruby_prof/rp_profile.c +6 -23
- data/ext/ruby_prof/rp_profile.h +0 -1
- data/ext/ruby_prof/rp_stack.c +2 -2
- data/ext/ruby_prof/vc/ruby_prof.sln +8 -0
- data/ext/ruby_prof/vc/ruby_prof.vcxproj +14 -3
- data/ext/ruby_prof/vc/ruby_prof_linux.vcxproj +132 -0
- data/lib/ruby-prof.rb +3 -2
- data/lib/ruby-prof/rack.rb +51 -130
- data/lib/ruby-prof/version.rb +1 -1
- data/test/client.rb +9 -0
- data/test/fiber_test.rb +55 -187
- data/test/rack_test.rb +0 -64
- data/test/server.rb +64 -0
- metadata +7 -6
- data/lib/2.6.5/ruby_prof.so +0 -0
- data/lib/2.7.0/ruby_prof.so +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 412103d1d1140848e4e171d010d603e98753a561ee75e62f217013e46848ac64
|
4
|
+
data.tar.gz: 686d22e4138b8fbe4a03f9ba8bfcd1e5523671fb5b4a3f46029676339a2736b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 374122cf537066f530a7df115941ba2d184229e9d88614ee5901430950d8d87b354fae3faddf9ebdf1fc5b7899b553892f9fa5604a559adc7ad336fb0db36b52
|
7
|
+
data.tar.gz: '058817625f1294f32d1aaba1dcc5f607d9375ec85177f69461be92a9fda972765f211e2d385514360779d6a034f15b4966de082c22d8017a3889c2a856aadd3b'
|
data/CHANGES
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
1.4.0 (2020-05-12)
|
2
|
+
=====================
|
3
|
+
* API change - remove merge_fibers support since it resulted in incorrect results or crashes (Charlie Savage)
|
4
|
+
* Fix crash when profiling memory usage (Charlie Savage)
|
5
|
+
* When tracing execution correctly print out newobj tracepoint events (Charlie Savage)
|
6
|
+
* Remove no longer needed code for building extensions (Charlie Savage)
|
7
|
+
|
8
|
+
1.3.2 (2020-04-19)
|
9
|
+
=====================
|
10
|
+
* Fix rack profiler so it is thread safe (Charlie Savage)
|
11
|
+
* Fix loading of prebuilt binaries on mingw64 to use Ruby's major and minor version (Charlie Savage)
|
12
|
+
|
1
13
|
1.3.1 (2020-03-11)
|
2
14
|
=====================
|
3
15
|
* Add max_percent and filter_by options to flat printer (Sean McGivern)
|
data/Rakefile
CHANGED
@@ -38,9 +38,9 @@ Rake::ExtensionTask.new do |ext|
|
|
38
38
|
ext.gem_spec = default_spec
|
39
39
|
ext.name = SO_NAME
|
40
40
|
ext.ext_dir = "ext/#{SO_NAME}"
|
41
|
-
ext.lib_dir = "lib/#{RUBY_VERSION}"
|
41
|
+
ext.lib_dir = "lib/#{Gem::Version.new(RUBY_VERSION).segments[0..1].join('.')}"
|
42
42
|
ext.cross_compile = true
|
43
|
-
ext.cross_platform = ['
|
43
|
+
ext.cross_platform = ['x64-mingw32']
|
44
44
|
end
|
45
45
|
|
46
46
|
# Rake task to build the default package
|
@@ -58,7 +58,6 @@ if RUBY_PLATFORM.match(/win32|mingw32/)
|
|
58
58
|
win_spec = default_spec.clone
|
59
59
|
win_spec.platform = Gem::Platform::CURRENT
|
60
60
|
win_spec.files += Dir.glob('lib/**/*.so')
|
61
|
-
win_spec.instance_variable_set(:@cache_file, nil) # Hack to work around gem issue
|
62
61
|
|
63
62
|
# Unset extensions
|
64
63
|
win_spec.extensions = nil
|
data/ext/ruby_prof/extconf.rb
CHANGED
@@ -1,35 +1,5 @@
|
|
1
1
|
require "mkmf"
|
2
2
|
|
3
|
-
if RUBY_ENGINE != "ruby"
|
4
|
-
STDERR.puts("\n\n***** This gem is MRI-specific. It does not support #{RUBY_ENGINE}. *****\n\n")
|
5
|
-
exit(1)
|
6
|
-
end
|
7
|
-
|
8
|
-
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.4.0')
|
9
|
-
STDERR.puts("\n\n***** Ruby version #{RUBY_VERSION} is no longer supported. Please upgrade to 2.3 or higher. *****\n\n")
|
10
|
-
exit(1)
|
11
|
-
end
|
12
|
-
|
13
|
-
# For the love of bitfields...
|
14
|
-
$CFLAGS += ' -std=c99'
|
15
|
-
|
16
|
-
# And since we are using C99
|
17
|
-
CONFIG['warnflags'].gsub!('-Wdeclaration-after-statement', '')
|
18
|
-
|
19
|
-
def add_define(name, value = nil)
|
20
|
-
if value
|
21
|
-
$defs.push("-D#{name}=#{value}")
|
22
|
-
else
|
23
|
-
$defs.push("-D#{name}")
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def windows?
|
28
|
-
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
|
29
|
-
end
|
30
|
-
|
31
|
-
add_define("RUBY_PROF_RUBY_VERSION", RUBY_VERSION.split('.')[0..2].inject(0){|v,d| v*100+d.to_i})
|
32
|
-
|
33
3
|
# This function was added in Ruby 2.5, so once Ruby 2.4 is no longer supported this can be removed
|
34
4
|
have_func('rb_tracearg_callee_id', ["ruby.h"])
|
35
5
|
|
@@ -219,7 +219,7 @@ VALUE prof_call_trees_callers(VALUE self)
|
|
219
219
|
}
|
220
220
|
}
|
221
221
|
|
222
|
-
VALUE result = rb_ary_new_capa(callers->num_entries);
|
222
|
+
VALUE result = rb_ary_new_capa((long)callers->num_entries);
|
223
223
|
rb_st_foreach(callers, prof_call_trees_collect_aggregates, result);
|
224
224
|
rb_st_free_table(callers);
|
225
225
|
return result;
|
@@ -239,7 +239,7 @@ VALUE prof_call_trees_callees(VALUE self)
|
|
239
239
|
rb_st_foreach((*call_tree)->children, prof_call_trees_collect_callees, (st_data_t)callees);
|
240
240
|
}
|
241
241
|
|
242
|
-
VALUE result = rb_ary_new_capa(callees->num_entries);
|
242
|
+
VALUE result = rb_ary_new_capa((long)callees->num_entries);
|
243
243
|
rb_st_foreach(callees, prof_call_trees_collect_aggregates, result);
|
244
244
|
rb_st_free_table(callees);
|
245
245
|
return result;
|
@@ -10,7 +10,7 @@ VALUE total_allocated_objects_key;
|
|
10
10
|
|
11
11
|
static double measure_allocations_via_gc_stats(rb_trace_arg_t* trace_arg)
|
12
12
|
{
|
13
|
-
return rb_gc_stat(total_allocated_objects_key);
|
13
|
+
return (double)rb_gc_stat(total_allocated_objects_key);
|
14
14
|
}
|
15
15
|
|
16
16
|
static double measure_allocations_via_tracing(rb_trace_arg_t* trace_arg)
|
@@ -24,7 +24,7 @@ static double measure_process_time(rb_trace_arg_t* trace_arg)
|
|
24
24
|
userTimeInt.LowPart = userTime.dwLowDateTime;
|
25
25
|
userTimeInt.HighPart = userTime.dwHighDateTime;
|
26
26
|
|
27
|
-
return sysTimeInt.QuadPart + userTimeInt.QuadPart;
|
27
|
+
return (double)(sysTimeInt.QuadPart + userTimeInt.QuadPart);
|
28
28
|
#elif !defined(CLOCK_PROCESS_CPUTIME_ID)
|
29
29
|
struct rusage usage;
|
30
30
|
getrusage(RUSAGE_SELF, &usage);
|
@@ -17,7 +17,7 @@ static double measure_wall_time(rb_trace_arg_t* trace_arg)
|
|
17
17
|
#if defined(_WIN32)
|
18
18
|
LARGE_INTEGER time;
|
19
19
|
QueryPerformanceCounter(&time);
|
20
|
-
return time.QuadPart;
|
20
|
+
return (double)time.QuadPart;
|
21
21
|
#elif defined(__APPLE__)
|
22
22
|
return mach_absolute_time();// * (uint64_t)mach_timebase.numer / (uint64_t)mach_timebase.denom;
|
23
23
|
#elif defined(__linux__)
|
data/ext/ruby_prof/rp_profile.c
CHANGED
@@ -65,25 +65,19 @@ static const char* get_event_name(rb_event_flag_t event)
|
|
65
65
|
return "fiber-switch";
|
66
66
|
case RUBY_EVENT_RAISE:
|
67
67
|
return "raise";
|
68
|
+
case RUBY_INTERNAL_EVENT_NEWOBJ:
|
69
|
+
return "newobj";
|
68
70
|
default:
|
69
71
|
return "unknown";
|
70
72
|
}
|
71
73
|
}
|
72
74
|
|
73
|
-
VALUE get_fiber(prof_profile_t* profile)
|
74
|
-
{
|
75
|
-
if (profile->merge_fibers)
|
76
|
-
return rb_thread_current();
|
77
|
-
else
|
78
|
-
return rb_fiber_current();
|
79
|
-
}
|
80
|
-
|
81
75
|
thread_data_t* check_fiber(prof_profile_t* profile, double measurement)
|
82
76
|
{
|
83
77
|
thread_data_t* result = NULL;
|
84
78
|
|
85
|
-
|
86
|
-
VALUE fiber =
|
79
|
+
// Get the current fiber
|
80
|
+
VALUE fiber = rb_fiber_current();
|
87
81
|
|
88
82
|
/* We need to switch the profiling context if we either had none before,
|
89
83
|
we don't merge fibers and the fiber ids differ, or the thread ids differ. */
|
@@ -172,7 +166,7 @@ prof_method_t* check_method(VALUE profile, rb_trace_arg_t* trace_arg, rb_event_f
|
|
172
166
|
static void prof_trace(prof_profile_t* profile, rb_trace_arg_t* trace_arg, double measurement)
|
173
167
|
{
|
174
168
|
static VALUE last_fiber = Qnil;
|
175
|
-
VALUE fiber =
|
169
|
+
VALUE fiber = rb_fiber_current();
|
176
170
|
|
177
171
|
rb_event_flag_t event = rb_tracearg_event_flag(trace_arg);
|
178
172
|
const char* event_name = get_event_name(event);
|
@@ -487,7 +481,6 @@ static VALUE prof_allocate(VALUE klass)
|
|
487
481
|
profile->include_threads_tbl = NULL;
|
488
482
|
profile->running = Qfalse;
|
489
483
|
profile->allow_exceptions = false;
|
490
|
-
profile->merge_fibers = false;
|
491
484
|
profile->exclude_methods_tbl = method_table_create();
|
492
485
|
profile->running = Qfalse;
|
493
486
|
profile->tracepoints = rb_ary_new();
|
@@ -529,9 +522,6 @@ prof_stop_threads(prof_profile_t* profile)
|
|
529
522
|
If not specified, defaults to RubyProf::WALL_TIME.
|
530
523
|
allow_exceptions: Whether to raise exceptions encountered during profiling,
|
531
524
|
or to suppress all exceptions during profiling
|
532
|
-
merge_fibers: Whether profiling data for a given thread's fibers should all be
|
533
|
-
subsumed under a single entry. Basically only useful to produce
|
534
|
-
callgrind profiles.
|
535
525
|
track_allocations: Whether to track object allocations while profiling. True or false.
|
536
526
|
exclude_common: Exclude common methods from the profile. True or false.
|
537
527
|
exclude_threads: Threads to exclude from the profiling results.
|
@@ -546,7 +536,6 @@ static VALUE prof_initialize(int argc, VALUE* argv, VALUE self)
|
|
546
536
|
VALUE include_threads = Qnil;
|
547
537
|
VALUE exclude_common = Qnil;
|
548
538
|
VALUE allow_exceptions = Qfalse;
|
549
|
-
VALUE merge_fibers = Qfalse;
|
550
539
|
VALUE track_allocations = Qfalse;
|
551
540
|
|
552
541
|
int i;
|
@@ -566,7 +555,6 @@ static VALUE prof_initialize(int argc, VALUE* argv, VALUE self)
|
|
566
555
|
mode = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("measure_mode")));
|
567
556
|
track_allocations = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("track_allocations")));
|
568
557
|
allow_exceptions = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("allow_exceptions")));
|
569
|
-
merge_fibers = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("merge_fibers")));
|
570
558
|
exclude_common = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_common")));
|
571
559
|
exclude_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("exclude_threads")));
|
572
560
|
include_threads = rb_hash_aref(mode_or_options, ID2SYM(rb_intern("include_threads")));
|
@@ -587,7 +575,6 @@ static VALUE prof_initialize(int argc, VALUE* argv, VALUE self)
|
|
587
575
|
}
|
588
576
|
profile->measurer = prof_get_measurer(NUM2INT(mode), track_allocations == Qtrue);
|
589
577
|
profile->allow_exceptions = (allow_exceptions == Qtrue);
|
590
|
-
profile->merge_fibers = (merge_fibers == Qtrue);
|
591
578
|
|
592
579
|
if (exclude_threads != Qnil)
|
593
580
|
{
|
@@ -679,7 +666,7 @@ static VALUE prof_start(VALUE self)
|
|
679
666
|
|
680
667
|
profile->running = Qtrue;
|
681
668
|
profile->paused = Qfalse;
|
682
|
-
profile->last_thread_data = threads_table_insert(profile,
|
669
|
+
profile->last_thread_data = threads_table_insert(profile, rb_fiber_current());
|
683
670
|
|
684
671
|
/* open trace file if environment wants it */
|
685
672
|
trace_file_name = getenv("RUBY_PROF_TRACE");
|
@@ -769,11 +756,7 @@ static VALUE prof_stop(VALUE self)
|
|
769
756
|
{
|
770
757
|
if (trace_file != stderr && trace_file != stdout)
|
771
758
|
{
|
772
|
-
#ifdef _MSC_VER
|
773
|
-
_fcloseall();
|
774
|
-
#else
|
775
759
|
fclose(trace_file);
|
776
|
-
#endif
|
777
760
|
}
|
778
761
|
trace_file = NULL;
|
779
762
|
}
|
data/ext/ruby_prof/rp_profile.h
CHANGED
data/ext/ruby_prof/rp_stack.c
CHANGED
@@ -129,7 +129,7 @@ prof_frame_t* prof_frame_push(prof_stack_t* stack, prof_call_tree_t* call_tree,
|
|
129
129
|
prof_frame_t* prof_frame_unshift(prof_stack_t* stack, prof_call_tree_t* parent_call_tree, prof_call_tree_t* call_tree, double measurement)
|
130
130
|
{
|
131
131
|
if (prof_stack_last(stack))
|
132
|
-
rb_raise(rb_eRuntimeError, "
|
132
|
+
rb_raise(rb_eRuntimeError, "Stack unshift can only be called with an empty stack");
|
133
133
|
|
134
134
|
parent_call_tree->measurement->total_time = call_tree->measurement->total_time;
|
135
135
|
parent_call_tree->measurement->self_time = 0;
|
@@ -187,7 +187,7 @@ prof_frame_t* prof_frame_pop(prof_stack_t* stack, double measurement)
|
|
187
187
|
|
188
188
|
prof_method_t* prof_find_method(prof_stack_t* stack, VALUE source_file, int source_line)
|
189
189
|
{
|
190
|
-
prof_frame_t* frame = stack
|
190
|
+
prof_frame_t* frame = prof_stack_last(stack);
|
191
191
|
while (frame >= stack->start)
|
192
192
|
{
|
193
193
|
if (!frame->call_tree)
|
@@ -7,16 +7,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ruby_prof", "ruby_prof.vcxp
|
|
7
7
|
EndProject
|
8
8
|
Global
|
9
9
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
10
|
+
Debug|ARM = Debug|ARM
|
11
|
+
Debug|ARM64 = Debug|ARM64
|
10
12
|
Debug|x64 = Debug|x64
|
11
13
|
Debug|x86 = Debug|x86
|
14
|
+
Release|ARM = Release|ARM
|
15
|
+
Release|ARM64 = Release|ARM64
|
12
16
|
Release|x64 = Release|x64
|
13
17
|
Release|x86 = Release|x86
|
14
18
|
EndGlobalSection
|
15
19
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
20
|
+
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|ARM.ActiveCfg = Debug|Win32
|
21
|
+
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|ARM64.ActiveCfg = Debug|Win32
|
16
22
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x64.ActiveCfg = Debug|x64
|
17
23
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x64.Build.0 = Debug|x64
|
18
24
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x86.ActiveCfg = Debug|Win32
|
19
25
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Debug|x86.Build.0 = Debug|Win32
|
26
|
+
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|ARM.ActiveCfg = Release|Win32
|
27
|
+
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|ARM64.ActiveCfg = Release|Win32
|
20
28
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x64.ActiveCfg = Release|x64
|
21
29
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x64.Build.0 = Release|x64
|
22
30
|
{6B4978F4-3B5F-4D38-81A8-069EC28CC069}.Release|x86.ActiveCfg = Release|Win32
|
@@ -64,7 +64,7 @@
|
|
64
64
|
</PropertyGroup>
|
65
65
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
66
66
|
<TargetExt>.so</TargetExt>
|
67
|
-
<OutDir>..\..\..\lib
|
67
|
+
<OutDir>..\..\..\lib\</OutDir>
|
68
68
|
</PropertyGroup>
|
69
69
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
70
70
|
<ClCompile>
|
@@ -102,17 +102,28 @@
|
|
102
102
|
</ItemDefinitionGroup>
|
103
103
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
104
104
|
<ClCompile>
|
105
|
-
<AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-2.7.
|
105
|
+
<AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-2.7.1vc\include\ruby-2.7.0\x64-mswin64_140;C:\msys64\usr\local\ruby-2.7.1vc\include\ruby-2.7.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
106
106
|
<Optimization>Disabled</Optimization>
|
107
107
|
<PreprocessorDefinitions>HAVE_RB_TRACEARG_CALLEE_ID;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
108
|
+
<WarningLevel>Level3</WarningLevel>
|
108
109
|
</ClCompile>
|
109
110
|
<Link>
|
110
|
-
<AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-2.7.
|
111
|
+
<AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-2.7.1vc\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
111
112
|
<AdditionalDependencies>x64-vcruntime140-ruby270.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
112
113
|
<ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
|
113
114
|
<SubSystem>Console</SubSystem>
|
114
115
|
</Link>
|
115
116
|
</ItemDefinitionGroup>
|
117
|
+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
118
|
+
<ClCompile>
|
119
|
+
<AdditionalIncludeDirectories>C:\msys64\usr\local\ruby-2.7.1vc\include\ruby-2.7.0\x64-mswin64_140;C:\msys64\usr\local\ruby-2.7.1vc\include\ruby-2.7.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
120
|
+
</ClCompile>
|
121
|
+
<Link>
|
122
|
+
<AdditionalLibraryDirectories>C:\msys64\usr\local\ruby-2.7.1vc\lib</AdditionalLibraryDirectories>
|
123
|
+
<AdditionalDependencies>x64-vcruntime140-ruby270.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>
|
124
|
+
<ModuleDefinitionFile>ruby_prof.def</ModuleDefinitionFile>
|
125
|
+
</Link>
|
126
|
+
</ItemDefinitionGroup>
|
116
127
|
<ItemGroup>
|
117
128
|
<ClInclude Include="..\rp_aggregate_call_tree.h" />
|
118
129
|
<ClInclude Include="..\rp_allocation.h" />
|
@@ -0,0 +1,132 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
3
|
+
<ItemGroup Label="ProjectConfigurations">
|
4
|
+
<ProjectConfiguration Include="Debug|ARM">
|
5
|
+
<Configuration>Debug</Configuration>
|
6
|
+
<Platform>ARM</Platform>
|
7
|
+
</ProjectConfiguration>
|
8
|
+
<ProjectConfiguration Include="Release|ARM">
|
9
|
+
<Configuration>Release</Configuration>
|
10
|
+
<Platform>ARM</Platform>
|
11
|
+
</ProjectConfiguration>
|
12
|
+
<ProjectConfiguration Include="Debug|ARM64">
|
13
|
+
<Configuration>Debug</Configuration>
|
14
|
+
<Platform>ARM64</Platform>
|
15
|
+
</ProjectConfiguration>
|
16
|
+
<ProjectConfiguration Include="Release|ARM64">
|
17
|
+
<Configuration>Release</Configuration>
|
18
|
+
<Platform>ARM64</Platform>
|
19
|
+
</ProjectConfiguration>
|
20
|
+
<ProjectConfiguration Include="Debug|x86">
|
21
|
+
<Configuration>Debug</Configuration>
|
22
|
+
<Platform>x86</Platform>
|
23
|
+
</ProjectConfiguration>
|
24
|
+
<ProjectConfiguration Include="Release|x86">
|
25
|
+
<Configuration>Release</Configuration>
|
26
|
+
<Platform>x86</Platform>
|
27
|
+
</ProjectConfiguration>
|
28
|
+
<ProjectConfiguration Include="Debug|x64">
|
29
|
+
<Configuration>Debug</Configuration>
|
30
|
+
<Platform>x64</Platform>
|
31
|
+
</ProjectConfiguration>
|
32
|
+
<ProjectConfiguration Include="Release|x64">
|
33
|
+
<Configuration>Release</Configuration>
|
34
|
+
<Platform>x64</Platform>
|
35
|
+
</ProjectConfiguration>
|
36
|
+
</ItemGroup>
|
37
|
+
<PropertyGroup Label="Globals">
|
38
|
+
<ProjectGuid>{fd79d690-c808-464c-a46b-01188609976e}</ProjectGuid>
|
39
|
+
<Keyword>Linux</Keyword>
|
40
|
+
<RootNamespace>Project1</RootNamespace>
|
41
|
+
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
|
42
|
+
<ApplicationType>Linux</ApplicationType>
|
43
|
+
<ApplicationTypeRevision>1.0</ApplicationTypeRevision>
|
44
|
+
<TargetLinuxPlatform>Generic</TargetLinuxPlatform>
|
45
|
+
<LinuxProjectType>{2238F9CD-F817-4ECC-BD14-2524D2669B35}</LinuxProjectType>
|
46
|
+
</PropertyGroup>
|
47
|
+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
48
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
49
|
+
<UseDebugLibraries>true</UseDebugLibraries>
|
50
|
+
</PropertyGroup>
|
51
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
52
|
+
<UseDebugLibraries>false</UseDebugLibraries>
|
53
|
+
</PropertyGroup>
|
54
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
55
|
+
<UseDebugLibraries>true</UseDebugLibraries>
|
56
|
+
</PropertyGroup>
|
57
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
58
|
+
<UseDebugLibraries>false</UseDebugLibraries>
|
59
|
+
</PropertyGroup>
|
60
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
61
|
+
<UseDebugLibraries>true</UseDebugLibraries>
|
62
|
+
<PlatformToolset>WSL_1_0</PlatformToolset>
|
63
|
+
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
64
|
+
</PropertyGroup>
|
65
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
66
|
+
<UseDebugLibraries>false</UseDebugLibraries>
|
67
|
+
<PlatformToolset>WSL_1_0</PlatformToolset>
|
68
|
+
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
69
|
+
</PropertyGroup>
|
70
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
|
71
|
+
<UseDebugLibraries>false</UseDebugLibraries>
|
72
|
+
</PropertyGroup>
|
73
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
|
74
|
+
<UseDebugLibraries>true</UseDebugLibraries>
|
75
|
+
</PropertyGroup>
|
76
|
+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
77
|
+
<ImportGroup Label="ExtensionSettings" />
|
78
|
+
<ImportGroup Label="Shared" />
|
79
|
+
<ImportGroup Label="PropertySheets" />
|
80
|
+
<PropertyGroup Label="UserMacros" />
|
81
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
82
|
+
<TargetName>ruby_prof</TargetName>
|
83
|
+
<OutDir>../usr/local/lib64/gems/ruby/ruby-prof-1.3.2/</OutDir>
|
84
|
+
</PropertyGroup>
|
85
|
+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
86
|
+
<TargetName>ruby_prof</TargetName>
|
87
|
+
</PropertyGroup>
|
88
|
+
<ItemGroup>
|
89
|
+
<ClInclude Include="..\rp_aggregate_call_tree.h" />
|
90
|
+
<ClInclude Include="..\rp_allocation.h" />
|
91
|
+
<ClInclude Include="..\rp_call_tree.h" />
|
92
|
+
<ClInclude Include="..\rp_call_trees.h" />
|
93
|
+
<ClInclude Include="..\rp_measurement.h" />
|
94
|
+
<ClInclude Include="..\rp_method.h" />
|
95
|
+
<ClInclude Include="..\rp_profile.h" />
|
96
|
+
<ClInclude Include="..\rp_stack.h" />
|
97
|
+
<ClInclude Include="..\rp_thread.h" />
|
98
|
+
<ClInclude Include="..\ruby_prof.h" />
|
99
|
+
</ItemGroup>
|
100
|
+
<ItemGroup>
|
101
|
+
<ClCompile Include="..\rp_aggregate_call_tree.c" />
|
102
|
+
<ClCompile Include="..\rp_allocation.c" />
|
103
|
+
<ClCompile Include="..\rp_call_tree.c" />
|
104
|
+
<ClCompile Include="..\rp_call_trees.c" />
|
105
|
+
<ClCompile Include="..\rp_measurement.c" />
|
106
|
+
<ClCompile Include="..\rp_measure_allocations.c" />
|
107
|
+
<ClCompile Include="..\rp_measure_memory.c" />
|
108
|
+
<ClCompile Include="..\rp_measure_process_time.c" />
|
109
|
+
<ClCompile Include="..\rp_measure_wall_time.c" />
|
110
|
+
<ClCompile Include="..\rp_method.c" />
|
111
|
+
<ClCompile Include="..\rp_profile.c" />
|
112
|
+
<ClCompile Include="..\rp_stack.c" />
|
113
|
+
<ClCompile Include="..\rp_thread.c" />
|
114
|
+
<ClCompile Include="..\ruby_prof.c" />
|
115
|
+
</ItemGroup>
|
116
|
+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
117
|
+
<Link>
|
118
|
+
<LibraryDependencies>ruby</LibraryDependencies>
|
119
|
+
</Link>
|
120
|
+
<ClCompile>
|
121
|
+
<PreprocessorDefinitions>
|
122
|
+
</PreprocessorDefinitions>
|
123
|
+
</ClCompile>
|
124
|
+
</ItemDefinitionGroup>
|
125
|
+
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
126
|
+
<Link>
|
127
|
+
<LibraryDependencies>ruby</LibraryDependencies>
|
128
|
+
</Link>
|
129
|
+
</ItemDefinitionGroup>
|
130
|
+
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
131
|
+
<ImportGroup Label="ExtensionTargets" />
|
132
|
+
</Project>
|
data/lib/ruby-prof.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require 'rubygems/version'
|
2
3
|
|
3
4
|
# Load the C-based binding.
|
4
5
|
begin
|
5
|
-
|
6
|
-
require "#{
|
6
|
+
version = Gem::Version.new(RUBY_VERSION)
|
7
|
+
require "#{version.segments[0..1].join('.')}/ruby_prof.so"
|
7
8
|
rescue LoadError
|
8
9
|
require "ruby_prof.so"
|
9
10
|
end
|
data/lib/ruby-prof/rack.rb
CHANGED
@@ -5,43 +5,37 @@ module Rack
|
|
5
5
|
class RubyProf
|
6
6
|
def initialize(app, options = {})
|
7
7
|
@app = app
|
8
|
+
@options = options
|
9
|
+
@options[:min_percent] ||= 1
|
8
10
|
|
9
|
-
options[:
|
11
|
+
@tmpdir = options[:path] || Dir.tmpdir
|
12
|
+
FileUtils.mkdir_p(@tmpdir)
|
10
13
|
|
11
|
-
options[:
|
12
|
-
|
14
|
+
@printer_klasses = @options[:printers] || {::RubyProf::FlatPrinter => 'flat.txt',
|
15
|
+
::RubyProf::GraphPrinter => 'graph.txt',
|
16
|
+
::RubyProf::GraphHtmlPrinter => 'graph.html',
|
17
|
+
::RubyProf::CallStackPrinter => 'call_stack.html'}
|
13
18
|
|
14
19
|
@skip_paths = options[:skip_paths] || [%r{^/assets}, %r{\.(css|js|png|jpeg|jpg|gif)$}]
|
15
20
|
@only_paths = options[:only_paths]
|
16
|
-
|
17
|
-
@max_requests = options[:max_requests]
|
18
|
-
|
19
|
-
@options = options
|
20
21
|
end
|
21
22
|
|
22
23
|
def call(env)
|
23
24
|
request = Rack::Request.new(env)
|
24
25
|
|
25
26
|
if should_profile?(request.path)
|
26
|
-
profiler.resume
|
27
27
|
begin
|
28
|
-
result =
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
result = nil
|
29
|
+
data = ::RubyProf::Profile.profile(profiling_options) do
|
30
|
+
result = @app.call(env)
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
nil
|
36
|
-
else
|
37
|
-
request.path.gsub('/', '-')[1..-1]
|
38
|
-
end
|
33
|
+
path = request.path.gsub('/', '-')
|
34
|
+
path.slice!(0)
|
39
35
|
|
40
|
-
|
41
|
-
|
36
|
+
print(data, path)
|
37
|
+
result
|
42
38
|
end
|
43
|
-
|
44
|
-
result
|
45
39
|
else
|
46
40
|
@app.call(env)
|
47
41
|
end
|
@@ -49,126 +43,53 @@ module Rack
|
|
49
43
|
|
50
44
|
private
|
51
45
|
|
52
|
-
|
53
|
-
|
54
|
-
@options = options
|
55
|
-
|
56
|
-
@profile = ::RubyProf::Profile.new(profiling_options)
|
57
|
-
@profile.start
|
58
|
-
@profile.pause
|
59
|
-
|
60
|
-
@printer_klasses = options[:printers] || default_printers
|
61
|
-
|
62
|
-
@tmpdir = options[:path]
|
63
|
-
|
64
|
-
@max_requests = options[:max_requests] || 1
|
65
|
-
@requests_count = 0
|
46
|
+
def should_profile?(path)
|
47
|
+
return false if paths_match?(path, @skip_paths)
|
66
48
|
|
67
|
-
|
68
|
-
|
69
|
-
# ongoing profile is not lost if the process shuts down before the
|
70
|
-
# max request count is reached
|
71
|
-
ObjectSpace.define_finalizer(self, proc { print! })
|
72
|
-
end
|
49
|
+
@only_paths ? paths_match?(path, @only_paths) : true
|
50
|
+
end
|
73
51
|
|
74
|
-
|
75
|
-
|
76
|
-
|
52
|
+
def paths_match?(path, paths)
|
53
|
+
paths.any? { |skip_path| skip_path =~ path }
|
54
|
+
end
|
77
55
|
|
78
|
-
|
79
|
-
|
80
|
-
|
56
|
+
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]
|
81
67
|
end
|
82
|
-
|
83
|
-
|
84
|
-
@requests_count >= @max_requests
|
68
|
+
if @options[:merge_fibers]
|
69
|
+
options[:merge_fibers] = true
|
85
70
|
end
|
71
|
+
options
|
72
|
+
end
|
86
73
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
data = @profile.stop
|
91
|
-
|
92
|
-
prefix ||= "multi-requests-#{@requests_count}"
|
93
|
-
|
94
|
-
@printer_klasses.each do |printer_klass, base_name|
|
95
|
-
printer = printer_klass.new(data)
|
96
|
-
|
97
|
-
if base_name.respond_to?(:call)
|
98
|
-
base_name = base_name.call
|
99
|
-
end
|
74
|
+
def print(data, path)
|
75
|
+
@printer_klasses.each do |printer_klass, base_name|
|
76
|
+
printer = printer_klass.new(data)
|
100
77
|
|
101
|
-
|
102
|
-
|
103
|
-
printer.print(@options.merge(:profile => "#{prefix}-#{base_name}"))
|
104
|
-
else
|
105
|
-
file_name = ::File.join(@tmpdir, "#{prefix}-#{base_name}")
|
106
|
-
::File.open(file_name, 'wb') do |file|
|
107
|
-
printer.print(file, @options)
|
108
|
-
end
|
109
|
-
end
|
78
|
+
if base_name.respond_to?(:call)
|
79
|
+
base_name = base_name.call
|
110
80
|
end
|
111
81
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
options[:exclude_threads] =
|
121
|
-
if @options[:ignore_existing_threads]
|
122
|
-
Thread.list.select{|t| t != Thread.current}
|
123
|
-
else
|
124
|
-
::RubyProf.exclude_threads
|
82
|
+
if printer_klass == ::RubyProf::MultiPrinter
|
83
|
+
printer.print(@options.merge(:profile => "#{path}-#{base_name}"))
|
84
|
+
elsif printer_klass == ::RubyProf::CallTreePrinter
|
85
|
+
printer.print(@options.merge(:profile => "#{path}-#{base_name}"))
|
86
|
+
else
|
87
|
+
file_name = ::File.join(@tmpdir, "#{path}-#{base_name}")
|
88
|
+
::File.open(file_name, 'wb') do |file|
|
89
|
+
printer.print(file, @options)
|
125
90
|
end
|
126
|
-
if @options[:request_thread_only]
|
127
|
-
options[:include_threads] = [Thread.current]
|
128
91
|
end
|
129
|
-
if @options[:merge_fibers]
|
130
|
-
options[:merge_fibers] = true
|
131
|
-
end
|
132
|
-
options
|
133
|
-
end
|
134
|
-
|
135
|
-
def default_printers
|
136
|
-
{::RubyProf::FlatPrinter => 'flat.txt',
|
137
|
-
::RubyProf::GraphPrinter => 'graph.txt',
|
138
|
-
::RubyProf::GraphHtmlPrinter => 'graph.html',
|
139
|
-
::RubyProf::CallStackPrinter => 'call_stack.html'}
|
140
92
|
end
|
141
93
|
end
|
142
|
-
|
143
|
-
def profiler
|
144
|
-
if aggregate_requests?
|
145
|
-
@@_shared_profiler ||= RackProfiler.new(@options)
|
146
|
-
else
|
147
|
-
@_profiler ||= RackProfiler.new(@options)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def delete_profiler!
|
152
|
-
if aggregate_requests?
|
153
|
-
@@_shared_profiler.print! if @@_shared_profiler
|
154
|
-
@@_shared_profiler = nil
|
155
|
-
else
|
156
|
-
@_profiler = nil
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def aggregate_requests?
|
161
|
-
!@max_requests.nil?
|
162
|
-
end
|
163
|
-
|
164
|
-
def should_profile?(path)
|
165
|
-
return false if paths_match?(path, @skip_paths)
|
166
|
-
|
167
|
-
@only_paths ? paths_match?(path, @only_paths) : true
|
168
|
-
end
|
169
|
-
|
170
|
-
def paths_match?(path, paths)
|
171
|
-
paths.any? { |skip_path| skip_path =~ path }
|
172
|
-
end
|
173
94
|
end
|
174
95
|
end
|
data/lib/ruby-prof/version.rb
CHANGED
data/test/client.rb
ADDED
data/test/fiber_test.rb
CHANGED
@@ -8,254 +8,122 @@ require 'set'
|
|
8
8
|
|
9
9
|
# -- Tests ----
|
10
10
|
class FiberTest < TestCase
|
11
|
+
|
11
12
|
def enumerator_with_fibers
|
12
|
-
@fiber_ids << Fiber.current.object_id
|
13
13
|
enum = Enumerator.new do |yielder|
|
14
14
|
[1,2].each do |x|
|
15
|
-
@fiber_ids << Fiber.current.object_id
|
16
|
-
sleep 0.1
|
17
15
|
yielder.yield x
|
18
16
|
end
|
19
17
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
18
|
+
|
19
|
+
enum.next
|
20
|
+
enum.next
|
21
|
+
end
|
22
|
+
|
23
|
+
def fiber_yield_resume
|
24
|
+
fiber = Fiber.new do
|
25
|
+
Fiber.yield 1
|
26
|
+
Fiber.yield 2
|
27
|
+
end
|
28
|
+
|
29
|
+
fiber.resume
|
30
|
+
fiber.resume
|
28
31
|
end
|
29
32
|
|
30
33
|
def setup
|
31
34
|
# Need to use wall time for this test due to the sleep calls
|
32
35
|
RubyProf::measure_mode = RubyProf::WALL_TIME
|
33
|
-
@fiber_ids = Set.new
|
34
|
-
@root_fiber = Fiber.current.object_id
|
35
|
-
@thread_id = Thread.current.object_id
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_fibers
|
39
39
|
result = RubyProf.profile { enumerator_with_fibers }
|
40
40
|
|
41
|
-
|
42
|
-
assert_equal(2, result.threads.length)
|
43
|
-
assert_equal([@thread_id], result.threads.map(&:id).uniq)
|
44
|
-
assert_equal(@fiber_ids, Set.new(profiled_fiber_ids))
|
41
|
+
assert_equal(2, result.threads.size)
|
45
42
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
assert_in_delta(0.33, root_fiber_profile.total_time, 0.05)
|
50
|
-
assert_in_delta(0.33, enum_fiber_profile.total_time, 0.05)
|
51
|
-
|
52
|
-
methods = result.threads[0].methods.sort.reverse
|
53
|
-
assert_equal(12, methods.count)
|
43
|
+
thread1 = result.threads[0]
|
44
|
+
methods = thread1.methods.sort.reverse
|
45
|
+
assert_equal(5, methods.count)
|
54
46
|
|
55
47
|
method = methods[0]
|
56
48
|
assert_equal('FiberTest#test_fibers', method.full_name)
|
57
49
|
assert_equal(1, method.called)
|
58
|
-
assert_in_delta(0.33, method.total_time, 0.05)
|
59
|
-
assert_in_delta(0, method.self_time, 0.05)
|
60
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
61
|
-
assert_in_delta(0.33, method.children_time, 0.05)
|
62
50
|
|
63
51
|
method = methods[1]
|
64
52
|
assert_equal('FiberTest#enumerator_with_fibers', method.full_name)
|
65
53
|
assert_equal(1, method.called)
|
66
|
-
assert_in_delta(0.33, method.total_time, 0.05)
|
67
|
-
assert_in_delta(0, method.self_time, 0.05)
|
68
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
69
|
-
assert_in_delta(0.33, method.children_time, 0.05)
|
70
54
|
|
71
55
|
method = methods[2]
|
72
56
|
assert_equal('Enumerator#next', method.full_name)
|
73
|
-
assert_equal(
|
74
|
-
assert_in_delta(0.22, method.total_time, 0.05)
|
75
|
-
assert_in_delta(0, method.self_time, 0.05)
|
76
|
-
assert_in_delta(0.22, method.wait_time, 0.05)
|
77
|
-
assert_in_delta(0, method.children_time, 0.05)
|
57
|
+
assert_equal(2, method.called)
|
78
58
|
|
79
59
|
method = methods[3]
|
80
|
-
assert_equal('Kernel#sleep', method.full_name)
|
81
|
-
assert_equal(1, method.called)
|
82
|
-
assert_in_delta(0.11, method.total_time, 0.05)
|
83
|
-
assert_in_delta(0.11, method.self_time, 0.05)
|
84
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
85
|
-
assert_in_delta(0, method.children_time, 0.05)
|
86
|
-
|
87
|
-
# Since these methods have such short times their order is a bit indeterminate
|
88
|
-
method = methods.detect {|a_method| a_method.full_name == 'Class#new'}
|
89
60
|
assert_equal('Class#new', method.full_name)
|
90
61
|
assert_equal(1, method.called)
|
91
|
-
assert_in_delta(0, method.total_time, 0.05)
|
92
|
-
assert_in_delta(0, method.self_time, 0.05)
|
93
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
94
|
-
assert_in_delta(0, method.children_time, 0.05)
|
95
|
-
|
96
|
-
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
97
|
-
method = methods.detect {|a_method| a_method.full_name == 'Set#<<'}
|
98
|
-
assert_equal('Set#<<', method.full_name)
|
99
|
-
assert_equal(1, method.called)
|
100
|
-
assert_in_delta(0, method.total_time, 0.05)
|
101
|
-
assert_in_delta(0, method.self_time, 0.05)
|
102
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
103
|
-
assert_in_delta(0, method.children_time, 0.05)
|
104
|
-
end
|
105
62
|
|
106
|
-
method = methods
|
107
|
-
assert_equal('
|
63
|
+
method = methods[4]
|
64
|
+
assert_equal('Enumerator#initialize', method.full_name)
|
108
65
|
assert_equal(1, method.called)
|
109
|
-
assert_in_delta(0, method.total_time, 0.05)
|
110
|
-
assert_in_delta(0, method.self_time, 0.05)
|
111
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
112
|
-
assert_in_delta(0, method.children_time, 0.05)
|
113
66
|
|
114
|
-
|
115
|
-
|
116
|
-
assert_equal(
|
117
|
-
assert_in_delta(0, method.total_time, 0.05)
|
118
|
-
assert_in_delta(0, method.self_time, 0.05)
|
119
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
120
|
-
assert_in_delta(0, method.children_time, 0.05)
|
67
|
+
thread2 = result.threads[1]
|
68
|
+
methods = thread2.methods.sort.reverse
|
69
|
+
assert_equal(4, methods.count)
|
121
70
|
|
122
|
-
method = methods
|
123
|
-
assert_equal('
|
71
|
+
method = methods[0]
|
72
|
+
assert_equal('Enumerator#each', method.full_name)
|
124
73
|
assert_equal(1, method.called)
|
125
|
-
assert_in_delta(0, method.total_time, 0.05)
|
126
|
-
assert_in_delta(0, method.self_time, 0.05)
|
127
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
128
|
-
assert_in_delta(0, method.children_time, 0.05)
|
129
74
|
|
130
|
-
method = methods
|
131
|
-
assert_equal('
|
75
|
+
method = methods[1]
|
76
|
+
assert_equal('Enumerator::Generator#each', method.full_name)
|
132
77
|
assert_equal(1, method.called)
|
133
|
-
assert_in_delta(0, method.total_time, 0.05)
|
134
|
-
assert_in_delta(0, method.self_time, 0.05)
|
135
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
136
|
-
assert_in_delta(0, method.children_time, 0.05)
|
137
78
|
|
138
|
-
method = methods
|
139
|
-
assert_equal('
|
79
|
+
method = methods[2]
|
80
|
+
assert_equal('Array#each', method.full_name)
|
140
81
|
assert_equal(1, method.called)
|
141
|
-
assert_in_delta(0, method.total_time, 0.05)
|
142
|
-
assert_in_delta(0, method.self_time, 0.05)
|
143
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
144
|
-
assert_in_delta(0, method.children_time, 0.05)
|
145
82
|
|
146
|
-
method = methods
|
147
|
-
assert_equal('Enumerator#
|
148
|
-
assert_equal(
|
149
|
-
|
150
|
-
assert_in_delta(0, method.self_time, 0.05)
|
151
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
152
|
-
assert_in_delta(0, method.children_time, 0.05)
|
83
|
+
method = methods[3]
|
84
|
+
assert_equal('Enumerator::Yielder#yield', method.full_name)
|
85
|
+
assert_equal(2, method.called)
|
86
|
+
end
|
153
87
|
|
154
|
-
|
88
|
+
def test_fiber_resume
|
89
|
+
result = RubyProf.profile { fiber_yield_resume }
|
155
90
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
91
|
+
assert_equal(2, result.threads.size)
|
92
|
+
|
93
|
+
thread1 = result.threads[0]
|
94
|
+
methods = thread1.methods.sort.reverse
|
95
|
+
assert_equal(5, methods.count)
|
161
96
|
|
162
97
|
method = methods[0]
|
163
|
-
assert_equal('
|
98
|
+
assert_equal('FiberTest#test_fiber_resume', method.full_name)
|
164
99
|
assert_equal(1, method.called)
|
165
|
-
assert_in_delta(0.33, method.total_time, 0.05)
|
166
|
-
assert_in_delta(0, method.self_time, 0.05)
|
167
|
-
assert_in_delta(0.11, method.wait_time, 0.05)
|
168
|
-
assert_in_delta(0.22, method.children_time, 0.05)
|
169
100
|
|
170
101
|
method = methods[1]
|
171
|
-
assert_equal('
|
102
|
+
assert_equal('FiberTest#fiber_yield_resume', method.full_name)
|
172
103
|
assert_equal(1, method.called)
|
173
|
-
assert_in_delta(0.22, method.total_time, 0.05)
|
174
|
-
assert_in_delta(0, method.self_time, 0.05)
|
175
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
176
|
-
assert_in_delta(0.22, method.children_time, 0.05)
|
177
104
|
|
178
105
|
method = methods[2]
|
179
|
-
assert_equal('
|
180
|
-
assert_equal(
|
181
|
-
assert_in_delta(0.22, method.total_time, 0.05)
|
182
|
-
assert_in_delta(0, method.self_time, 0.05)
|
183
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
184
|
-
assert_in_delta(0.22, method.children_time, 0.05)
|
106
|
+
assert_equal('Fiber#resume', method.full_name)
|
107
|
+
assert_equal(2, method.called)
|
185
108
|
|
186
109
|
method = methods[3]
|
187
|
-
assert_equal('
|
110
|
+
assert_equal('Class#new', method.full_name)
|
188
111
|
assert_equal(1, method.called)
|
189
|
-
assert_in_delta(0.22, method.total_time, 0.05)
|
190
|
-
assert_in_delta(0, method.self_time, 0.05)
|
191
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
192
|
-
assert_in_delta(0.22, method.children_time, 0.05)
|
193
112
|
|
194
113
|
method = methods[4]
|
195
|
-
assert_equal('
|
196
|
-
assert_equal(2, method.called)
|
197
|
-
assert_in_delta(0.22, method.total_time, 0.05)
|
198
|
-
assert_in_delta(0.22, method.self_time, 0.05)
|
199
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
200
|
-
assert_in_delta(0, method.children_time, 0.05)
|
201
|
-
|
202
|
-
# Since these methods have such short times their order is a bit indeterminate
|
203
|
-
method = methods.detect {|a_method| a_method.full_name == 'Exception#initialize'}
|
204
|
-
assert_equal('Exception#initialize', method.full_name)
|
114
|
+
assert_equal('Fiber#initialize', method.full_name)
|
205
115
|
assert_equal(1, method.called)
|
206
|
-
assert_in_delta(0, method.total_time, 0.05)
|
207
|
-
assert_in_delta(0, method.self_time, 0.05)
|
208
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
209
|
-
assert_in_delta(0, method.children_time, 0.05)
|
210
|
-
|
211
|
-
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
212
|
-
method = methods.detect {|a_method| a_method.full_name == 'Set#<<'}
|
213
|
-
assert_equal('Set#<<', method.full_name)
|
214
|
-
assert_equal(2, method.called)
|
215
|
-
assert_in_delta(0, method.total_time, 0.05)
|
216
|
-
assert_in_delta(0, method.self_time, 0.05)
|
217
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
218
|
-
assert_in_delta(0, method.children_time, 0.05)
|
219
|
-
end
|
220
116
|
|
221
|
-
|
222
|
-
|
223
|
-
assert_equal(2,
|
224
|
-
assert_in_delta(0, method.total_time, 0.05)
|
225
|
-
assert_in_delta(0, method.self_time, 0.05)
|
226
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
227
|
-
assert_in_delta(0, method.children_time, 0.05)
|
117
|
+
thread1 = result.threads[1]
|
118
|
+
methods = thread1.methods.sort.reverse
|
119
|
+
assert_equal(2, methods.count)
|
228
120
|
|
229
|
-
method = methods
|
230
|
-
assert_equal('
|
231
|
-
assert_equal(
|
232
|
-
assert_in_delta(0, method.total_time, 0.05)
|
233
|
-
assert_in_delta(0, method.self_time, 0.05)
|
234
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
235
|
-
assert_in_delta(0, method.children_time, 0.05)
|
121
|
+
method = methods[0]
|
122
|
+
assert_equal('FiberTest#fiber_yield_resume', method.full_name)
|
123
|
+
assert_equal(1, method.called)
|
236
124
|
|
237
|
-
method = methods
|
238
|
-
assert_equal('<Class::Fiber>#
|
125
|
+
method = methods[1]
|
126
|
+
assert_equal('<Class::Fiber>#yield', method.full_name)
|
239
127
|
assert_equal(2, method.called)
|
240
|
-
assert_in_delta(0, method.total_time, 0.05)
|
241
|
-
assert_in_delta(0, method.self_time, 0.05)
|
242
|
-
assert_in_delta(0, method.wait_time, 0.05)
|
243
|
-
assert_in_delta(0, method.children_time, 0.05)
|
244
|
-
end
|
245
|
-
|
246
|
-
def test_merged_fibers
|
247
|
-
result = RubyProf.profile(merge_fibers: true) { enumerator_with_fibers }
|
248
|
-
|
249
|
-
assert_equal(1, result.threads.length)
|
250
|
-
|
251
|
-
thread = result.threads.first
|
252
|
-
assert_equal(thread.id, thread.fiber_id)
|
253
|
-
assert_in_delta(0.3, thread.total_time, 0.05)
|
254
|
-
|
255
|
-
assert(method_next = thread.methods.detect{|m| m.full_name == "Enumerator#next"})
|
256
|
-
assert(method_each = thread.methods.detect{|m| m.full_name == "Enumerator#each"})
|
257
|
-
|
258
|
-
assert_in_delta(0.2, method_next.total_time, 0.05)
|
259
|
-
assert_in_delta(0.2, method_each.total_time, 0.05)
|
260
128
|
end
|
261
129
|
end
|
data/test/rack_test.rb
CHANGED
@@ -24,16 +24,6 @@ module Rack
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
module Rack
|
28
|
-
class RubyProf
|
29
|
-
attr_reader :_profiler
|
30
|
-
|
31
|
-
def public_delete_profiler!
|
32
|
-
delete_profiler!
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
27
|
class RackTest < TestCase
|
38
28
|
def test_create_print_path
|
39
29
|
path = Dir.mktmpdir
|
@@ -100,58 +90,4 @@ class RackTest < TestCase
|
|
100
90
|
file_path = ::File.join(path, 'path-to-resource.json-dynamic.txt')
|
101
91
|
assert(File.exist?(file_path))
|
102
92
|
end
|
103
|
-
|
104
|
-
def test_works_for_multiple_requests
|
105
|
-
path = Dir.mktmpdir
|
106
|
-
|
107
|
-
adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :max_requests => 2)
|
108
|
-
|
109
|
-
# make a 1st request, and check that this didn't create any files
|
110
|
-
adapter.call(:fake_env)
|
111
|
-
assert(Dir["#{path}/*"].empty?)
|
112
|
-
|
113
|
-
# now a second request should create all the expected files
|
114
|
-
adapter.call(:fake_env)
|
115
|
-
%w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
|
116
|
-
file_path = ::File.join(path, "multi-requests-2-#{base_name}")
|
117
|
-
assert(File.exist?(file_path))
|
118
|
-
end
|
119
|
-
|
120
|
-
# let's clean up
|
121
|
-
FileUtils.rm_rf(Dir["#{path}/*"])
|
122
|
-
|
123
|
-
# and do the same again for the next 2 requests
|
124
|
-
adapter.call(:fake_env)
|
125
|
-
assert(Dir["#{path}/*"].empty?)
|
126
|
-
|
127
|
-
adapter.call(:fake_env)
|
128
|
-
%w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
|
129
|
-
file_path = ::File.join(path, "multi-requests-2-#{base_name}")
|
130
|
-
assert(File.exist?(file_path))
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def test_tries_to_print_results_if_shut_down_before_max_requests_reached
|
135
|
-
path = Dir.mktmpdir
|
136
|
-
|
137
|
-
adapter = Rack::RubyProf.new(FakeRackApp.new, :path => path, :max_requests => 100)
|
138
|
-
|
139
|
-
# make a 1st request, and check that this didn't create any files
|
140
|
-
adapter.call(:fake_env)
|
141
|
-
assert(Dir["#{path}/*"].empty?)
|
142
|
-
|
143
|
-
adapter.public_delete_profiler!
|
144
|
-
|
145
|
-
%w(flat.txt graph.txt graph.html call_stack.html).each do |base_name|
|
146
|
-
file_path = ::File.join(path, "multi-requests-1-#{base_name}")
|
147
|
-
assert(File.exist?(file_path))
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def test_it_uses_separate_profilers_if_not_aggregating_multiple_requests
|
152
|
-
adapter1 = Rack::RubyProf.new(FakeRackApp.new)
|
153
|
-
adapter2 = Rack::RubyProf.new(FakeRackApp.new)
|
154
|
-
|
155
|
-
assert(adapter1.object_id != adapter2.object_id)
|
156
|
-
end
|
157
93
|
end
|
data/test/server.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'fiber'
|
5
|
+
|
6
|
+
# The full implementation is given here, in order to show all the parts. A simpler implementation is given below.
|
7
|
+
class Reactor
|
8
|
+
def initialize
|
9
|
+
@readable = {}
|
10
|
+
@writable = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
while @readable.any? or @writable.any?
|
15
|
+
readable, writable = IO.select(@readable.keys, @writable.keys, [])
|
16
|
+
|
17
|
+
readable.each do |io|
|
18
|
+
@readable[io].resume
|
19
|
+
end
|
20
|
+
|
21
|
+
writable.each do |io|
|
22
|
+
@writable[io].resume
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def wait_readable(io)
|
28
|
+
@readable[io] = Fiber.current
|
29
|
+
Fiber.yield
|
30
|
+
@readable.delete(io)
|
31
|
+
|
32
|
+
return yield if block_given?
|
33
|
+
end
|
34
|
+
|
35
|
+
def wait_writable(io)
|
36
|
+
@writable[io] = Fiber.current
|
37
|
+
Fiber.yield
|
38
|
+
@writable.delete(io)
|
39
|
+
|
40
|
+
return yield if block_given?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
server = TCPServer.new('localhost', 9090)
|
45
|
+
reactor = Reactor.new
|
46
|
+
|
47
|
+
Fiber.new do
|
48
|
+
loop do
|
49
|
+
client = reactor.wait_readable(server) do
|
50
|
+
server.accept
|
51
|
+
end
|
52
|
+
|
53
|
+
Fiber.new do
|
54
|
+
while buffer = reactor.wait_readable(client) {client.gets}
|
55
|
+
reactor.wait_writable(client)
|
56
|
+
client.puts(buffer)
|
57
|
+
end
|
58
|
+
|
59
|
+
client.close
|
60
|
+
end.resume
|
61
|
+
end
|
62
|
+
end.resume
|
63
|
+
|
64
|
+
reactor.run
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-prof
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: x64-mingw32
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda, Charlie Savage, Roger Pack, Stefan Kaes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -99,8 +99,7 @@ files:
|
|
99
99
|
- ext/ruby_prof/ruby_prof.h
|
100
100
|
- ext/ruby_prof/vc/ruby_prof.sln
|
101
101
|
- ext/ruby_prof/vc/ruby_prof.vcxproj
|
102
|
-
-
|
103
|
-
- lib/2.7.0/ruby_prof.so
|
102
|
+
- ext/ruby_prof/vc/ruby_prof_linux.vcxproj
|
104
103
|
- lib/ruby-prof.rb
|
105
104
|
- lib/ruby-prof/assets/call_stack_printer.html.erb
|
106
105
|
- lib/ruby-prof/assets/call_stack_printer.png
|
@@ -132,6 +131,7 @@ files:
|
|
132
131
|
- test/basic_test.rb
|
133
132
|
- test/call_tree_visitor_test.rb
|
134
133
|
- test/call_trees_test.rb
|
134
|
+
- test/client.rb
|
135
135
|
- test/duplicate_names_test.rb
|
136
136
|
- test/dynamic_method_test.rb
|
137
137
|
- test/enumerable_test.rb
|
@@ -164,6 +164,7 @@ files:
|
|
164
164
|
- test/printing_recursive_graph_test.rb
|
165
165
|
- test/rack_test.rb
|
166
166
|
- test/recursive_test.rb
|
167
|
+
- test/server.rb
|
167
168
|
- test/singleton_test.rb
|
168
169
|
- test/stack_printer_test.rb
|
169
170
|
- test/start_stop_test.rb
|
@@ -178,7 +179,7 @@ metadata:
|
|
178
179
|
bug_tracker_uri: https://github.com/ruby-prof/ruby-prof/issues
|
179
180
|
changelog_uri: https://github.com/ruby-prof/ruby-prof/blob/master/CHANGES
|
180
181
|
documentation_uri: https://ruby-prof.github.io/
|
181
|
-
source_code_uri: https://github.com/ruby-prof/ruby-prof/tree/v1.
|
182
|
+
source_code_uri: https://github.com/ruby-prof/ruby-prof/tree/v1.4.0
|
182
183
|
post_install_message:
|
183
184
|
rdoc_options: []
|
184
185
|
require_paths:
|
@@ -194,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
194
195
|
- !ruby/object:Gem::Version
|
195
196
|
version: '0'
|
196
197
|
requirements: []
|
197
|
-
rubygems_version: 3.
|
198
|
+
rubygems_version: 3.1.2
|
198
199
|
signing_key:
|
199
200
|
specification_version: 4
|
200
201
|
summary: Fast Ruby profiler
|
data/lib/2.6.5/ruby_prof.so
DELETED
Binary file
|
data/lib/2.7.0/ruby_prof.so
DELETED
Binary file
|