timmy 0.4.0 → 0.5.0
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/timmy.rb +1 -0
- data/lib/timmy/command_streamer.rb +58 -0
- data/lib/timmy/config_loader.rb +4 -0
- data/lib/timmy/logger.rb +56 -20
- data/lib/timmy/option_parser.rb +21 -4
- data/lib/timmy/runner.rb +24 -8
- data/lib/timmy/targeted_timer_manager.rb +6 -2
- data/lib/timmy/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dffd420c4f6701cdf73b18488939d50783a51a9
|
4
|
+
data.tar.gz: 57c3f635a25771abe102b10ae5739c0ccbd0ff2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 882a7604e4d4d90e293a88411c4fa9c8b2a85119bfaaf74ccb1ef49bfe17270cd9855e46aaf1a0bd52b706ad482f3934844d8a434732b2d0f421f8906283b64c
|
7
|
+
data.tar.gz: ea0f80623f0ef1173c4bf8e03399322e70ca0b4010e547e23193775eed20fc232fddefd03fea1a52763ab67831be1ba31a4a03b2a244bee2e3f0f308159a39b7
|
data/lib/timmy.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Timmy
|
5
|
+
class CommandStreamer
|
6
|
+
class << self
|
7
|
+
def stream(command, &block)
|
8
|
+
self.new(command).stream(&block)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(command)
|
13
|
+
@command = command
|
14
|
+
@queue = Queue.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def stream(&block)
|
18
|
+
Open3.popen3(*@command) do |stdin, stdout, stderr, wait_thr|
|
19
|
+
start_readers(stdout: stdout, stderr: stderr)
|
20
|
+
pop_lines_from_active_readers(block)
|
21
|
+
join_readers()
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def start_readers(streams)
|
28
|
+
@readers = streams.map do |type, stream|
|
29
|
+
thread = Thread.new do
|
30
|
+
until (line = stream.gets).nil? do
|
31
|
+
@queue << [type, line]
|
32
|
+
end
|
33
|
+
@queue << [type, nil]
|
34
|
+
end
|
35
|
+
|
36
|
+
[type, thread]
|
37
|
+
end.to_h
|
38
|
+
|
39
|
+
@active_readers = @readers.keys
|
40
|
+
end
|
41
|
+
|
42
|
+
def pop_lines_from_active_readers(delegate)
|
43
|
+
while @active_readers.any? do
|
44
|
+
type, line = @queue.pop
|
45
|
+
|
46
|
+
if line
|
47
|
+
delegate.call(type, line)
|
48
|
+
else
|
49
|
+
@active_readers.delete(type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def join_readers
|
55
|
+
@readers.values.each(&:join)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/timmy/config_loader.rb
CHANGED
data/lib/timmy/logger.rb
CHANGED
@@ -5,6 +5,10 @@ module Timmy
|
|
5
5
|
@output_dir = File.expand_path(dir)
|
6
6
|
end
|
7
7
|
|
8
|
+
def set_quiet(quiet)
|
9
|
+
@quiet = quiet
|
10
|
+
end
|
11
|
+
|
8
12
|
def set_precision(precision)
|
9
13
|
@precision = precision
|
10
14
|
end
|
@@ -13,38 +17,61 @@ module Timmy
|
|
13
17
|
@profile = profile
|
14
18
|
end
|
15
19
|
|
16
|
-
def
|
20
|
+
def match_replay_header(line)
|
21
|
+
line.match(/^TIMMY-SESSION:v1:(?<s>\d+\.\d{9})$/)
|
22
|
+
end
|
23
|
+
|
24
|
+
def match_replay_line(line)
|
25
|
+
line.match(/^(?<s>\d+(\.\d+)?)(?<t>e)? (?<content>.*)/)
|
26
|
+
end
|
27
|
+
|
28
|
+
def put_output(output, error = false)
|
17
29
|
duration = MasterTimer.get
|
18
|
-
formatted_duration = format_duration(duration)
|
19
30
|
|
20
|
-
|
31
|
+
if @quiet
|
32
|
+
puts output
|
33
|
+
else
|
34
|
+
formatted_duration = format_duration(duration)
|
35
|
+
formatted_duration = red(formatted_duration) if error
|
36
|
+
puts "\e[0m" + feint(formatted_duration) + " " + output
|
37
|
+
end
|
38
|
+
$stdout.flush
|
21
39
|
|
22
40
|
@output ||= ''
|
23
|
-
@output += sprintf("%.9f %s\n", duration, output)
|
41
|
+
@output += sprintf("%.9f%s %s\n", duration, error ? 'e' : '', output)
|
24
42
|
end
|
25
43
|
|
26
44
|
def put_eof
|
27
|
-
put_output(feint("EOF"))
|
45
|
+
put_output(feint("EOF")) unless @quiet
|
28
46
|
end
|
29
47
|
|
30
48
|
def put_timer(timer)
|
31
|
-
|
49
|
+
do_put_timer(timer) unless @quiet
|
32
50
|
end
|
33
51
|
|
34
52
|
def finalize
|
53
|
+
save
|
54
|
+
put_profile if profile?
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def save
|
35
60
|
suffix = "#{MasterTimer.start.to_i}+#{MasterTimer.get.to_i}"
|
36
61
|
filename = File.join(output_dir, "timmy-#{suffix}.log")
|
37
|
-
header = sprintf("TIMMY-SESSION:v1:%.9f\n", MasterTimer.start)
|
38
62
|
|
39
|
-
File.
|
63
|
+
return if File.exists?(filename)
|
40
64
|
|
65
|
+
header = sprintf("TIMMY-SESSION:v1:%.9f\n", MasterTimer.start)
|
66
|
+
File.write(filename, header + @output)
|
41
67
|
puts feint("Log written to #{filename}")
|
42
68
|
puts
|
43
|
-
|
44
|
-
put_profile if profile?
|
45
69
|
end
|
46
70
|
|
47
|
-
|
71
|
+
def do_put_timer(timer)
|
72
|
+
puts format_timer(timer)
|
73
|
+
$stdout.flush
|
74
|
+
end
|
48
75
|
|
49
76
|
def put_profile
|
50
77
|
slowest_timers = TargetedTimerManager
|
@@ -64,9 +91,14 @@ module Timmy
|
|
64
91
|
end
|
65
92
|
|
66
93
|
def format_timer(timer)
|
67
|
-
string =
|
68
|
-
|
69
|
-
|
94
|
+
string = (
|
95
|
+
bold(format_duration(timer.duration)) +
|
96
|
+
" " +
|
97
|
+
bold(green(format_id(timer.definition.id)))
|
98
|
+
)
|
99
|
+
|
100
|
+
string += " " + green("(" + timer.group + ")") if timer.group
|
101
|
+
string += " #{timer.label}" if timer.label
|
70
102
|
|
71
103
|
string
|
72
104
|
end
|
@@ -80,16 +112,20 @@ module Timmy
|
|
80
112
|
sprintf(format, duration / 60, duration % 60)
|
81
113
|
end
|
82
114
|
|
83
|
-
def green(string)
|
84
|
-
"\e[0m\e[32m#{string}\e[0m"
|
85
|
-
end
|
86
|
-
|
87
115
|
def bold(string)
|
88
|
-
"\e[
|
116
|
+
"\e[1m#{string}\e[0m"
|
89
117
|
end
|
90
118
|
|
91
119
|
def feint(string)
|
92
|
-
"\e[
|
120
|
+
"\e[2m#{string}\e[0m"
|
121
|
+
end
|
122
|
+
|
123
|
+
def green(string)
|
124
|
+
"\e[32m#{string}\e[0m"
|
125
|
+
end
|
126
|
+
|
127
|
+
def red(string)
|
128
|
+
"\e[31m#{string}\e[0m"
|
93
129
|
end
|
94
130
|
|
95
131
|
def output_dir
|
data/lib/timmy/option_parser.rb
CHANGED
@@ -2,6 +2,14 @@ module Timmy
|
|
2
2
|
class OptionParser
|
3
3
|
class << self
|
4
4
|
def parse
|
5
|
+
if command_start_at = ARGV.find_index { |arg| arg == '--' }
|
6
|
+
command = ARGV.slice!(command_start_at, ARGV.length - command_start_at)[1..-1]
|
7
|
+
elsif ARGV[0] && !ARGV[0].start_with?('-')
|
8
|
+
command = ARGV.slice!(0, ARGV.length)
|
9
|
+
else
|
10
|
+
command = nil
|
11
|
+
end
|
12
|
+
|
5
13
|
opts = {}
|
6
14
|
|
7
15
|
::OptionParser.new do |parser|
|
@@ -10,19 +18,25 @@ module Timmy
|
|
10
18
|
|
11
19
|
\e[33mUsage:\e[0m
|
12
20
|
|
13
|
-
Pipe output from
|
21
|
+
Pipe output from command:
|
22
|
+
|
23
|
+
\e[36mCOMMAND | timmy [OPTIONS]\e[0m
|
14
24
|
|
15
|
-
|
25
|
+
Pass command as argument (records STDERR, gives more precise results):
|
26
|
+
|
27
|
+
\e[36mtimmy [OPTIONS --] COMMAND\e[0m
|
16
28
|
|
17
29
|
Replay previous session:
|
18
30
|
|
19
|
-
\e[36mcat
|
31
|
+
\e[36mcat LOGFILE | timmy [OPTIONS]\e[0m
|
20
32
|
EOS
|
21
33
|
|
22
34
|
parser.separator ""
|
23
35
|
parser.separator "\e[33mOptions:\e[0m"
|
24
36
|
parser.separator ""
|
25
37
|
|
38
|
+
parser.on("-q", "--quiet",
|
39
|
+
"Don't print times and targeted timers (default: false)")
|
26
40
|
parser.on("-p", "--precision NUM", Integer,
|
27
41
|
"Set precision used when printing time (default: 0)")
|
28
42
|
parser.on("-r", "--[no-]profile",
|
@@ -33,7 +47,10 @@ EOS
|
|
33
47
|
parser.separator ""
|
34
48
|
end.parse!(into: opts)
|
35
49
|
|
36
|
-
opts.map { |key, value| [key.to_s.gsub('-', '_').to_sym, value] }.to_h
|
50
|
+
opts = opts.map { |key, value| [key.to_s.gsub('-', '_').to_sym, value] }.to_h
|
51
|
+
opts[:command] = command if command
|
52
|
+
|
53
|
+
opts
|
37
54
|
end
|
38
55
|
end
|
39
56
|
end
|
data/lib/timmy/runner.rb
CHANGED
@@ -10,10 +10,17 @@ module Timmy
|
|
10
10
|
|
11
11
|
options = OptionParser.parse
|
12
12
|
replay_speed = options[:replay_speed] || @replay_speed
|
13
|
+
Logger.set_quiet(options[:quiet]) if options.key?(:quiet)
|
13
14
|
Logger.set_precision(options[:precision]) if options.key?(:precision)
|
14
15
|
Logger.set_profile(options[:profile]) if options.key?(:profile)
|
15
16
|
|
16
|
-
self.new(replay_speed: replay_speed)
|
17
|
+
instance = self.new(replay_speed: replay_speed)
|
18
|
+
|
19
|
+
if command = options[:command]
|
20
|
+
instance.stream_command(command)
|
21
|
+
else
|
22
|
+
instance.consume_stdin()
|
23
|
+
end
|
17
24
|
end
|
18
25
|
end
|
19
26
|
|
@@ -22,6 +29,14 @@ module Timmy
|
|
22
29
|
@last_replay_time = 0
|
23
30
|
end
|
24
31
|
|
32
|
+
def stream_command(command)
|
33
|
+
around_run_lines do
|
34
|
+
CommandStreamer.stream(command) do |type, line|
|
35
|
+
run_line(line.rstrip, type)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
25
40
|
def consume_stdin
|
26
41
|
around_run_lines do
|
27
42
|
STDIN.each_line do |line|
|
@@ -33,8 +48,6 @@ module Timmy
|
|
33
48
|
run_line(line.rstrip)
|
34
49
|
end
|
35
50
|
end
|
36
|
-
|
37
|
-
Logger.put_eof unless @replay_mode
|
38
51
|
end
|
39
52
|
end
|
40
53
|
|
@@ -42,7 +55,7 @@ module Timmy
|
|
42
55
|
|
43
56
|
def init_replay_mode(line)
|
44
57
|
if @replay_mode == nil
|
45
|
-
if (match =
|
58
|
+
if (match = Logger.match_replay_header(line))
|
46
59
|
MasterTimer.start(match[:s].to_f)
|
47
60
|
@replay_mode = true
|
48
61
|
else
|
@@ -56,13 +69,16 @@ module Timmy
|
|
56
69
|
|
57
70
|
yield
|
58
71
|
|
72
|
+
Logger.put_eof unless @replay_mode
|
59
73
|
TargetedTimerManager.stop_all
|
60
74
|
Logger.finalize
|
61
75
|
end
|
62
76
|
|
63
77
|
def replay_line(line)
|
64
|
-
line_match =
|
78
|
+
line_match = Logger.match_replay_line(line)
|
79
|
+
line_content = line_match[:content]
|
65
80
|
line_time = line_match[:s].to_f
|
81
|
+
line_type = (line_match.send(:[], :t) rescue nil) == 'e' ? :stderr : :stdout
|
66
82
|
|
67
83
|
duration = line_time - @last_replay_time
|
68
84
|
sleep duration / @replay_speed if @replay_speed
|
@@ -70,12 +86,12 @@ module Timmy
|
|
70
86
|
|
71
87
|
MasterTimer.set(line_time)
|
72
88
|
|
73
|
-
run_line(
|
89
|
+
run_line(line_content, line_type)
|
74
90
|
end
|
75
91
|
|
76
|
-
def run_line(line)
|
92
|
+
def run_line(line, type = :stdout)
|
77
93
|
TargetedTimerManager.start_for_line(line)
|
78
|
-
Logger.put_output(line)
|
94
|
+
Logger.put_output(line, type == :stderr)
|
79
95
|
TargetedTimerManager.stop_for_line(line)
|
80
96
|
end
|
81
97
|
end
|
@@ -3,7 +3,7 @@ module Timmy
|
|
3
3
|
class << self
|
4
4
|
def start_for_line(line)
|
5
5
|
TargetedTimerDefinition.all.each do |definition|
|
6
|
-
if match = line
|
6
|
+
if match = match_line(line, definition.start_regex)
|
7
7
|
label = get_capture(match, :label)
|
8
8
|
group = get_capture(match, :group)
|
9
9
|
|
@@ -16,7 +16,7 @@ module Timmy
|
|
16
16
|
def stop_for_line(line)
|
17
17
|
started.each do |timer|
|
18
18
|
if (stop_regex = timer.definition.stop_regex) &&
|
19
|
-
(match = line
|
19
|
+
(match = match_line(line, stop_regex)) &&
|
20
20
|
get_capture(match, :group) == timer.group
|
21
21
|
stop(timer)
|
22
22
|
end
|
@@ -53,6 +53,10 @@ module Timmy
|
|
53
53
|
|
54
54
|
private
|
55
55
|
|
56
|
+
def match_line(line, regex)
|
57
|
+
line.gsub(/(\x1b\[[0-9;]*m)/, '').match(regex)
|
58
|
+
end
|
59
|
+
|
56
60
|
def get_capture(match, name)
|
57
61
|
match[name]
|
58
62
|
rescue IndexError
|
data/lib/timmy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timmy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karol Słuszniak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,6 +62,7 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- bin/timmy
|
64
64
|
- lib/timmy.rb
|
65
|
+
- lib/timmy/command_streamer.rb
|
65
66
|
- lib/timmy/config_loader.rb
|
66
67
|
- lib/timmy/logger.rb
|
67
68
|
- lib/timmy/master_timer.rb
|