This repository was archived by the owner on Dec 16, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathprofile-parser.rb
executable file
·162 lines (127 loc) · 3.07 KB
/
profile-parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env ruby
require 'terminal-table'
require 'colored'
class Namespace
attr_accessor :object
def initialize(namespace, object)
@namespace = namespace.split('.')
@object = object
@children = {}
end
def add(ns, object)
parts = ns.split('.')
child_ns = parts[0..-2]
child_id = parts.last
if(parts == @namespace)
# We are the object
@object = object
elsif @namespace == child_ns
get(child_id).add(ns, object)
else
id = parts.drop(@namespace.size).first
child = get(id)
child.add(ns, object)
end
end
def get(id)
@children[id] ||= Namespace.new([@namespace, id].flatten.join('.'), nil)
end
def display
print " " * @namespace.size
print "#{@namespace.join('.').green} "
print @object.inspect
puts
@children.values.each(&:display)
end
end
class Slice
attr_reader :id
attr_reader :time
end
class FunctionSlice < Slice
attr_reader :function
alias name function
def parse(line)
match = line.match(/([\d\.]+) Called (\S+): took ([\d\.]+) seconds/)
@id = match[1]
@function = match[2]
@time = match[3].to_f
self
end
def inspect
time = "(#{@time} seconds)".yellow
"function #{@function} #{time}"
end
end
class ResourceSlice < Slice
attr_reader :type
alias name type
attr_reader :title
def parse(line)
match = line.match(/([\d\.]+) Evaluated resource ([\w:]+)\[(.*)\]: took ([\d\.]+) seconds$/)
@id = match[1]
@type = match[2]
@title = match[3]
@time = match[4].to_f
self
end
def inspect
time = "(#{@time} seconds)".yellow
"resource #{@type}[#{@title}] #{time}"
end
end
class OtherSlice < Slice
attr_reader :name
def parse(line)
match = line.match(/PROFILE \[\d+\] ([\d\.]+) (.*): took ([\d\.]+) seconds$/)
@id = match[1]
@name = match[2]
@time = match[3]
self
end
def inspect
time = "(#{@time} seconds)".yellow
"#{@name} #{time}"
end
end
def parse(line)
case line
when /Called/
FunctionSlice.new.parse(line)
when /Evaluated resource/
ResourceSlice.new.parse(line)
else
OtherSlice.new.parse(line)
end
end
def process_group(title, slices)
total = 0.0
itemized_totals = Hash.new { |h, k| h[k] = 0.0 }
slices.each do |slice|
total += slice.time
itemized_totals[slice.name] += slice.time
end
rows = itemized_totals.to_a.sort { |a, b| b[1] <=> a[1] }
table = Terminal::Table.new(:headings => ["Source", "Time"], :rows => rows)
puts "--- #{title} ---"
puts "Total time: #{total}"
puts "Itemized:"
puts table
end
things = []
ARGF.each_line do |line|
next unless line.match(/PROFILE/)
# Strip off leader
if (match = line.match(/(PROFILE.*)$/))
things << parse(match[1])
end
end
root = Namespace.new('1', nil)
things.each do |thing|
root.add(thing.id, thing)
end
root.display
funcalls = things.select { |thing| thing.is_a? FunctionSlice }
resevals = things.select { |thing| thing.is_a? ResourceSlice }
process_group("Function calls", funcalls)
process_group("Resource evaluations", resevals)