trace_tree 0.1.5 → 0.2.1
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/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
|