sampling_prof 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 9788852fedbd8c4a9fc1d38ccef911e8a94e6376
4
- data.tar.gz: 90beb461326396ebf9def203708553b1b06afbb0
5
- SHA512:
6
- metadata.gz: 0aaed78d0c76cb5f3b7d9a263a843db6751fda97375850e7d61e34a1fd046489b53cd0e632eea661d47e271bd2c13e70089f98bb9d8c67f1cc1e614b41e2e82f
7
- data.tar.gz: 3c9af4039b08d98d72b35ae45bfbb3883744684e6c00f9db20c405c5f2ff81a4a9f75ea97bd9b180c33dff8ebfd17748351f6b3c0a133e65f92734fe237e71fd
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b3d5e9f4030836370938d494ceb34b7c6a153531
4
+ data.tar.gz: c39ad8399c81faa1dc45bbdcb8c99d910e22a6c0
5
+ SHA512:
6
+ metadata.gz: 313981a2ed4edb5cea888052515eb87b7e2fe62271ad951960f8517580a5fb4983ffaddd370152d4e11e0f7a3193f8678b740d80427779c8fb64ec6cbc6d9637
7
+ data.tar.gz: 1ae6f1690779efd29ec5633a827a048e5622d9157ea0ef67688838ace40f54b33669060675be3069758e1a88a66257adccdbac34c4b1fdf05b117919767201b3
data/README.md CHANGED
@@ -1,51 +1,45 @@
1
1
  SamplingProf
2
2
  ===============
3
3
 
4
- SamplingProf is a profiling tool that operates by sampling your running thread stacktrace. The result is statistical approximation, but it allows your code to run near full speed.
4
+ SamplingProf is a statistical profiler or sampling profiler that operates by sampling your running thread stacktrace. The result is statistical approximation, but it allows your code to run near full speed.
5
5
 
6
6
  Quick start
7
7
  ---------------
8
8
 
9
9
  For single thread profiling, start with default options:
10
10
 
11
- prof = SamplingProf.new
12
- prof.profile do
13
- ... your code ...
14
- end
11
+ prof = SamplingProf.new
12
+ prof.profile do
13
+ ... your code ...
14
+ end
15
+ at_exit { prof.terminate }
15
16
 
16
17
  After profiling finished, the output will be write to file SamplingProf::DEFAULT_OUTPUT_FILE
17
18
 
18
19
  Options
19
20
  ---------------
20
21
 
21
- SamplingProf class initializer takes 3 arguments:
22
+ SamplingProf class initializer takes 1 argument:
22
23
 
23
24
  1. sampling interval: seconds
24
- 2. multithreading: boolean
25
- 3. output interval: seconds
26
25
 
27
26
  SamplingProf class also takes block as another option to overwite default output handler, the default output handler will write output data to a local file defined by output_file attribute, which is default to SamplingProf::DEFAULT_OUTPUT_FILE
28
27
 
28
+ When a SamplingProf is initialized, a thread will be started to handle sampling process.
29
+ So you need call SamplingProf#terminate to shutdown the sampling thread after everything is done.
30
+
29
31
  ### Sampling interval
30
32
 
31
33
  This is an interval to control how frequent SamplingProf should take sample of target thread stacktrace.
32
34
  The default value is 0.1 seconds.
33
35
 
34
- ### Multithreading mode
35
-
36
- When running SamplingProf in multithreading environment (e.g. Rails multithreading production environment), you need turn on multithreading mode so that you can profile all requests processing at same time cross threads.
37
-
38
- For performance concerns, multithreading mode will only take limit number of threads' sample while profiling. The default max samples of the threads is 4, you can change it by set max_sampling_threads.
39
-
40
- Since we randomly find max_sampling_threads number of threads in profiling threads, the result is still a statistical approximation.
36
+ ### Multithreading
41
37
 
42
- ### output interval
38
+ When running SamplingProf in multithreading environment (e.g. Rails multithreading production environment), it can profile all requests processing at same time cross threads.
43
39
 
44
- Output interval controls how frequent SamplingProf should call output handler to flush out cached data.
45
- The default value of output interval is depend on the option of multithreading.
46
- When multithreading is off, the default value is nil, because single thread mode is more likely only need to do once data output when the profiling finished.
40
+ For performance concerns, you should controll how many number of threads' sample while profiling.
47
41
 
48
- When multithreading is on, the default value is 60 seconds, means every 60 seconds SamplingProf will call output handler once to flush out data and restart to cumulate data.
42
+ Randomly select some threads for profiling, the result is still a statistical approximation.
49
43
 
50
44
  Output data format
51
45
  ---------------
@@ -93,7 +87,7 @@ SamplingProf collects count of each call element at runtime. There are 2 type of
93
87
 
94
88
  Same with call element id map, we use comma to separate data, and one line represents one call element data.
95
89
  Here we use call element id instead of call element string to reference the call element.
96
- The line looks like this: <call element id>,<self count>,<total count>
90
+ The line looks like this: [call element id],[self count],[total count]
97
91
 
