trace_tree 0.1.5 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +308 -114
- data/lib/trace_tree/point/call_activesupportconcern_appendfeatures.rb +17 -0
- data/lib/trace_tree/point/call_modulemutexm_extendobject.rb +17 -0
- data/lib/trace_tree/point/ccall_classthread_new.rb +11 -0
- data/lib/trace_tree/point/ccall_kernel_extend.rb +14 -0
- data/lib/trace_tree/point/ccall_module_appendfeatures.rb +15 -0
- data/lib/trace_tree/point/ccall_module_extendobject.rb +15 -0
- data/lib/trace_tree/point/ccall_module_include.rb +15 -0
- data/lib/trace_tree/point/ccall_module_prepend.rb +15 -0
- data/lib/trace_tree/point/ccall_module_prependfeatures.rb +15 -0
- data/lib/trace_tree/point/ccall_thread_initialize.rb +15 -0
- data/lib/trace_tree/point/common.rb +10 -0
- data/lib/trace_tree/point/creturn_classthread_new.rb +11 -0
- data/lib/trace_tree/point/creturn_module_appendfeatures.rb +11 -0
- data/lib/trace_tree/point/creturn_module_extendobject.rb +18 -0
- data/lib/trace_tree/point/creturn_module_prependfeatures.rb +11 -0
- data/lib/trace_tree/point/creturn_thread_initialize.rb +12 -0
- data/lib/trace_tree/point/threadbegin.rb +23 -0
- data/lib/trace_tree/point/threadend.rb +22 -0
- data/lib/trace_tree/point.rb +168 -0
- data/lib/trace_tree/short_gem_path.rb +3 -1
- data/lib/trace_tree/tree_graphable.rb +2 -0
- data/lib/trace_tree/version.rb +1 -1
- data/lib/trace_tree.rb +70 -32
- data/trace_tree.gemspec +3 -1
- metadata +51 -5
- data/lib/trace_tree/node.rb +0 -85
@@ -0,0 +1,18 @@
|
|
1
|
+
class TraceTree
|
2
|
+
class Point
|
3
|
+
class CreturnModuleExtendobject < Point
|
4
|
+
|
5
|
+
def initialize trace_point
|
6
|
+
super
|
7
|
+
@mixin = return_value.singleton_class.ancestors[1]
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :mixin
|
11
|
+
|
12
|
+
def self.event_class_method
|
13
|
+
[:c_return, Module, :extend_object]
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class TraceTree
|
2
|
+
class Point
|
3
|
+
class Threadbegin < Point
|
4
|
+
|
5
|
+
def self.event_class_method
|
6
|
+
[:thread_begin, nil, nil]
|
7
|
+
end
|
8
|
+
|
9
|
+
def class_name
|
10
|
+
''
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_name
|
14
|
+
:thread_run
|
15
|
+
end
|
16
|
+
|
17
|
+
def call_symbol
|
18
|
+
''
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class TraceTree
|
2
|
+
class Point
|
3
|
+
class Threadend < Point
|
4
|
+
|
5
|
+
def self.event_class_method
|
6
|
+
[:thread_end, nil, nil]
|
7
|
+
end
|
8
|
+
|
9
|
+
def class_name
|
10
|
+
''
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_name
|
14
|
+
:thread_run
|
15
|
+
end
|
16
|
+
|
17
|
+
def call_symbol
|
18
|
+
''
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'trace_tree/tree_graphable'
|
2
|
+
require 'trace_tree/tree_htmlable'
|
3
|
+
|
4
|
+
class TraceTree
|
5
|
+
class Point
|
6
|
+
|
7
|
+
include TreeGraphable
|
8
|
+
include TreeHtmlable
|
9
|
+
|
10
|
+
attr_reader :current, :thread
|
11
|
+
attr_accessor :terminal
|
12
|
+
|
13
|
+
Interfaces = [:event, :defined_class, :method_id, :path, :lineno]
|
14
|
+
attr_reader *Interfaces
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def inherited base
|
18
|
+
bases << base
|
19
|
+
end
|
20
|
+
|
21
|
+
def classes
|
22
|
+
@classes ||= bases.each_with_object(Hash.new{|h| h[:common]}){|c, h| h[c.event_class_method] = c}
|
23
|
+
end
|
24
|
+
|
25
|
+
def bases
|
26
|
+
@bases ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def hashify point
|
30
|
+
attrs = Interfaces.each_with_object({}) do |attr, hash|
|
31
|
+
hash[attr] = point.send attr
|
32
|
+
end
|
33
|
+
attrs.merge!({return_value: point.return_value}) if point.event =~ /return/
|
34
|
+
attrs.merge!({thread: point.thread}) if point.respond_to? :thread
|
35
|
+
attrs
|
36
|
+
end
|
37
|
+
|
38
|
+
def class_of? point
|
39
|
+
[point.event, point.defined_class, point.method_id] == event_class_method
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize_clone proto
|
43
|
+
super.tap do
|
44
|
+
instance_variable_set :@proto, proto
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :proto
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing method_id, *args, &blk
|
52
|
+
raise NoMethodError, "NoMethodError: undefined method `#{method_id}' for #<#{self.class.proto or self.class.name}#{inspect}>"
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize trace_point
|
56
|
+
Interfaces.each do |i|
|
57
|
+
instance_variable_set "@#{i}", trace_point.send(i)
|
58
|
+
end
|
59
|
+
@return_value = trace_point.return_value if x_return?
|
60
|
+
@current = trace_point.binding.of_callers[3] unless thread?
|
61
|
+
@thread = thread? ? trace_point.self : current.send(:eval, 'Thread.current')
|
62
|
+
rescue => e
|
63
|
+
puts e
|
64
|
+
end
|
65
|
+
|
66
|
+
def c_call?
|
67
|
+
event == :c_call
|
68
|
+
end
|
69
|
+
|
70
|
+
def x_return?
|
71
|
+
event =~ /return/
|
72
|
+
end
|
73
|
+
|
74
|
+
def thread?
|
75
|
+
event =~ /thread/
|
76
|
+
end
|
77
|
+
|
78
|
+
def return_value
|
79
|
+
raise RuntimeError.new('RuntimeError: not supported by this event') unless x_return?
|
80
|
+
@return_value
|
81
|
+
end
|
82
|
+
|
83
|
+
def inspect
|
84
|
+
to_h.inspect
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_h
|
88
|
+
self.class.hashify(self)
|
89
|
+
end
|
90
|
+
|
91
|
+
def terminate? point
|
92
|
+
same_method?(point) and ending?(point)
|
93
|
+
end
|
94
|
+
|
95
|
+
def same_method? point
|
96
|
+
point.defined_class == defined_class and point.method_id == method_id
|
97
|
+
end
|
98
|
+
|
99
|
+
def ending? point
|
100
|
+
(event == :b_return and point.event == :b_call) or
|
101
|
+
(event == :c_return and point.event == :c_call) or
|
102
|
+
(event == :return and point.event == :call) or
|
103
|
+
(event == :end and point.event == :class) or
|
104
|
+
(event == :thread_end and point.event == :thread_begin)
|
105
|
+
end
|
106
|
+
|
107
|
+
def << node
|
108
|
+
callees << node
|
109
|
+
end
|
110
|
+
|
111
|
+
def callees
|
112
|
+
@callees ||= []
|
113
|
+
end
|
114
|
+
|
115
|
+
def class_and_method
|
116
|
+
"#{_class_and_method}#{arg}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def _class_and_method
|
120
|
+
@km ||= "#{class_name}#{call_symbol}#{method_name}"
|
121
|
+
end
|
122
|
+
|
123
|
+
def class_name
|
124
|
+
c_call? ? defined_class : current.klass
|
125
|
+
rescue => e
|
126
|
+
puts event
|
127
|
+
end
|
128
|
+
|
129
|
+
def method_name
|
130
|
+
c_call? ? method_id : current.frame_env
|
131
|
+
end
|
132
|
+
|
133
|
+
def call_symbol
|
134
|
+
c_call? ? '#' : current.call_symbol
|
135
|
+
end
|
136
|
+
|
137
|
+
def source_location
|
138
|
+
"#{path}:#{lineno}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def arg
|
142
|
+
respond_to?(:parameters) ? "(#{parameters})" : nil
|
143
|
+
end
|
144
|
+
|
145
|
+
class Loader
|
146
|
+
|
147
|
+
attr_reader :point_classes
|
148
|
+
|
149
|
+
def initialize *enhancement
|
150
|
+
return @point_classes = Point.classes if enhancement.empty?
|
151
|
+
@point_classes = Point.classes.each_with_object(Point.classes.dup) do |entry, hash|
|
152
|
+
hash[entry[0]] = entry[1].clone.prepend *enhancement
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def create point
|
157
|
+
point_klass = point_classes[[point.event, point.defined_class, point.method_id]]
|
158
|
+
point_klass.new point
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
Dir.glob(File.expand_path('../point/*', __FILE__)).each do |concreate_point_path|
|
166
|
+
load concreate_point_path
|
167
|
+
#puts "---->#{concreate_point_path}"
|
168
|
+
end
|
@@ -4,12 +4,14 @@ class TraceTree
|
|
4
4
|
module ShortGemPath
|
5
5
|
|
6
6
|
def source_location
|
7
|
-
"#{shorten_gem_path current.file}:#{current.line}"
|
7
|
+
#"#{shorten_gem_path current.file}:#{current.line}"
|
8
|
+
"#{shorten_gem_path path}:#{lineno}"
|
8
9
|
end
|
9
10
|
|
10
11
|
private
|
11
12
|
|
12
13
|
def shorten_gem_path loc
|
14
|
+
return '' if loc.nil?
|
13
15
|
GemPaths.each{|name, path| loc = loc.gsub(path, "$#{name}")}
|
14
16
|
loc
|
15
17
|
end
|
data/lib/trace_tree/version.rb
CHANGED
data/lib/trace_tree.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require "trace_tree/version"
|
2
2
|
require 'binding_of_callers/pry'
|
3
|
-
require 'trace_tree/
|
3
|
+
require 'trace_tree/point'
|
4
4
|
require 'trace_tree/short_gem_path'
|
5
5
|
require 'trace_tree/color'
|
6
6
|
require 'trace_tree/tmp_file'
|
7
7
|
require 'trace_tree/timer'
|
8
|
+
require 'thread'
|
9
|
+
require 'terminal-tableofhashes'
|
8
10
|
|
9
11
|
class Binding
|
10
12
|
def trace_tree *log, **opt, &to_do
|
@@ -14,75 +16,111 @@ end
|
|
14
16
|
|
15
17
|
class TraceTree
|
16
18
|
|
19
|
+
Events = [:b_call, :b_return,
|
20
|
+
:c_call, :c_return,
|
21
|
+
:call, :return,
|
22
|
+
:class, :end,
|
23
|
+
:thread_begin, :thread_end]
|
24
|
+
|
17
25
|
def initialize bi
|
18
26
|
@bi = bi
|
19
|
-
@trace_points =
|
27
|
+
@trace_points = Queue.new
|
20
28
|
@timer = Timer.new
|
21
29
|
end
|
22
30
|
|
23
31
|
def generate *log, **opt, &to_do
|
24
32
|
@opt = opt
|
25
33
|
@log = dump_location *log
|
26
|
-
|
34
|
+
enhance_point **opt
|
27
35
|
@build_command = opt[:html] ? :tree_html_full : :tree_graph
|
28
|
-
|
29
|
-
bi.eval('self')
|
30
|
-
ensure
|
31
|
-
stop_trace
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
36
|
+
@ignore = opt[:ignore] || {}
|
37
|
+
here = bi.eval('self')
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
def start_trace
|
39
|
+
#start_trace
|
39
40
|
timer[:trace]
|
40
|
-
@tp = TracePoint.
|
41
|
-
trace_points <<
|
41
|
+
@tp = TracePoint.new(*Events) do |point|
|
42
|
+
trace_points << point_loader.create(point) if wanted? point
|
42
43
|
end
|
43
|
-
|
44
|
+
@tp.enable
|
44
45
|
|
45
|
-
|
46
|
+
here.instance_eval &to_do
|
47
|
+
ensure
|
48
|
+
#stop_trace
|
46
49
|
return unless @tp
|
47
50
|
@tp.disable
|
48
51
|
timer[:trace]
|
49
52
|
dump_trace_tree
|
50
53
|
end
|
51
54
|
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :bi, :trace_points, :log, :build_command, :timer, :opt, :point_loader
|
58
|
+
|
52
59
|
def dump_location *log
|
53
60
|
return TmpFile.new opt[:tmp] if opt[:tmp]
|
54
61
|
log.empty? ? STDOUT : log[0]
|
55
62
|
end
|
56
63
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
def enhance_point opt
|
65
|
+
enhancement = []
|
66
|
+
enhancement << TraceTree::Color unless opt[:color] == false
|
67
|
+
enhancement << TraceTree::ShortGemPath unless opt[:gem] == false
|
68
|
+
@point_loader = Point::Loader.new *enhancement
|
62
69
|
end
|
63
70
|
|
64
71
|
def dump_trace_tree
|
65
72
|
timer[:tree]
|
66
|
-
tree = sort(
|
73
|
+
tree = sort(trace_points_array).send build_command
|
67
74
|
timer[:tree]
|
68
75
|
log.puts tree
|
69
76
|
log.puts timer.to_s if opt[:timer]
|
77
|
+
rescue => e
|
78
|
+
log.puts timer.to_s
|
79
|
+
log.puts e
|
80
|
+
log.puts Terminal::Table.from_hashes trace_points_array.map(&:to_h)
|
70
81
|
end
|
71
82
|
|
72
83
|
def wanted? trace_point
|
73
|
-
|
84
|
+
@ignore.any? do |attr, pattern|
|
85
|
+
pattern =~ trace_point.send(attr)
|
86
|
+
end ? false : true
|
74
87
|
end
|
75
88
|
|
76
|
-
def sort
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
def sort trace_points
|
90
|
+
stacks = Hash.new{|h, thread| h[thread] = []}
|
91
|
+
initialized_threads, began_threads = {}, {}
|
92
|
+
|
93
|
+
trace_points.each do |point|
|
94
|
+
stack = stacks[point.thread]
|
95
|
+
unless stack.empty?
|
96
|
+
if point.terminate? stack.last
|
97
|
+
stack.last.terminal = point
|
98
|
+
stack.pop
|
99
|
+
else
|
100
|
+
stack.last << point
|
101
|
+
stack << point
|
102
|
+
end
|
103
|
+
else
|
104
|
+
stack << point
|
82
105
|
end
|
83
|
-
|
106
|
+
initialized_threads[point.return_value] = point if Point::CreturnThreadInitialize.class_of? point
|
107
|
+
began_threads[point.thread] = point if Point::Threadbegin.class_of? point
|
84
108
|
end
|
85
|
-
|
109
|
+
|
110
|
+
initialized_threads.each do |thread, point|
|
111
|
+
point.thread_begin = began_threads[thread]
|
112
|
+
end
|
113
|
+
|
114
|
+
stacks[trace_points.first.thread][0].
|
115
|
+
callees[0].
|
116
|
+
callees[0]
|
117
|
+
end
|
118
|
+
|
119
|
+
def trace_points_array
|
120
|
+
return @tpa if defined? @tpa
|
121
|
+
@tpa = []
|
122
|
+
@tpa << trace_points.deq until trace_points.size == 0
|
123
|
+
@tpa
|
86
124
|
end
|
87
125
|
|
88
126
|
end
|
data/trace_tree.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["ken"]
|
10
10
|
spec.email = ["block24block@gmail.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{Print TracePoint(
|
12
|
+
spec.summary = %q{Print TracePoint(:b_call, :b_return, :c_call, :c_return, :call, :return, :class, :end, :thread_begin, :thread_end) in tree view, to console or html}
|
13
13
|
spec.homepage = "https://github.com/turnon/trace_tree"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -27,4 +27,6 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_dependency "binding_of_callers", "0.1.3"
|
28
28
|
spec.add_dependency "tree_graph", "~> 0.2.0"
|
29
29
|
spec.add_dependency "tree_html", "~> 0.1.0"
|
30
|
+
spec.add_dependency "activesupport", ">= 5.0.0"
|
31
|
+
spec.add_dependency "terminal-tableofhashes", "~> 0.1.0"
|
30
32
|
end
|