delfos 0.0.1 → 0.0.2.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/delfos.rb +72 -13
- data/lib/delfos/call_stack.rb +9 -9
- data/lib/delfos/call_stack/stack.rb +18 -18
- data/lib/delfos/file_system.rb +16 -0
- data/lib/delfos/file_system/app_directories.rb +42 -0
- data/lib/delfos/file_system/common_path.rb +17 -11
- data/lib/delfos/file_system/distance_calculation.rb +12 -3
- data/lib/delfos/file_system/path_determination.rb +4 -1
- data/lib/delfos/file_system/relation.rb +1 -0
- data/lib/delfos/method_trace.rb +59 -0
- data/lib/delfos/method_trace/call_handler.rb +48 -0
- data/lib/delfos/method_trace/code_location.rb +35 -0
- data/lib/delfos/method_trace/code_location/call_site.rb +38 -0
- data/lib/delfos/method_trace/code_location/container_method_factory.rb +71 -0
- data/lib/delfos/method_trace/code_location/eval_in_caller.rb +17 -0
- data/lib/delfos/method_trace/code_location/filename_helpers.rb +46 -0
- data/lib/delfos/method_trace/code_location/method.rb +58 -0
- data/lib/delfos/neo4j.rb +4 -4
- data/lib/delfos/neo4j/batch/execution.rb +30 -88
- data/lib/delfos/neo4j/batch/retryable.rb +101 -0
- data/lib/delfos/neo4j/call_site_query.rb +89 -0
- data/lib/delfos/neo4j/distance/call_site_fetcher.rb +1 -0
- data/lib/delfos/neo4j/distance/update.rb +22 -5
- data/lib/delfos/neo4j/live/call_site_logger.rb +17 -0
- data/lib/delfos/neo4j/offline/call_site_logger.rb +39 -0
- data/lib/delfos/neo4j/offline/importer.rb +68 -0
- data/lib/delfos/neo4j/query_execution/errors.rb +13 -3
- data/lib/delfos/neo4j/query_execution/http.rb +1 -0
- data/lib/delfos/neo4j/query_execution/http_query.rb +20 -15
- data/lib/delfos/neo4j/query_execution/sync.rb +1 -0
- data/lib/delfos/neo4j/query_execution/transactional.rb +24 -13
- data/lib/delfos/neo4j/schema.rb +5 -19
- data/lib/delfos/setup.rb +76 -44
- metadata +77 -18
- data/lib/delfos/method_logging.rb +0 -52
- data/lib/delfos/method_logging/call_site_parsing.rb +0 -74
- data/lib/delfos/method_logging/code_location.rb +0 -89
- data/lib/delfos/method_logging/method_parameters.rb +0 -53
- data/lib/delfos/neo4j/call_stack_query.rb +0 -88
- data/lib/delfos/neo4j/informer.rb +0 -128
- data/lib/delfos/patching/basic_object.rb +0 -14
- data/lib/delfos/patching/basic_object_remove.rb +0 -7
- data/lib/delfos/patching/method_cache.rb +0 -83
- data/lib/delfos/patching/method_override.rb +0 -148
- data/lib/delfos/patching/module_defining_methods.rb +0 -105
- data/lib/delfos/patching/unstubber.rb +0 -34
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "execution"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module Neo4j
|
7
|
+
module Batch
|
8
|
+
class Retryable
|
9
|
+
class << self
|
10
|
+
MUTEX = Mutex.new
|
11
|
+
|
12
|
+
def reset!
|
13
|
+
MUTEX.synchronize do
|
14
|
+
Thread.current[:_delfos__retryable_batch] = nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def ensure_instance(size: nil)
|
19
|
+
MUTEX.synchronize do
|
20
|
+
Thread.current[:_delfos__retryable_batch] ||= new(size: size || 1_000)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute!(query, params: {}, size: nil)
|
25
|
+
ensure_instance(size: size).execute!(query, params: params)
|
26
|
+
end
|
27
|
+
|
28
|
+
def flush!
|
29
|
+
ensure_instance&.flush!
|
30
|
+
reset!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :size, :queries, :execution
|
35
|
+
attr_accessor :retry_count
|
36
|
+
|
37
|
+
def initialize(size:)
|
38
|
+
@size = size
|
39
|
+
reset!
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute!(query, params: {}, retrying: false)
|
43
|
+
queries.push([query, params]) unless retrying
|
44
|
+
|
45
|
+
with_retry(retrying) do
|
46
|
+
flushed = execution.execute!(query, params: params)
|
47
|
+
|
48
|
+
reset! if flushed
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def flush!
|
53
|
+
execution.flush!
|
54
|
+
reset!
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def with_retry(already_retrying)
|
60
|
+
yield
|
61
|
+
rescue QueryExecution::ExpiredTransaction
|
62
|
+
check_retry_limit! if already_retrying
|
63
|
+
|
64
|
+
retry_batch!
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_retry_limit!
|
68
|
+
self.retry_count += 1
|
69
|
+
|
70
|
+
return if self.retry_count <= 5
|
71
|
+
|
72
|
+
self.retry_count = 0
|
73
|
+
Delfos.logger.error "Transaction expired - 5 retries failed aborting"
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
|
77
|
+
def retry_batch!
|
78
|
+
Delfos.logger.error do
|
79
|
+
"Transaction expired - retrying batch. #{queries.count} queries retry_count: #{retry_count}"
|
80
|
+
end
|
81
|
+
|
82
|
+
new_execution
|
83
|
+
|
84
|
+
queries.each { |q, p| execute!(q, params: p, retrying: true) }
|
85
|
+
|
86
|
+
Delfos.logger.error { "Batch retry successful" }
|
87
|
+
end
|
88
|
+
|
89
|
+
def reset!
|
90
|
+
@queries = []
|
91
|
+
new_execution
|
92
|
+
@retry_count = 0
|
93
|
+
end
|
94
|
+
|
95
|
+
def new_execution
|
96
|
+
@execution = Execution.new(size: size)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Delfos
|
4
|
+
module Neo4j
|
5
|
+
class CallSiteQuery
|
6
|
+
def initialize(call_site, stack_uuid, step_number)
|
7
|
+
@call_site = call_site
|
8
|
+
@container_method = call_site.container_method
|
9
|
+
@called_method = call_site.called_method
|
10
|
+
@stack_uuid = stack_uuid
|
11
|
+
@step_number = step_number
|
12
|
+
end
|
13
|
+
|
14
|
+
def params
|
15
|
+
@params ||= calculate_params
|
16
|
+
end
|
17
|
+
|
18
|
+
def query
|
19
|
+
BODY
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# rubocop:disable Metrics/MethodLength
|
25
|
+
def calculate_params
|
26
|
+
params = {
|
27
|
+
"step_number" => @step_number,
|
28
|
+
"stack_uuid" => @stack_uuid,
|
29
|
+
|
30
|
+
"call_site_file" => @call_site .file,
|
31
|
+
"call_site_line_number" => @call_site .line_number,
|
32
|
+
|
33
|
+
"container_method_klass_name" => @container_method .klass_name,
|
34
|
+
"container_method_type" => @container_method .method_type,
|
35
|
+
"container_method_name" => @container_method .method_name,
|
36
|
+
"container_method_file" => @container_method .file,
|
37
|
+
"container_method_line_number" => @container_method .line_number || -1,
|
38
|
+
|
39
|
+
"called_method_klass_name" => @called_method .klass_name,
|
40
|
+
"called_method_type" => @called_method .method_type,
|
41
|
+
"called_method_name" => @called_method .method_name,
|
42
|
+
"called_method_file" => @called_method .file,
|
43
|
+
"called_method_line_number" => @called_method .line_number,
|
44
|
+
}
|
45
|
+
|
46
|
+
params
|
47
|
+
end
|
48
|
+
# rubocop:enable Metrics/MethodLength,Metrics/LineLength,Metrics/AbcSize
|
49
|
+
|
50
|
+
BODY = <<-QUERY
|
51
|
+
MERGE (container_method_klass:Class {name: {container_method_klass_name}})
|
52
|
+
MERGE (called_method_klass:Class {name: {called_method_klass_name}})
|
53
|
+
|
54
|
+
MERGE (container_method_klass) - [:OWNS] ->
|
55
|
+
(container_method:Method
|
56
|
+
{
|
57
|
+
type: {container_method_type},
|
58
|
+
name: {container_method_name},
|
59
|
+
file: {container_method_file},
|
60
|
+
line_number: {container_method_line_number}
|
61
|
+
}
|
62
|
+
)
|
63
|
+
MERGE (container_method) - [:CONTAINS] ->
|
64
|
+
(call_site:CallSite
|
65
|
+
{
|
66
|
+
file: {call_site_file},
|
67
|
+
line_number: {call_site_line_number}
|
68
|
+
}
|
69
|
+
)
|
70
|
+
|
71
|
+
MERGE (called_method_klass) - [:OWNS] ->
|
72
|
+
(called_method:Method
|
73
|
+
{
|
74
|
+
type: {called_method_type},
|
75
|
+
name: {called_method_name},
|
76
|
+
file: {called_method_file},
|
77
|
+
line_number: {called_method_line_number}
|
78
|
+
}
|
79
|
+
)
|
80
|
+
|
81
|
+
MERGE (call_site) - [:CALLS] -> (called_method)
|
82
|
+
|
83
|
+
MERGE (call_stack:CallStack{uuid: {stack_uuid}})
|
84
|
+
|
85
|
+
MERGE (call_stack) - [:STEP {number: {step_number}}] -> (call_site)
|
86
|
+
QUERY
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require "delfos/file_system"
|
3
4
|
require_relative "call_site_fetcher"
|
4
5
|
|
5
6
|
module Delfos
|
@@ -11,9 +12,7 @@ module Delfos
|
|
11
12
|
return if results.length.negative?
|
12
13
|
|
13
14
|
results.each do |start_file, call_site_id, finish_file, called_id|
|
14
|
-
|
15
|
-
|
16
|
-
update(call_site_id, called_id, calc)
|
15
|
+
handle(start_file, call_site_id, finish_file, called_id)
|
17
16
|
end
|
18
17
|
|
19
18
|
Neo4j.flush!
|
@@ -25,7 +24,7 @@ module Delfos
|
|
25
24
|
call_site_id: call_site_id,
|
26
25
|
called_id: called_id,
|
27
26
|
sum_traversals: calc.sum_traversals,
|
28
|
-
sum_possible_traversals: calc.sum_possible_traversals
|
27
|
+
sum_possible_traversals: calc.sum_possible_traversals,
|
29
28
|
)
|
30
29
|
end
|
31
30
|
|
@@ -44,6 +43,24 @@ module Delfos
|
|
44
43
|
-> (called)
|
45
44
|
QUERY
|
46
45
|
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def handle(start_file, call_site_id, finish_file, called_id)
|
50
|
+
calc = FileSystem.distance_calculation(start_file, finish_file)
|
51
|
+
|
52
|
+
update(call_site_id, called_id, calc)
|
53
|
+
rescue FileSystem::Error => e
|
54
|
+
error(e, start_file, call_site_id, finish_file, called_id)
|
55
|
+
end
|
56
|
+
|
57
|
+
def error(e, start_file, call_site_id, finish_file, called_id)
|
58
|
+
Delfos.logger.error([
|
59
|
+
"Whilst updating distance #{start_file}->#{finish_file} ",
|
60
|
+
"call_site_id: #{call_site_id}",
|
61
|
+
"called_id: #{called_id} - #{e.message} #{e.backtrace}",
|
62
|
+
].join("\n"))
|
63
|
+
end
|
47
64
|
end
|
48
65
|
end
|
49
66
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delfos/neo4j/call_site_query"
|
4
|
+
|
5
|
+
module Delfos
|
6
|
+
module Neo4j
|
7
|
+
module Live
|
8
|
+
class CallSiteLogger
|
9
|
+
def log(call_site, stack_uuid, step_number)
|
10
|
+
q = CallSiteQuery.new(call_site, stack_uuid, step_number)
|
11
|
+
|
12
|
+
Neo4j.execute(q.query, q.params)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "delfos/neo4j/call_site_query"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Delfos
|
7
|
+
module Neo4j
|
8
|
+
module Offline
|
9
|
+
class CallSiteLogger
|
10
|
+
attr_writer :count
|
11
|
+
|
12
|
+
def log(call_site, stack_uuid, step_number)
|
13
|
+
query = CallSiteQuery.new(call_site, stack_uuid, step_number)
|
14
|
+
|
15
|
+
file.puts JSON.dump(query.params)
|
16
|
+
self.count += 1
|
17
|
+
file.flush if (count % 100).zero?
|
18
|
+
end
|
19
|
+
|
20
|
+
def finish!
|
21
|
+
return if file.closed?
|
22
|
+
|
23
|
+
file.flush
|
24
|
+
file.close
|
25
|
+
end
|
26
|
+
|
27
|
+
def count
|
28
|
+
@count ||= 0
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def file
|
34
|
+
@file ||= File.open(Delfos.offline_query_filename, "a")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "delfos/neo4j"
|
5
|
+
require "delfos/neo4j/call_site_query"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
module Delfos
|
9
|
+
module Neo4j
|
10
|
+
module Offline
|
11
|
+
class Importer
|
12
|
+
attr_reader :filename
|
13
|
+
|
14
|
+
def initialize(filename)
|
15
|
+
@filename = filename
|
16
|
+
end
|
17
|
+
|
18
|
+
def perform
|
19
|
+
each_line do |params, err|
|
20
|
+
params = JSON.parse(params)
|
21
|
+
|
22
|
+
execute(CallSiteQuery::BODY, params, err)
|
23
|
+
end
|
24
|
+
|
25
|
+
Neo4j.flush!
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def execute(query, params, err)
|
31
|
+
Neo4j.execute_sync(query, params)
|
32
|
+
rescue Delfos::Neo4j::QueryExecution::InvalidQuery => e
|
33
|
+
@no_errors = false
|
34
|
+
Delfos.logger.error e.message.to_s
|
35
|
+
err.puts JSON.dump(params)
|
36
|
+
end
|
37
|
+
|
38
|
+
def each_line
|
39
|
+
with_errors do |err|
|
40
|
+
with_input do |f|
|
41
|
+
f.each_line.lazy.each do |params|
|
42
|
+
yield params, err
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_errors
|
49
|
+
@no_errors = true
|
50
|
+
|
51
|
+
error_filename = "#{filename}.errors"
|
52
|
+
|
53
|
+
File.open(error_filename, "w") do |err|
|
54
|
+
yield err
|
55
|
+
end
|
56
|
+
|
57
|
+
FileUtils.rm_rf error_filename if @no_errors
|
58
|
+
end
|
59
|
+
|
60
|
+
def with_input
|
61
|
+
File.open(filename, "r") do |f|
|
62
|
+
yield f
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,25 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
|
2
5
|
module Delfos
|
3
6
|
module Neo4j
|
4
7
|
module QueryExecution
|
5
8
|
HTTP_ERRORS = [
|
6
9
|
EOFError,
|
7
|
-
Errno::
|
10
|
+
Errno::EAGAIN,
|
8
11
|
Errno::ECONNREFUSED,
|
12
|
+
Errno::ECONNRESET,
|
13
|
+
Errno::EHOSTUNREACH,
|
9
14
|
Errno::EINVAL,
|
15
|
+
Errno::ENETDOWN,
|
16
|
+
Errno::ENETUNREACH,
|
17
|
+
Errno::ETIMEDOUT,
|
10
18
|
Net::HTTPBadResponse,
|
11
19
|
Net::HTTPHeaderSyntaxError,
|
12
20
|
Net::ProtocolError,
|
21
|
+
Net::ReadTimeout,
|
22
|
+
SocketError,
|
13
23
|
Timeout::Error,
|
14
24
|
].freeze
|
15
25
|
|
16
26
|
class InvalidQuery < IOError
|
17
27
|
def initialize(errors, query, params)
|
18
28
|
message = errors.map do |e|
|
19
|
-
e.select { |k, _| %w
|
29
|
+
e.select { |k, _| %w[code message].include?(k) }
|
20
30
|
end.join("\n")
|
21
31
|
|
22
|
-
super [message, { query: query, params: params }
|
32
|
+
super [message, { query: query.to_s }, { params: params.to_s }].join("\n\n")
|
23
33
|
end
|
24
34
|
end
|
25
35
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Delfos
|
3
4
|
module Neo4j
|
4
5
|
module QueryExecution
|
@@ -15,19 +16,20 @@ module Delfos
|
|
15
16
|
@uri = uri
|
16
17
|
end
|
17
18
|
|
19
|
+
def query_length
|
20
|
+
request_body.length
|
21
|
+
end
|
22
|
+
|
18
23
|
private
|
19
24
|
|
20
25
|
def request_body
|
21
|
-
{
|
22
|
-
"statements": [{ "statement":
|
26
|
+
@request_body ||= {
|
27
|
+
"statements": [{ "statement": formatted_query, "parameters": params }],
|
23
28
|
}.to_json
|
24
29
|
end
|
25
30
|
|
26
|
-
def
|
27
|
-
|
28
|
-
gsub(/^\s+/, "").
|
29
|
-
gsub(/ +/, " ").
|
30
|
-
gsub("\n\n", "\n")
|
31
|
+
def formatted_query
|
32
|
+
strip_whitespace(query)
|
31
33
|
end
|
32
34
|
|
33
35
|
def json
|
@@ -49,18 +51,21 @@ module Delfos
|
|
49
51
|
end
|
50
52
|
|
51
53
|
def log_query
|
52
|
-
|
54
|
+
Delfos.logger.debug do
|
55
|
+
statement = formatted_query
|
53
56
|
|
54
|
-
if statement
|
55
|
-
begin
|
56
57
|
params.each { |k, v| statement = statement.gsub("{#{k}}", v.inspect) }
|
57
|
-
|
58
|
-
byebug
|
59
|
-
end
|
60
|
-
Delfos.logger.debug "sending query: "
|
61
|
-
Delfos.logger.debug statement.gsub(/^/, " ")
|
58
|
+
statement.gsub(/^/, " ")
|
62
59
|
end
|
63
60
|
end
|
61
|
+
|
62
|
+
def strip_whitespace(s)
|
63
|
+
s.
|
64
|
+
gsub(/^\s+/, "").
|
65
|
+
gsub(/ +/, " ").
|
66
|
+
gsub("\n\n", "\n").
|
67
|
+
tr("\n", " ")
|
68
|
+
end
|
64
69
|
end
|
65
70
|
end
|
66
71
|
end
|