98
92
  ### call graph
99
93
 
@@ -101,8 +95,8 @@ SamplingProf collects counts of function calls. One function call is A call elem
101
95
 
102
96
  The call graph stores the counts of function calls.
103
97
  Every line is one function call data.
104
- the line looks like this: <call element id1>,<call element id2>,<count>
98
+ the line looks like this: [call element id1],[call element id2],[count]
105
99
 
106
- Direct recursive calls are recorded as: <call element id1>,<call element id1>,<count>
100
+ Direct recursive calls are recorded as: [call element id1],[call element id1],[count]
107
101
 
108
102
  Indirect recursive calls are ignored as function call only have direct call info. Hence the data maybe odd when there are indirect recursive calls.
Binary file
@@ -11,17 +11,9 @@ class SamplingProf
11
11
 
12
12
  # options:
13
13
  # sampling_interval: default to 0.1 second
14
- # multithreading: default to false
15
- # output_interval: default to (multithreading ? 60 : nil)
16
14
  # &output_handler: default to write into output_file
17
15
  def initialize(*args, &output_handler)
18
16
  self.sampling_interval = args[0] || 0.1
19
- self.multithreading = args[1] || false
20
- if args.length > 2
21
- self.output_interval = args[2]
22
- else
23
- self.output_interval = multithreading ? 60 : nil
24
- end
25
17
  self.output_handler = block_given? ? output_handler : default_output_handler
26
18
  internal_initialize if respond_to?(:internal_initialize)
27
19
  end
@@ -3,11 +3,10 @@ require 'thread'
3
3
 
4
4
  class SamplingProf
5
5
  class Sampling
6
- def initialize(threads)
6
+ def initialize
7
7
  @samples = Hash.new{|h,k| h[k] = [0, 0] }
8
8
  @call_graph = Hash.new{|h,k| h[k] = 0}
9
9
  @nodes = {}
10
- @threads = threads
11
10
  @start_at = Time.now
12
11
  end
13
12
 
@@ -20,37 +19,35 @@ class SamplingProf
20
19
  end
21
20
 
22
21
  def result
23
- ret = [@threads.sampling_runtime * 1000]
22
+ ret = [runtime * 1000]
24
23
  ret << @nodes.map {|node| node.join(',')}.join("\n")
25
24
  ret << @samples.map {|count| count.flatten.join(',')}.join("\n")
26
25
  ret << @call_graph.map {|v| v.flatten.join(',')}.join("\n")
27
26
  "#{ret.join("\n\n")}\n"
28
27
  end
29
28
 
30
- def process
31
- @threads.sample_threads.each do |thread|
32
- locations = thread.backtrace_locations
33
- from = -1
34
- paths = []
35
- calls = []
36
- top_index = locations.size - 1
37
- locations.reverse.each_with_index do |loc, i|
38
- node_id = node_id(loc)
39
- if i == top_index
40
- @samples[node_id][0] += 1
41
- end
42
-
43
- path = [from, node_id]
44
- if !paths.include?(path)
45
- paths << path
46
- @call_graph[path] += 1
47
- end
48
- if !calls.include?(node_id)
49
- calls << node_id
50
- @samples[node_id][1] += 1
51
- end
52
- from = node_id
29
+ def process(thread)
30
+ locations = thread.backtrace_locations
31
+ from = -1
32
+ paths = []
33
+ calls = []
34
+ top_index = locations.size - 1
35
+ locations.reverse.each_with_index do |loc, i|
36
+ node_id = node_id(loc)
37
+ if i == top_index
38
+ @samples[node_id][0] += 1
39
+ end
40
+
41
+ path = [from, node_id]
42
+ if !paths.include?(path)
43
+ paths << path
44
+ @call_graph[path] += 1
53
45
  end
46
+ if !calls.include?(node_id)
47
+ calls << node_id
48
+ @samples[node_id][1] += 1
49
+ end
50
+ from = node_id
54
51
  end
55
52
  end
56
53
 
@@ -63,100 +60,63 @@ class SamplingProf
63
60
  end
64
61
  end
65
62
 
66
- class Threads
67
- attr_accessor :max
68
-
69
- def initialize
70
- @hash = {}
71
- @mutex = Mutex.new
72
- @remain_sampling_time = 0
73
- @max = 4
74
- end
75
-
76
- def sample_threads
77
- @mutex.synchronize { @hash.keys.dup.shuffle[0..@max] }
78
- end
79
-
80
- def add(obj, time=Time.now)
81
- @mutex.synchronize { @hash[obj] = time }
82
- end
83
-
84
- def sampling_runtime
85
- now = Time.now
86
- @mutex.synchronize do
87
- ret, @remain_sampling_time = @remain_sampling_time, 0
88
- @hash.keys.each do |k|
89
- ret += now - @hash[k]
90
- @hash[k] = now
91
- end
92
- ret
93
- end
94
- end
95
-
96
- def delete(obj)
97
- @mutex.synchronize do
98
- start = @hash.delete(obj)
99
- @remain_sampling_time += Time.now - start
100
- end
101
- end
102
- end
103
-
104
- attr_accessor :sampling_interval, :multithreading, :output_interval, :output_handler
63
+ attr_accessor :sampling_interval, :output_handler
105
64
 
