sqreen 1.19.1-java → 1.21.0.beta3-java
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 +5 -5
- data/CHANGELOG.md +34 -0
- data/lib/sqreen/actions/block_user.rb +1 -1
- data/lib/sqreen/actions/redirect_ip.rb +1 -1
- data/lib/sqreen/actions/redirect_user.rb +1 -1
- data/lib/sqreen/agent_message.rb +20 -0
- data/lib/sqreen/aggregated_metric.rb +25 -0
- data/lib/sqreen/attack_detected.html +1 -2
- data/lib/sqreen/ca.crt +24 -0
- data/lib/sqreen/condition_evaluator.rb +9 -2
- data/lib/sqreen/conditionable.rb +24 -6
- data/lib/sqreen/configuration.rb +11 -5
- data/lib/sqreen/deferred_logger.rb +50 -14
- data/lib/sqreen/deliveries/batch.rb +12 -2
- data/lib/sqreen/deliveries/simple.rb +4 -0
- data/lib/sqreen/deprecation.rb +38 -0
- data/lib/sqreen/ecosystem.rb +96 -0
- data/lib/sqreen/ecosystem/dispatch_table.rb +43 -0
- data/lib/sqreen/ecosystem/exception_reporting.rb +26 -0
- data/lib/sqreen/ecosystem/http/net_http.rb +50 -0
- data/lib/sqreen/ecosystem/http/rack_request.rb +39 -0
- data/lib/sqreen/ecosystem/loggable.rb +13 -0
- data/lib/sqreen/ecosystem/module_api.rb +30 -0
- data/lib/sqreen/ecosystem/module_api/event_listener.rb +18 -0
- data/lib/sqreen/ecosystem/module_api/instrumentation.rb +23 -0
- data/lib/sqreen/ecosystem/module_api/message_producer.rb +51 -0
- data/lib/sqreen/ecosystem/module_api/signal_producer.rb +24 -0
- data/lib/sqreen/ecosystem/module_api/tracing.rb +45 -0
- data/lib/sqreen/ecosystem/module_api/tracing/client_data.rb +31 -0
- data/lib/sqreen/ecosystem/module_api/tracing/server_data.rb +27 -0
- data/lib/sqreen/ecosystem/module_api/tracing_id_generation.rb +16 -0
- data/lib/sqreen/ecosystem/module_api/transaction_storage.rb +71 -0
- data/lib/sqreen/ecosystem/module_registry.rb +44 -0
- data/lib/sqreen/ecosystem/redis/redis_connection.rb +43 -0
- data/lib/sqreen/ecosystem/tracing/modules/client.rb +31 -0
- data/lib/sqreen/ecosystem/tracing/modules/server.rb +30 -0
- data/lib/sqreen/ecosystem/tracing/sampler.rb +160 -0
- data/lib/sqreen/ecosystem/tracing/sampling_configuration.rb +150 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_client.rb +53 -0
- data/lib/sqreen/ecosystem/tracing/signals/tracing_server.rb +53 -0
- data/lib/sqreen/ecosystem/tracing_broker.rb +101 -0
- data/lib/sqreen/ecosystem/tracing_id_setup.rb +34 -0
- data/lib/sqreen/ecosystem/transaction_storage.rb +64 -0
- data/lib/sqreen/ecosystem/util/call_writers_from_init.rb +13 -0
- data/lib/sqreen/ecosystem_integration.rb +87 -0
- data/lib/sqreen/ecosystem_integration/around_callbacks.rb +99 -0
- data/lib/sqreen/ecosystem_integration/instrumentation_service.rb +42 -0
- data/lib/sqreen/ecosystem_integration/request_lifecycle_tracking.rb +58 -0
- data/lib/sqreen/ecosystem_integration/signal_consumption.rb +35 -0
- data/lib/sqreen/endpoint_testing.rb +184 -0
- data/lib/sqreen/event.rb +7 -5
- data/lib/sqreen/events/attack.rb +23 -18
- data/lib/sqreen/events/remote_exception.rb +0 -22
- data/lib/sqreen/events/request_record.rb +15 -71
- data/lib/sqreen/frameworks/generic.rb +24 -1
- data/lib/sqreen/frameworks/rails.rb +0 -7
- data/lib/sqreen/frameworks/request_recorder.rb +15 -2
- data/lib/sqreen/graft/call.rb +106 -19
- data/lib/sqreen/graft/callback.rb +1 -1
- data/lib/sqreen/graft/hook.rb +212 -100
- data/lib/sqreen/graft/hook_point.rb +18 -11
- data/lib/sqreen/kit/signals/specialized/aggregated_metric.rb +72 -0
- data/lib/sqreen/kit/signals/specialized/attack.rb +57 -0
- data/lib/sqreen/kit/signals/specialized/binning_metric.rb +76 -0
- data/lib/sqreen/kit/signals/specialized/http_trace.rb +26 -0
- data/lib/sqreen/kit/signals/specialized/sdk_track_call.rb +50 -0
- data/lib/sqreen/kit/signals/specialized/sqreen_exception.rb +57 -0
- data/lib/sqreen/legacy/instrumentation.rb +22 -10
- data/lib/sqreen/legacy/old_event_submission_strategy.rb +228 -0
- data/lib/sqreen/legacy/waf_redactions.rb +49 -0
- data/lib/sqreen/log.rb +3 -2
- data/lib/sqreen/log/loggable.rb +2 -1
- data/lib/sqreen/logger.rb +24 -0
- data/lib/sqreen/metrics.rb +1 -0
- data/lib/sqreen/metrics/base.rb +3 -0
- data/lib/sqreen/metrics/req_detailed.rb +41 -0
- data/lib/sqreen/metrics_store.rb +33 -12
- data/lib/sqreen/null_logger.rb +22 -0
- data/lib/sqreen/performance_notifications/binned_metrics.rb +8 -2
- data/lib/sqreen/remote_command.rb +4 -0
- data/lib/sqreen/rules.rb +12 -6
- data/lib/sqreen/rules/blacklist_ips_cb.rb +2 -2
- data/lib/sqreen/rules/custom_error_cb.rb +3 -3
- data/lib/sqreen/rules/not_found_cb.rb +2 -0
- data/lib/sqreen/rules/rule_cb.rb +6 -2
- data/lib/sqreen/rules/waf_cb.rb +16 -13
- data/lib/sqreen/runner.rb +138 -16
- data/lib/sqreen/sensitive_data_redactor.rb +19 -31
- data/lib/sqreen/session.rb +53 -43
- data/lib/sqreen/signals/conversions.rb +288 -0
- data/lib/sqreen/signals/http_trace_redaction.rb +111 -0
- data/lib/sqreen/signals/signals_submission_strategy.rb +78 -0
- data/lib/sqreen/version.rb +1 -1
- data/lib/sqreen/weave/budget.rb +35 -0
- data/lib/sqreen/weave/legacy/instrumentation.rb +277 -135
- data/lib/sqreen/worker.rb +6 -2
- metadata +86 -10
- data/lib/sqreen/backport.rb +0 -9
- data/lib/sqreen/backport/clock_gettime.rb +0 -74
- data/lib/sqreen/backport/original_name.rb +0 -88
- data/lib/sqreen/encoding_sanitizer.rb +0 -27
data/lib/sqreen/event.rb
CHANGED
@@ -8,17 +8,19 @@
|
|
8
8
|
module Sqreen
|
9
9
|
# Master interface for point in time events (e.g. Attack, RemoteException)
|
10
10
|
class Event
|
11
|
+
# @return [Hash]
|
11
12
|
attr_reader :payload
|
13
|
+
|
14
|
+
# @return [Time]
|
15
|
+
attr_accessor :time # writer used only in tests
|
16
|
+
|
12
17
|
def initialize(payload)
|
13
18
|
@payload = payload
|
14
|
-
|
15
|
-
|
16
|
-
def to_hash
|
17
|
-
payload.to_hash
|
19
|
+
@time = Time.now.utc
|
18
20
|
end
|
19
21
|
|
20
22
|
def to_s
|
21
|
-
"<#{self.class.name}: #{to_hash}>"
|
23
|
+
"<#{self.class.name}: #{payload.to_hash}>"
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
data/lib/sqreen/events/attack.rb
CHANGED
@@ -11,6 +11,8 @@ module Sqreen
|
|
11
11
|
# Attack
|
12
12
|
# When creating a new attack, it gets automatically pushed to the event's
|
13
13
|
# queue.
|
14
|
+
# XXX: TURNS OUT THIS CLASS IS ACTUALLY NOT USED ANYMORE
|
15
|
+
# Framework.observe is used instead with unstructured attack details
|
14
16
|
class Attack < Event
|
15
17
|
def self.record(payload)
|
16
18
|
attack = Attack.new(payload)
|
@@ -26,11 +28,31 @@ module Sqreen
|
|
26
28
|
payload['rule']['rulespack_id']
|
27
29
|
end
|
28
30
|
|
29
|
-
def
|
31
|
+
def rule_name
|
30
32
|
return nil unless payload['rule']
|
31
33
|
payload['rule']['name']
|
32
34
|
end
|
33
35
|
|
36
|
+
def test?
|
37
|
+
return nil unless payload['rule']
|
38
|
+
payload['rule']['test'] ? true : false
|
39
|
+
end
|
40
|
+
|
41
|
+
def beta?
|
42
|
+
return nil unless payload['rule']
|
43
|
+
payload['rule']['beta'] ? true : false
|
44
|
+
end
|
45
|
+
|
46
|
+
def block?
|
47
|
+
return nil unless payload['rule']
|
48
|
+
payload['rule']['block'] ? true : false
|
49
|
+
end
|
50
|
+
|
51
|
+
def attack_type
|
52
|
+
return nil unless payload['rule']
|
53
|
+
payload['rule']['attack_type']
|
54
|
+
end
|
55
|
+
|
34
56
|
def time
|
35
57
|
return nil unless payload['local']
|
36
58
|
payload['local']['time']
|
@@ -44,22 +66,5 @@ module Sqreen
|
|
44
66
|
def enqueue
|
45
67
|
Sqreen.queue.push(self)
|
46
68
|
end
|
47
|
-
|
48
|
-
def to_hash
|
49
|
-
res = {}
|
50
|
-
rule_p = payload['rule']
|
51
|
-
request_p = payload['request']
|
52
|
-
res[:rule_name] = rule_p['name'] if rule_p && rule_p['name']
|
53
|
-
res[:rulespack_id] = rule_p['rulespack_id'] if rule_p && rule_p['rulespack_id']
|
54
|
-
res[:test] = rule_p['test'] if rule_p && rule_p['test']
|
55
|
-
res[:infos] = payload['infos'] if payload['infos']
|
56
|
-
res[:time] = time if time
|
57
|
-
res[:client_ip] = request_p[:addr] if request_p && request_p[:addr]
|
58
|
-
res[:request] = request_p if request_p
|
59
|
-
res[:params] = payload['params'] if payload['params']
|
60
|
-
res[:context] = payload['context'] if payload['context']
|
61
|
-
res[:headers] = payload['headers'] if payload['headers']
|
62
|
-
res
|
63
|
-
end
|
64
69
|
end
|
65
70
|
end
|
@@ -30,27 +30,5 @@ module Sqreen
|
|
30
30
|
def klass
|
31
31
|
payload['exception'].class.name
|
32
32
|
end
|
33
|
-
|
34
|
-
def to_hash
|
35
|
-
exception = payload['exception']
|
36
|
-
ev = {
|
37
|
-
:klass => exception.class.name,
|
38
|
-
:message => exception.message,
|
39
|
-
:params => payload['request_params'],
|
40
|
-
:time => payload['time'],
|
41
|
-
:infos => {
|
42
|
-
:client_ip => payload['client_ip'],
|
43
|
-
},
|
44
|
-
:request => payload['request_infos'],
|
45
|
-
:headers => payload['headers'],
|
46
|
-
:rule_name => payload['rule_name'],
|
47
|
-
:rulespack_id => payload['rulespack_id'],
|
48
|
-
}
|
49
|
-
|
50
|
-
ev[:infos].merge!(payload['infos']) if payload['infos']
|
51
|
-
return ev unless exception.backtrace
|
52
|
-
ev[:context] = { :backtrace => exception.backtrace.map(&:to_s) }
|
53
|
-
ev
|
54
|
-
end
|
55
33
|
end
|
56
34
|
end
|
@@ -8,12 +8,15 @@
|
|
8
8
|
require 'json'
|
9
9
|
require 'sqreen/log'
|
10
10
|
require 'sqreen/event'
|
11
|
-
require 'sqreen/encoding_sanitizer'
|
12
11
|
require 'sqreen/sensitive_data_redactor'
|
13
12
|
|
14
13
|
module Sqreen
|
15
14
|
# When a request is deeemed worthy of being sent to the backend
|
16
15
|
class RequestRecord < Sqreen::Event
|
16
|
+
attr_reader :redactor
|
17
|
+
|
18
|
+
# @param [Hash] payload
|
19
|
+
# @param [Sqreen::SensitiveDataRedactor] redactor
|
17
20
|
def initialize(payload, redactor = nil)
|
18
21
|
@redactor = redactor
|
19
22
|
super(payload)
|
@@ -23,74 +26,18 @@ module Sqreen
|
|
23
26
|
(payload && payload[:observed]) || {}
|
24
27
|
end
|
25
28
|
|
26
|
-
def
|
27
|
-
|
28
|
-
if payload[:observed]
|
29
|
-
res[:observed] = payload[:observed].dup
|
30
|
-
rulespack = nil
|
31
|
-
if observed[:attacks]
|
32
|
-
res[:observed][:attacks] = observed[:attacks].map do |att|
|
33
|
-
natt = att.dup
|
34
|
-
rulespack = natt.delete(:rulespack_id) || rulespack
|
35
|
-
natt
|
36
|
-
end
|
37
|
-
end
|
38
|
-
if observed[:sqreen_exceptions]
|
39
|
-
res[:observed][:sqreen_exceptions] = observed[:sqreen_exceptions].map do |exc|
|
40
|
-
nex = exc.dup
|
41
|
-
excp = nex.delete(:exception)
|
42
|
-
if excp
|
43
|
-
nex[:message] = excp.message
|
44
|
-
nex[:klass] = excp.class.name
|
45
|
-
end
|
46
|
-
rulespack = nex.delete(:rulespack_id) || rulespack
|
47
|
-
nex
|
48
|
-
end
|
49
|
-
end
|
50
|
-
res[:rulespack_id] = rulespack unless rulespack.nil?
|
51
|
-
if observed[:observations]
|
52
|
-
res[:observed][:observations] = observed[:observations].map do |cat, key, value, time|
|
53
|
-
{ :category => cat, :key => key, :value => value, :time => time }
|
54
|
-
end
|
55
|
-
end
|
56
|
-
if observed[:sdk]
|
57
|
-
res[:observed][:sdk] = processed_sdk_calls
|
58
|
-
end
|
59
|
-
end
|
60
|
-
res[:local] = payload['local'] if payload['local']
|
61
|
-
if payload['request']
|
62
|
-
res[:request] = payload['request'].dup
|
63
|
-
res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip]
|
64
|
-
else
|
65
|
-
res[:request] = {}
|
66
|
-
end
|
67
|
-
if payload['response']
|
68
|
-
res[:response] = payload['response'].dup
|
69
|
-
else
|
70
|
-
res[:response] = {}
|
71
|
-
end
|
72
|
-
|
73
|
-
res[:request][:parameters] = payload['params'] if payload['params']
|
74
|
-
res[:request][:headers] = payload['headers'] if payload['headers']
|
75
|
-
|
76
|
-
res = Sqreen::EncodingSanitizer.sanitize(res)
|
29
|
+
def last_identify_args
|
30
|
+
return nil unless observed[:sdk]
|
77
31
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
res[:observed][:attacks] = @redactor.redact_attacks!(res[:observed][:attacks], redacted)
|
82
|
-
end
|
83
|
-
if redacted.any? && res[:observed] && res[:observed][:sqreen_exceptions]
|
84
|
-
res[:observed][:sqreen_exceptions] = @redactor.redact_exceptions!(res[:observed][:sqreen_exceptions], redacted)
|
85
|
-
end
|
32
|
+
observed[:sdk].reverse_each do |meth, _time, *args|
|
33
|
+
next unless meth == :identify
|
34
|
+
return args
|
86
35
|
end
|
87
|
-
|
88
|
-
res
|
36
|
+
nil
|
89
37
|
end
|
90
38
|
|
91
|
-
private
|
92
|
-
|
93
39
|
def processed_sdk_calls
|
40
|
+
return [] unless observed[:sdk]
|
94
41
|
auth_keys = last_identify_id
|
95
42
|
|
96
43
|
observed[:sdk].map do |meth, time, *args|
|
@@ -102,6 +49,8 @@ module Sqreen
|
|
102
49
|
end
|
103
50
|
end
|
104
51
|
|
52
|
+
private
|
53
|
+
|
105
54
|
def inject_identifiers(args, meth, auth_keys)
|
106
55
|
return args unless meth == :track && auth_keys
|
107
56
|
|
@@ -118,13 +67,8 @@ module Sqreen
|
|
118
67
|
end
|
119
68
|
|
120
69
|
def last_identify_id
|
121
|
-
|
122
|
-
|
123
|
-
observed[:sdk].reverse_each do |meth, _time, *args|
|
124
|
-
next unless meth == :identify
|
125
|
-
return args.first if args.respond_to? :first
|
126
|
-
end
|
127
|
-
nil
|
70
|
+
args = last_identify_args
|
71
|
+
args.first if args.respond_to? :first
|
128
72
|
end
|
129
73
|
end
|
130
74
|
end
|
@@ -22,8 +22,17 @@ module Sqreen
|
|
22
22
|
include RequestRecorder
|
23
23
|
attr_accessor :sqreen_configuration
|
24
24
|
|
25
|
+
attr_writer :req_start_cb, :req_end_cb
|
26
|
+
|
25
27
|
def initialize
|
26
28
|
clean_request_record
|
29
|
+
|
30
|
+
# for notifying the ecosystem of request boundaries
|
31
|
+
# XXX: this should be refactored. It shouldn't be
|
32
|
+
# the framework doing these notifications to the ecosystem
|
33
|
+
# Probably the rule callback should do it itself
|
34
|
+
@req_start_cb = Proc.new {}
|
35
|
+
@req_end_cb = Proc.new {}
|
27
36
|
end
|
28
37
|
|
29
38
|
# What kind of database is this
|
@@ -209,7 +218,16 @@ module Sqreen
|
|
209
218
|
|
210
219
|
# Should the agent not be starting up?
|
211
220
|
def prevent_startup
|
221
|
+
# SQREEN-880 - prevent Sqreen startup on Sidekiq workers
|
222
|
+
return :sidekiq_cli if defined?(Sidekiq::CLI)
|
223
|
+
return :delayed_job if defined?(Delayed::Command)
|
224
|
+
|
225
|
+
# Prevent Sqreen startup on rake tasks - unless this is a Sqreen test
|
226
|
+
run_in_test = sqreen_configuration.get(:run_in_test)
|
227
|
+
return :rake if !run_in_test && $0.end_with?('rake')
|
228
|
+
|
212
229
|
return :irb if $0 == 'irb'
|
230
|
+
|
213
231
|
return if sqreen_configuration.nil?
|
214
232
|
disable = sqreen_configuration.get(:disable)
|
215
233
|
return :config_disable if disable == true || disable.to_s.to_i == 1
|
@@ -251,8 +269,12 @@ module Sqreen
|
|
251
269
|
# Nota: cleanup should be performed at end of request (see clean_request)
|
252
270
|
def store_request(object)
|
253
271
|
return unless ensure_rack_loaded
|
272
|
+
|
273
|
+
rack_req = Rack::Request.new(object)
|
274
|
+
@req_start_cb.call(rack_req)
|
275
|
+
|
254
276
|
self.remaining_perf_budget = Sqreen.performance_budget
|
255
|
-
SharedStorage.set(:request,
|
277
|
+
SharedStorage.set(:request, rack_req)
|
256
278
|
SharedStorage.set(:xss_params, nil)
|
257
279
|
SharedStorage.set(:whitelisted, nil)
|
258
280
|
SharedStorage.set(:request_overtime, nil)
|
@@ -281,6 +303,7 @@ module Sqreen
|
|
281
303
|
SharedStorage.set(:xss_params, nil)
|
282
304
|
SharedStorage.set(:whitelisted, nil)
|
283
305
|
SharedStorage.set(:request_overtime, nil)
|
306
|
+
@req_end_cb.call
|
284
307
|
end
|
285
308
|
|
286
309
|
def remaining_perf_budget
|
@@ -103,13 +103,6 @@ module Sqreen
|
|
103
103
|
run_in_test = sqreen_configuration.get(:run_in_test)
|
104
104
|
return :rails_test if !run_in_test && (Rails.env.test? || Rails.env.cucumber?)
|
105
105
|
|
106
|
-
# SQREEN-880 - prevent Sqreen startup on Sidekiq workers
|
107
|
-
return :sidekiq_cli if defined?(Sidekiq::CLI)
|
108
|
-
return :delayed_job if defined?(Delayed::Command)
|
109
|
-
|
110
|
-
# Prevent Sqreen startup on rake tasks - unless this is a Sqreen test
|
111
|
-
return :rake if !run_in_test && $0.end_with?('rake')
|
112
|
-
|
113
106
|
return nil unless defined?(Rails::CommandsTasks)
|
114
107
|
return nil if defined?(Rails::Server)
|
115
108
|
return :rails_console if defined?(Rails::Console)
|
@@ -58,12 +58,22 @@ module Sqreen
|
|
58
58
|
Sqreen.log.debug { "close_request_record called. observed_items: #{observed_items}" }
|
59
59
|
|
60
60
|
clean_request_record if observed_items.nil?
|
61
|
-
if only_metric_observation
|
61
|
+
if Sqreen.features['use_signals'] || only_metric_observation
|
62
62
|
push_metrics(observations_queue, queue)
|
63
|
-
return clean_request_record
|
64
63
|
end
|
64
|
+
|
65
|
+
if only_metric_observation
|
66
|
+
clean_request_record
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
# signals require request section to be present
|
71
|
+
payload_requests << 'request'
|
72
|
+
# for signals, response is optional, but the backend team wants them
|
73
|
+
payload_requests << 'response'
|
65
74
|
payload = payload_creator.payload(payload_requests)
|
66
75
|
payload[:observed] = observed_items
|
76
|
+
|
67
77
|
queue.push create_request_record(payload)
|
68
78
|
clean_request_record
|
69
79
|
end
|
@@ -79,10 +89,13 @@ module Sqreen
|
|
79
89
|
@redactor ||= SensitiveDataRedactor.from_config
|
80
90
|
end
|
81
91
|
|
92
|
+
# pushes metric observations to the observations queue
|
93
|
+
# and clears the list for the request record
|
82
94
|
def push_metrics(observations_queue, event_queue)
|
83
95
|
observed_items[:observations].each do |obs|
|
84
96
|
observations_queue.push obs
|
85
97
|
end
|
98
|
+
observed_items[:observations] = []
|
86
99
|
return unless observations_queue.size > MAX_OBS_QUEUE_LENGTH / 2
|
87
100
|
event_queue.push Sqreen::METRICS_EVENT
|
88
101
|
end
|
data/lib/sqreen/graft/call.rb
CHANGED
@@ -27,6 +27,10 @@ module Sqreen
|
|
27
27
|
def raise(value)
|
28
28
|
Flow.raise(value)
|
29
29
|
end
|
30
|
+
|
31
|
+
def noop
|
32
|
+
Flow.noop
|
33
|
+
end
|
30
34
|
end
|
31
35
|
|
32
36
|
class Flow
|
@@ -46,12 +50,17 @@ module Sqreen
|
|
46
50
|
def raise(value)
|
47
51
|
new(:raise, value)
|
48
52
|
end
|
53
|
+
|
54
|
+
def noop
|
55
|
+
new(:noop, nil)
|
56
|
+
end
|
49
57
|
end
|
50
58
|
|
51
59
|
def initialize(action, value, brk = false)
|
52
60
|
@action = action
|
53
61
|
@value = value
|
54
62
|
@break = brk
|
63
|
+
@passed_conditions = false
|
55
64
|
end
|
56
65
|
|
57
66
|
def return?
|
@@ -91,60 +100,138 @@ module Sqreen
|
|
91
100
|
def break?
|
92
101
|
@break ? true : false
|
93
102
|
end
|
103
|
+
|
104
|
+
def passed_conditions!
|
105
|
+
@passed_conditions = true
|
106
|
+
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def passed_conditions?
|
111
|
+
@passed_conditions
|
112
|
+
end
|
94
113
|
end
|
95
114
|
|
115
|
+
class TimerError < StandardError; end
|
116
|
+
|
96
117
|
class Timer
|
97
118
|
def self.read
|
98
119
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
99
120
|
end
|
100
121
|
|
101
|
-
attr_reader :tag
|
122
|
+
attr_reader :tag, :size
|
123
|
+
attr_accessor :conditions_passed
|
102
124
|
|
103
125
|
def initialize(tag, &block)
|
104
126
|
@tag = tag
|
105
|
-
@blips = []
|
106
127
|
@block = block
|
128
|
+
@tally = 0
|
129
|
+
@size = 0
|
107
130
|
end
|
108
131
|
|
109
|
-
def
|
110
|
-
|
132
|
+
def elapsed
|
133
|
+
raise(TimerError, 'Timer#elapsed when paused') if @size.even?
|
134
|
+
|
135
|
+
@tally + Timer.read
|
111
136
|
end
|
112
137
|
|
113
|
-
def
|
114
|
-
|
138
|
+
def duration
|
139
|
+
raise(TimerError, 'Timer#duration when running') if @size.odd?
|
140
|
+
|
141
|
+
@tally
|
115
142
|
end
|
116
143
|
|
117
144
|
def ignore
|
118
|
-
|
145
|
+
raise(TimerError, 'Timer#ignore when paused') if @size.even?
|
146
|
+
|
147
|
+
@size += 1
|
148
|
+
@tally += Timer.read
|
119
149
|
yield(self)
|
120
150
|
ensure
|
121
|
-
@
|
151
|
+
@size += 1
|
152
|
+
@tally -= Timer.read
|
122
153
|
end
|
123
154
|
|
124
|
-
def measure
|
125
|
-
|
155
|
+
def measure(opts = nil)
|
156
|
+
raise(TimerError, 'Timer#measure when running') if @size.odd?
|
157
|
+
|
158
|
+
now = Timer.read
|
159
|
+
|
160
|
+
ignore = opts[:ignore] if opts
|
161
|
+
if ignore
|
162
|
+
ignore.size += 1
|
163
|
+
ignore.tally += now
|
164
|
+
end
|
165
|
+
|
166
|
+
@size += 1
|
167
|
+
@tally -= now
|
168
|
+
|
126
169
|
yield(self)
|
127
170
|
ensure
|
128
|
-
|
171
|
+
now = Timer.read
|
172
|
+
|
173
|
+
if ignore
|
174
|
+
ignore.size += 1
|
175
|
+
ignore.tally -= now
|
176
|
+
end
|
177
|
+
|
178
|
+
@size += 1
|
179
|
+
@tally += now
|
180
|
+
|
129
181
|
@block.call(self) if @block
|
130
|
-
Sqreen::Graft.logger.debug { "#{@tag}: time=%.03fus" % (duration * 1_000_000) }
|
131
182
|
end
|
132
183
|
|
133
|
-
def start
|
134
|
-
|
184
|
+
def start(at = Timer.read)
|
185
|
+
raise(TimerError, 'Timer#start when started') unless @size.even?
|
186
|
+
|
187
|
+
@size += 1
|
188
|
+
@tally -= at
|
189
|
+
|
190
|
+
at
|
191
|
+
end
|
192
|
+
|
193
|
+
def stop(at = Timer.read)
|
194
|
+
raise(TimerError, 'Timer#stop when unstarted') unless @size.odd?
|
195
|
+
|
196
|
+
@size += 1
|
197
|
+
@tally += at
|
198
|
+
|
199
|
+
at
|
135
200
|
end
|
136
201
|
|
137
|
-
def
|
138
|
-
@
|
202
|
+
def started?
|
203
|
+
@size != 0 && @size.odd?
|
139
204
|
end
|
140
205
|
|
141
|
-
def
|
142
|
-
@
|
206
|
+
def stopped?
|
207
|
+
@size != 0 && @size.even?
|
208
|
+
end
|
209
|
+
|
210
|
+
def running?
|
211
|
+
@size.odd?
|
212
|
+
end
|
213
|
+
|
214
|
+
def paused?
|
215
|
+
@size.even?
|
216
|
+
end
|
217
|
+
|
218
|
+
def include_measurements(another_timer)
|
219
|
+
@blips += another_timer.instance_variable_get(:@blips)
|
220
|
+
end
|
221
|
+
|
222
|
+
def start_and_end
|
223
|
+
raise 'Not exactly two measurements recorded' unless size == 2
|
224
|
+
@blips
|
143
225
|
end
|
144
226
|
|
145
227
|
def to_s
|
146
|
-
"#{@tag}: time=%.03fus" % (
|
228
|
+
"#{@tag}: time=%.03fus" % (@tally * 1_000_000)
|
147
229
|
end
|
230
|
+
|
231
|
+
protected
|
232
|
+
|
233
|
+
attr_reader :tally
|
234
|
+
attr_writer :size, :tally
|
148
235
|
end
|
149
236
|
end
|
150
237
|
end
|