ruby-prof 1.3.1-x64-mingw32 → 1.4.0-x64-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|