106
65
  def internal_initialize
107
- @running = false
108
- @sampling_thread = nil
109
- @threads = Threads.new
110
- end
111
-
112
- def max_sampling_threads=(max)
113
- @threads.max = max
66
+ @samplings = {}
67
+ start_sampling_thread
114
68
  end
115
69
 
116
70
  def start
117
- if @multithreading || !@running
118
- @running = true
119
- @threads.add(Thread.current)
120
- @sampling_thread ||= Thread.start do
121
- loop do
122
- sampling = Sampling.new(@threads)
123
- loop do
124
- break unless @running
125
- if @multithreading
126
- break if output_interval < sampling.runtime
127
- end
128
- sampling.process
129
- sleep @sampling_interval
130
- end
131
- if sampling.sampling_data?
132
- @output_handler.call(sampling.result)
133
- end
134
- break unless @running
135
- end
136
- end
71
+ unless @samplings.has_key?(Thread.current)
72
+ @samplings[Thread.current] = Sampling.new
137
73
  true
138
74
  end
139
75
  end
140
76
 
141
77
  def stop
142
78
  if @running
143
- if @multithreading
144
- @threads.delete(Thread.current)
145
- else
146
- terminate
79
+ if sampling = @samplings.delete(Thread.current)
80
+ output(sampling)
81
+ true
147
82
  end
148
- true
149
83
  end
150
84
  end
151
85
 
152
86
  def terminate
87
+ return false unless @running
153
88
  @running = false
154
89
  @sampling_thread.join
155
90
  @sampling_thread = nil
91
+ @samplings = {}
156
92
  true
157
93
  end
158
94
 
159
95
  def profiling?
160
- !!@sampling_thread
96
+ !!@sampling_thread && @samplings.has_key?(Thread.current)
97
+ end
98
+
99
+ private
100
+ def start_sampling_thread
101
+ return if @running
102
+ @running = true
103
+ @sampling_thread ||= Thread.start do
104
+ loop do
105
+ @samplings.dup.each do |t, s|
106
+ s.process(t)
107
+ end
108
+ sleep @sampling_interval
109
+ break unless @running
110
+ end
111
+ @samplings.each do |sampling|
112
+ output(sampling)
113
+ end
114
+ end
115
+ end
116
+
117
+ def output(sampling)
118
+ if sampling.sampling_data?
119
+ @output_handler.call(sampling.result)
120
+ end
161
121
  end
162
122
  end
metadata CHANGED
@@ -1,70 +1,69 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: sampling_prof
3
- version: !ruby/object:Gem::Version
4
- version: 0.3.4
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
5
  platform: ruby
6
- authors:
7
- - Xiao Li
6
+ authors:
7
+ - Xiao Li
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
-
12
- date: 2014-04-11 00:00:00 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rake-compiler
16
- prerelease: false
17
- requirement: &id001 !ruby/object:Gem::Requirement
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: "0.9"
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: 0.9.2
25
- type: :development
26
- version_requirements: *id001
11
+ date: 2014-06-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake-compiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.9'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.9.2
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.9'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2
27
33
  description: |
28
34
  SamplingProf is a profiling tool that operates by sampling your running thread stacktrace. The result is statistical approximation, but it allows your code to run near full speed
29
-
30
- email:
31
- - swing1979@gmail.com
35
+ email:
36
+ - swing1979@gmail.com
32
37
  executables: []
33
-
34
38
  extensions: []
35
-
36
39
  extra_rdoc_files: []
37
-
38
- files:
39
- - README.md
40
- - lib/sampling_prof.jar
41
- - lib/sampling_prof.rb
42
- - lib/sampling_prof/internal.rb
40
+ files:
41
+ - README.md
42
+ - lib/sampling_prof.jar
43
+ - lib/sampling_prof.rb
44
+ - lib/sampling_prof/internal.rb
43
45
  homepage: https://github.com/xli/sampling_prof
44
- licenses:
45
- - MIT
46
+ licenses:
47
+ - MIT
46
48
  metadata: {}
47
-
48
49
  post_install_message:
49
50
  rdoc_options: []
50
-
51
- require_paths:
52
- - lib
53
- required_ruby_version: !ruby/object:Gem::Requirement
54
- requirements:
55
- - &id002
56
- - ">="
57
- - !ruby/object:Gem::Version
58
- version: "0"
59
- required_rubygems_version: !ruby/object:Gem::Requirement
60
- requirements:
61
- - *id002
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
62
63
  requirements: []
63
-
64
64
  rubyforge_project:
65
- rubygems_version: 2.1.9
65
+ rubygems_version: 2.2.0
66
66
  signing_key:
67
67
  specification_version: 4
68
68
  summary: Simple sampling profiler for ruby
69
69
  test_files: []
70
-