delfos 0.0.1 → 0.0.2.pre.rc2
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/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
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "binding_of_caller"
|
3
|
-
|
4
|
-
module Delfos
|
5
|
-
module MethodLogging
|
6
|
-
class CallSiteParsing
|
7
|
-
# This magic number is based on the implementation within this file.
|
8
|
-
# If the line with `call_site_binding.of_caller(stack_index + STACK_OFFSET).receiver`
|
9
|
-
# is moved up or down the call stack a test fails and we have to change `STACK_OFFSET`
|
10
|
-
STACK_OFFSET = 5
|
11
|
-
|
12
|
-
attr_reader :stack, :call_site_binding
|
13
|
-
|
14
|
-
def initialize(stack, call_site_binding, stack_offset: nil)
|
15
|
-
@stack = stack
|
16
|
-
@call_site_binding = call_site_binding
|
17
|
-
@stack_offset = stack_offset
|
18
|
-
end
|
19
|
-
|
20
|
-
def perform
|
21
|
-
file, line_number, method_name = method_details
|
22
|
-
return unless current && file && line_number && method_name
|
23
|
-
|
24
|
-
CodeLocation.new(object: object,
|
25
|
-
method_name: method_name.to_s,
|
26
|
-
class_method: class_method,
|
27
|
-
file: file,
|
28
|
-
line_number: line_number)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def class_method
|
34
|
-
object.is_a? Module
|
35
|
-
end
|
36
|
-
|
37
|
-
def current
|
38
|
-
stack.detect do |s|
|
39
|
-
file = s.split(":")[0]
|
40
|
-
Delfos::MethodLogging.include_file?(file)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def object
|
45
|
-
@object ||= call_site_binding.of_caller(stack_index + stack_offset).receiver
|
46
|
-
end
|
47
|
-
|
48
|
-
def stack_offset
|
49
|
-
@stack_offset ||= STACK_OFFSET
|
50
|
-
end
|
51
|
-
|
52
|
-
def stack_index
|
53
|
-
stack.index { |c| c == current }
|
54
|
-
end
|
55
|
-
|
56
|
-
METHOD_NAME_REGEX = /`(.*)'$/
|
57
|
-
|
58
|
-
def method_details
|
59
|
-
return unless current
|
60
|
-
file, line_number, rest, more = current.split(":")
|
61
|
-
|
62
|
-
rest = more.nil? ? rest : "#{rest}:#{more}"
|
63
|
-
method_name = rest.match(METHOD_NAME_REGEX)&.[](1)
|
64
|
-
|
65
|
-
return unless method_name && file && line_number
|
66
|
-
|
67
|
-
method_name = method_name.delete("`")
|
68
|
-
method_name = method_name.delete("'")
|
69
|
-
|
70
|
-
[file, line_number.to_i, method_name]
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative "call_site_parsing"
|
3
|
-
|
4
|
-
module Delfos
|
5
|
-
module MethodLogging
|
6
|
-
class CodeLocation
|
7
|
-
class << self
|
8
|
-
def from_call_site(stack, call_site_binding)
|
9
|
-
CallSiteParsing.new(stack, call_site_binding).perform
|
10
|
-
end
|
11
|
-
|
12
|
-
def from_called(object, called_method, class_method)
|
13
|
-
file, line_number = called_method.source_location
|
14
|
-
return unless file && line_number
|
15
|
-
|
16
|
-
new(object: object, method_name: called_method.name.to_s,
|
17
|
-
class_method: class_method, file: file, line_number: line_number)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
attr_reader :object, :method_name, :class_method, :line_number
|
23
|
-
|
24
|
-
def initialize(object:, method_name:, class_method:, file:, line_number:)
|
25
|
-
@object = object
|
26
|
-
@method_name = method_name
|
27
|
-
@class_method = class_method
|
28
|
-
@line_number = line_number.to_i
|
29
|
-
@file = file
|
30
|
-
end
|
31
|
-
|
32
|
-
def file
|
33
|
-
relative_filename @file
|
34
|
-
end
|
35
|
-
|
36
|
-
def klass
|
37
|
-
object.is_a?(Class) ? object : object.class
|
38
|
-
end
|
39
|
-
|
40
|
-
def method_definition_file
|
41
|
-
relative_filename(method_definition&.first || fallback_method_definition_file)
|
42
|
-
end
|
43
|
-
|
44
|
-
def method_definition_line
|
45
|
-
method_definition&.last&.to_i || fallback_method_definition_line_number
|
46
|
-
end
|
47
|
-
|
48
|
-
def method_type
|
49
|
-
class_method ? "ClassMethod" : "InstanceMethod"
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def relative_filename(f)
|
55
|
-
return unless f
|
56
|
-
file = f.to_s
|
57
|
-
|
58
|
-
Delfos.application_directories.map do |d|
|
59
|
-
file = relative_path(file, d)
|
60
|
-
end
|
61
|
-
|
62
|
-
file
|
63
|
-
end
|
64
|
-
|
65
|
-
def relative_path(file, dir)
|
66
|
-
match = dir.to_s.split("/")[0..-2].join("/")
|
67
|
-
|
68
|
-
if file[match]
|
69
|
-
file = file.gsub(match, "").
|
70
|
-
gsub(%r{^/}, "")
|
71
|
-
end
|
72
|
-
|
73
|
-
file
|
74
|
-
end
|
75
|
-
|
76
|
-
def fallback_method_definition_file
|
77
|
-
@file
|
78
|
-
end
|
79
|
-
|
80
|
-
def fallback_method_definition_line_number
|
81
|
-
0
|
82
|
-
end
|
83
|
-
|
84
|
-
def method_definition
|
85
|
-
@method_definition ||= Patching::MethodCache.find(klass: klass, method_name: method_name, class_method: class_method)&.source_location
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "delfos/patching/method_cache"
|
3
|
-
|
4
|
-
module Delfos
|
5
|
-
module MethodLogging
|
6
|
-
class MethodParameters
|
7
|
-
attr_reader :block
|
8
|
-
|
9
|
-
def initialize(args=[], keyword_args=nil, block=nil)
|
10
|
-
@raw_args = args
|
11
|
-
@raw_keyword_args = keyword_args
|
12
|
-
@block = block
|
13
|
-
end
|
14
|
-
|
15
|
-
def args
|
16
|
-
@args ||= calculate_args(@raw_args)
|
17
|
-
end
|
18
|
-
|
19
|
-
def argument_classes
|
20
|
-
(args + keyword_args).uniq
|
21
|
-
end
|
22
|
-
|
23
|
-
def keyword_args
|
24
|
-
@keyword_args ||= calculate_args(@raw_keyword_args.values)
|
25
|
-
end
|
26
|
-
|
27
|
-
def as_json(*params)
|
28
|
-
{arguments: args, keyword_arguments: keyword_args}
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def calculate_args(arguments)
|
34
|
-
arguments.
|
35
|
-
map { |o| o.is_a?(Class) ? o : o.class }.
|
36
|
-
select { |k| keep?(k) }
|
37
|
-
end
|
38
|
-
|
39
|
-
def keep?(klass)
|
40
|
-
files_for(klass).
|
41
|
-
any? { |f| record?(f) }
|
42
|
-
end
|
43
|
-
|
44
|
-
def files_for(klass)
|
45
|
-
Patching::MethodCache.files_for(klass)
|
46
|
-
end
|
47
|
-
|
48
|
-
def record?(f)
|
49
|
-
Delfos::MethodLogging.include_file?(f)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,88 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module Delfos
|
3
|
-
module Neo4j
|
4
|
-
class CallStackQuery
|
5
|
-
def initialize(call_sites, execution_count)
|
6
|
-
@call_sites = call_sites
|
7
|
-
|
8
|
-
@execution_count = execution_count
|
9
|
-
end
|
10
|
-
|
11
|
-
def query
|
12
|
-
map_call_sites do |c, i|
|
13
|
-
call_site_query(c, i)
|
14
|
-
end.join("\n")
|
15
|
-
end
|
16
|
-
|
17
|
-
def params
|
18
|
-
params = {}
|
19
|
-
|
20
|
-
map_call_sites do |c, i|
|
21
|
-
params.merge!(call_site_params(c, i))
|
22
|
-
end
|
23
|
-
|
24
|
-
params
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
attr_reader :call_sites, :execution_count
|
30
|
-
|
31
|
-
def map_call_sites
|
32
|
-
call_sites.compact.map.with_index do |c, i|
|
33
|
-
yield c, i
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def call_site_query(_cs, i)
|
38
|
-
<<-QUERY
|
39
|
-
MERGE
|
40
|
-
|
41
|
-
(
|
42
|
-
k#{i}:Class { name: {klass#{i}} }
|
43
|
-
)
|
44
|
-
|
45
|
-
- [:OWNS] ->
|
46
|
-
|
47
|
-
(
|
48
|
-
m#{i} :Method {
|
49
|
-
type: {method_type#{i}},
|
50
|
-
name: {method_name#{i}},
|
51
|
-
file: {method_definition_file#{i}},
|
52
|
-
line_number: {method_definition_line#{i}}}
|
53
|
-
)
|
54
|
-
|
55
|
-
MERGE
|
56
|
-
|
57
|
-
(m#{i})
|
58
|
-
|
59
|
-
-[:CONTAINS]->
|
60
|
-
|
61
|
-
(cs#{i}:CallSite {file: {file#{i}}, line_number: {line_number#{i}}})
|
62
|
-
|
63
|
-
MERGE (e#{i}:CallStack{number: {execution_count#{i}}})
|
64
|
-
|
65
|
-
MERGE (e#{i})
|
66
|
-
-
|
67
|
-
[:STEP {number: {step_number#{i}}}]
|
68
|
-
->
|
69
|
-
(cs#{i})
|
70
|
-
QUERY
|
71
|
-
end
|
72
|
-
|
73
|
-
def call_site_params(cs, i)
|
74
|
-
{
|
75
|
-
"klass#{i}" => cs.klass.to_s,
|
76
|
-
"method_name#{i}" => cs.method_name,
|
77
|
-
"method_type#{i}" => cs.method_type,
|
78
|
-
"method_definition_file#{i}" => cs.method_definition_file,
|
79
|
-
"execution_count#{i}" => execution_count,
|
80
|
-
"method_definition_line#{i}" => cs.method_definition_line,
|
81
|
-
"file#{i}" => cs.file,
|
82
|
-
"line_number#{i}" => cs.line_number,
|
83
|
-
"step_number#{i}" => i + 1,
|
84
|
-
}
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
@@ -1,128 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Delfos
|
4
|
-
module Neo4j
|
5
|
-
class Informer
|
6
|
-
def save_call_stack(call_sites, execution_number)
|
7
|
-
q = Neo4j::CallStackQuery.new(call_sites, execution_number)
|
8
|
-
Neo4j.execute(q.query, q.params)
|
9
|
-
end
|
10
|
-
|
11
|
-
def log(args, call_site, called_code)
|
12
|
-
query = query_for(args, call_site, called_code)
|
13
|
-
params = params_for(args, call_site, called_code)
|
14
|
-
|
15
|
-
Neo4j.execute(query, params)
|
16
|
-
end
|
17
|
-
|
18
|
-
def params_for(args, call_site, called_code)
|
19
|
-
assign_query_variables(args, call_site, called_code)
|
20
|
-
|
21
|
-
params = query_variables.each_with_object({}) do |(klass, name), object|
|
22
|
-
object[name] = klass.to_s
|
23
|
-
end
|
24
|
-
|
25
|
-
params["m1_type"] = call_site.method_type
|
26
|
-
params["m1_name"] = call_site.method_name
|
27
|
-
params["m1_file"] = call_site.method_definition_file
|
28
|
-
params["m1_line_number"] = call_site.method_definition_line
|
29
|
-
|
30
|
-
params["cs_file"] = call_site.file
|
31
|
-
params["cs_line_number"] = call_site.line_number
|
32
|
-
|
33
|
-
params["m2_type"] = called_code.method_type
|
34
|
-
params["m2_name"] = called_code.method_name
|
35
|
-
params["m2_file"] = called_code.method_definition_file
|
36
|
-
params["m2_line_number"] = called_code.method_definition_line
|
37
|
-
|
38
|
-
params
|
39
|
-
end
|
40
|
-
|
41
|
-
def query_for(args, call_site, called_code)
|
42
|
-
assign_query_variables(args, call_site, called_code)
|
43
|
-
|
44
|
-
klasses_query = query_variables.map do |_klass, name|
|
45
|
-
"MERGE (#{name}:Class {name: {#{name}}})"
|
46
|
-
end.join("\n")
|
47
|
-
|
48
|
-
<<-QUERY
|
49
|
-
#{klasses_query}
|
50
|
-
|
51
|
-
MERGE (#{query_variable(call_site.klass)}) - [:OWNS] ->
|
52
|
-
#{method_node("m1")}
|
53
|
-
|
54
|
-
MERGE (m1) - [:CONTAINS] ->
|
55
|
-
(cs:CallSite
|
56
|
-
{
|
57
|
-
file: {cs_file},
|
58
|
-
line_number: {cs_line_number}
|
59
|
-
}
|
60
|
-
)
|
61
|
-
|
62
|
-
MERGE (#{query_variable(called_code.klass)}) - [:OWNS] ->
|
63
|
-
#{method_node("m2")}
|
64
|
-
|
65
|
-
MERGE (cs) - [:CALLS] -> (m2)
|
66
|
-
|
67
|
-
#{args_query args}
|
68
|
-
QUERY
|
69
|
-
end
|
70
|
-
|
71
|
-
def assign_query_variables(args, call_site, called_code)
|
72
|
-
klasses = [call_site.klass, called_code.klass] + args.argument_classes
|
73
|
-
|
74
|
-
klasses.uniq.each do |k|
|
75
|
-
query_variables.assign(k, "k")
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def method_node(id)
|
80
|
-
<<-NODE
|
81
|
-
(#{id}:Method
|
82
|
-
{
|
83
|
-
type: {#{id}_type},
|
84
|
-
name: {#{id}_name},
|
85
|
-
file: {#{id}_file},
|
86
|
-
line_number: {#{id}_line_number}
|
87
|
-
}
|
88
|
-
)
|
89
|
-
NODE
|
90
|
-
end
|
91
|
-
|
92
|
-
def args_query(args)
|
93
|
-
query_text = args.argument_classes.map do |k|
|
94
|
-
name = query_variable(k)
|
95
|
-
"MERGE (cs) - [:ARG] -> (#{name})"
|
96
|
-
end
|
97
|
-
|
98
|
-
query_text.join("\n")
|
99
|
-
end
|
100
|
-
|
101
|
-
def query_variable(k)
|
102
|
-
query_variables[k.to_s]
|
103
|
-
end
|
104
|
-
|
105
|
-
def query_variables
|
106
|
-
@query_variables ||= QueryVariables.new
|
107
|
-
end
|
108
|
-
|
109
|
-
class QueryVariables < Hash
|
110
|
-
def initialize(*args)
|
111
|
-
super(*args)
|
112
|
-
@counters = Hash.new(1)
|
113
|
-
end
|
114
|
-
|
115
|
-
def assign(klass, prefix)
|
116
|
-
klass = klass.to_s
|
117
|
-
val = self[klass]
|
118
|
-
return val if val
|
119
|
-
|
120
|
-
"#{prefix}#{@counters[prefix]}".tap do |v|
|
121
|
-
self[klass] = v
|
122
|
-
@counters[prefix] += 1
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require_relative "method_override"
|
3
|
-
|
4
|
-
class BasicObject
|
5
|
-
def self.method_added(name)
|
6
|
-
::Delfos::Patching::MethodOverride.setup(self, name, private_instance_methods, class_method: false)
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.singleton_method_added(name)
|
10
|
-
return if %i(define_method extended included inherited method_added singleton_method_added).include?(name)
|
11
|
-
|
12
|
-
::Delfos::Patching::MethodOverride.setup(self, name, private_methods, class_method: true)
|
13
|
-
end
|
14
|
-
end
|