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
@@ -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
|