pry-timetravel 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -0
- data/lib/pry-timetravel.rb +87 -36
- data/lib/pry-timetravel/commands.rb +10 -7
- data/pry-timetravel.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 036cd80689a69996c2e965251b5eea0d8ad65958
|
4
|
+
data.tar.gz: 4469ac880bb66bcea64873f4cc423f06c2603a76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 948b4b85a37039e6829ff9a511ce3cf9c40f3c28d10b0f62e3d9726089c7e2280a9350553f903e15cbce77a36efc5334a1cd917a6c0cb23c0e35fd835204c727
|
7
|
+
data.tar.gz: c045d71745d255a7e3c37978cc212a666cde0e40adb625deded075b8323283fc12c070b7377e7b8dcbb3267ba50c05cc97e43f24740d8e6c1c95d62b92e62a75
|
data/README.md
CHANGED
@@ -26,6 +26,27 @@ There are a few more details, but that is the important bit.
|
|
26
26
|
|
27
27
|
WARNING: Time travel may cause zombies.
|
28
28
|
|
29
|
+
## KNOWN ISSUES
|
30
|
+
|
31
|
+
### Redis fork detection
|
32
|
+
|
33
|
+
Redis checks to see if you forked and yells at you about needing to reconnect.
|
34
|
+
Reasonably so, as it is a crazy idea to use the same connection in forked
|
35
|
+
children! But we are doing crazy things. In 3.1.0 the gem will auto-reconnect,
|
36
|
+
or you can pass inherit_socket to the connection to stop that. Or you can do
|
37
|
+
this to bypass safety measures:
|
38
|
+
|
39
|
+
class Redis
|
40
|
+
class Client
|
41
|
+
def ensure_connected
|
42
|
+
yield
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Maybe this will be a default hack that gets activated on your first snapshot at
|
48
|
+
some point in the near future.
|
49
|
+
|
29
50
|
## Meta
|
30
51
|
|
31
52
|
Released under the MIT license, see LICENSE.MIT for details. License is
|
data/lib/pry-timetravel.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'pry'
|
3
|
+
require 'json'
|
3
4
|
|
4
|
-
|
5
|
+
require_relative 'pry-timetravel/commands'
|
5
6
|
|
6
7
|
class PryTimetravel
|
7
8
|
class << self
|
@@ -23,92 +24,142 @@ class PryTimetravel
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def enter_suspended_animation
|
27
|
+
dlog("Installing SIGCONT trap")
|
26
28
|
old_sigcont_handler = Signal.trap('CONT') do
|
27
29
|
dlog("Got a SIGCONT")
|
28
30
|
end
|
29
31
|
|
32
|
+
dlog("Installing SIGEXIT trap")
|
30
33
|
old_sigexit_handler = Signal.trap('EXIT') do
|
31
34
|
dlog("got EXIT")
|
32
|
-
Kernel.exit!
|
35
|
+
Kernel.exit! true
|
33
36
|
end
|
34
37
|
|
35
38
|
dlog("Stopping myself")
|
36
39
|
Process.kill 'SIGSTOP', $$
|
37
40
|
dlog("Back from SIGSTOP!")
|
38
41
|
|
42
|
+
@snap_tree = JSON.parse(File.read("/tmp/timetravel_#{$root_parent}.json"))
|
43
|
+
|
39
44
|
dlog("Returning to old SIGCONT")
|
40
|
-
Signal.trap('CONT', old_sigcont_handler)
|
45
|
+
Signal.trap('CONT', old_sigcont_handler || "DEFAULT")
|
46
|
+
|
41
47
|
dlog("Returning to old SIGEXIT")
|
42
|
-
Signal.trap('EXIT', old_sigexit_handler)
|
48
|
+
Signal.trap('EXIT', old_sigexit_handler || "DEFAULT")
|
43
49
|
end
|
44
50
|
|
45
|
-
def
|
51
|
+
def start_root_parent
|
52
|
+
$root_parent = $$
|
53
|
+
child_pid = fork
|
54
|
+
if child_pid
|
55
|
+
Signal.trap('INT') do
|
56
|
+
dlog("root-parent got INT, ignoring")
|
57
|
+
end
|
58
|
+
Signal.trap('USR1') do
|
59
|
+
dlog("root-parent got USR1, exiting")
|
60
|
+
FileUtils.rm("/tmp/timetravel_#{$root_parent}.json")
|
61
|
+
Kernel.exit! true
|
62
|
+
end
|
63
|
+
dlog "Root parent waiting on #{child_pid}"
|
64
|
+
# sleep
|
65
|
+
# Process.waitall
|
66
|
+
Process.waitpid child_pid
|
67
|
+
# Process.waitpid 0
|
68
|
+
dlog "Root parent exiting after wait"
|
69
|
+
FileUtils.rm("/tmp/timetravel_#{$root_parent}.json")
|
70
|
+
Kernel.exit! true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def snapshot(target, parent = -> {}, child = -> {})
|
46
75
|
|
47
76
|
# We need a root-parent to keep the shell happy
|
48
77
|
if ! $root_parent
|
49
|
-
|
50
|
-
child_pid = fork
|
51
|
-
if child_pid
|
52
|
-
Signal.trap('INT') do
|
53
|
-
dlog("root-parent got INT")
|
54
|
-
end
|
55
|
-
Signal.trap('USR1') do
|
56
|
-
dlog("root-parent got USR1")
|
57
|
-
Kernel.exit!
|
58
|
-
end
|
59
|
-
dlog "Root parent waiting on #{child_pid}"
|
60
|
-
Process.waitpid child_pid
|
61
|
-
dlog "Root parent exiting!"
|
62
|
-
Kernel.exit!
|
63
|
-
end
|
78
|
+
start_root_parent
|
64
79
|
end
|
65
80
|
|
66
|
-
|
81
|
+
@timetravel_root ||= $$
|
82
|
+
@snap_tree ||= {
|
83
|
+
$$.to_s => {
|
84
|
+
"file" => target.eval('__FILE__'),
|
85
|
+
"line" => target.eval('__LINE__'),
|
86
|
+
}
|
87
|
+
}
|
67
88
|
|
68
89
|
parent_pid = $$
|
69
90
|
child_pid = fork
|
91
|
+
|
70
92
|
if child_pid
|
71
|
-
dlog("I am parent #{parent_pid}: I have a child pid #{child_pid}")
|
72
93
|
|
73
|
-
|
74
|
-
|
75
|
-
@previous_pid.push child_pid
|
94
|
+
dlog("I am parent #{parent_pid}: I have a child pid #{child_pid}")
|
95
|
+
enter_suspended_animation
|
76
96
|
|
77
|
-
# Perform operation
|
78
|
-
|
97
|
+
# Perform child operation
|
98
|
+
child.()
|
79
99
|
|
80
100
|
else
|
101
|
+
|
81
102
|
child_pid = $$
|
82
103
|
dlog("I am child #{child_pid}: I have a parent pid #{parent_pid}")
|
83
|
-
|
104
|
+
|
105
|
+
@snap_tree[child_pid.to_s] = {
|
106
|
+
"previous" => parent_pid,
|
107
|
+
"file" => target.eval('__FILE__'),
|
108
|
+
"line" => target.eval('__LINE__'),
|
109
|
+
}
|
110
|
+
|
111
|
+
# Perform parent operation
|
112
|
+
parent.()
|
113
|
+
|
84
114
|
end
|
85
115
|
end
|
86
116
|
|
87
|
-
def snapshot_list
|
88
|
-
@
|
117
|
+
def snapshot_list(target, indent = "", node = @timetravel_root.to_s)
|
118
|
+
# @snap_tree && @snap_tree.keys.join(" ")
|
119
|
+
return unless node && node != ""
|
120
|
+
|
121
|
+
# This shouldn't be here
|
122
|
+
@snap_tree[$$.to_s]["file"] = target.eval('__FILE__')
|
123
|
+
@snap_tree[$$.to_s]["line"] = target.eval('__LINE__')
|
124
|
+
|
125
|
+
out = "#{indent}#{node} #{@snap_tree[node]["file"]} #{@snap_tree[node]["line"]} #{ node == $$.to_s ? '***' : ''}\n"
|
126
|
+
@snap_tree.keys.select { |n|
|
127
|
+
@snap_tree[n]["previous"] == node.to_i
|
128
|
+
}.each do |n|
|
129
|
+
out += snapshot_list(target, indent + " ", n)
|
130
|
+
end
|
131
|
+
out
|
89
132
|
end
|
90
133
|
|
91
|
-
def restore_snapshot(target_pid = nil, count =
|
92
|
-
dlog("Thinking about time travel...");
|
134
|
+
def restore_snapshot(target, target_pid = nil, count = 1)
|
135
|
+
dlog("Thinking about time travel... $$");
|
93
136
|
|
94
|
-
if target_pid.nil? && @
|
95
|
-
count = 1 if count
|
96
|
-
target_pid = @
|
137
|
+
if target_pid.nil? && @snap_tree && ! @snap_tree[$$.to_s].nil?
|
138
|
+
count = 1 if count < 1
|
139
|
+
target_pid = @snap_tree[$$.to_s]["previous"]
|
140
|
+
@snap_tree[$$.to_s]["file"] = target.eval('__FILE__')
|
141
|
+
@snap_tree[$$.to_s]["line"] = target.eval('__LINE__')
|
97
142
|
else
|
98
143
|
target_pid = target_pid
|
99
144
|
end
|
100
145
|
|
101
146
|
if target_pid
|
102
147
|
dlog("ME #{$$}: I found a target pid #{target_pid}! TIME TRAVEL TIME")
|
148
|
+
|
149
|
+
File.open("/tmp/timetravel_#{$root_parent}.json", 'w') do |f|
|
150
|
+
f.puts @snap_tree.to_json
|
151
|
+
end
|
152
|
+
|
103
153
|
Process.kill 'SIGCONT', target_pid
|
104
154
|
enter_suspended_animation
|
105
155
|
else
|
106
156
|
dlog("I was unable to time travel. Maybe it is a myth.");
|
157
|
+
puts "No previous snapshot found."
|
107
158
|
end
|
108
159
|
end
|
109
160
|
|
110
161
|
def restore_root_snapshot
|
111
|
-
restore_snapshot(
|
162
|
+
restore_snapshot(@timetravel_root) if @timetravel_root
|
112
163
|
end
|
113
164
|
|
114
165
|
end
|
@@ -20,11 +20,14 @@ Pry::Commands.create_command "snap", "Create a snapshot that you can later retur
|
|
20
20
|
end
|
21
21
|
def process
|
22
22
|
if opts.l?
|
23
|
-
output.puts PryTimetravel.snapshot_list
|
23
|
+
output.puts PryTimetravel.snapshot_list(target)
|
24
24
|
else
|
25
|
-
PryTimetravel.snapshot
|
26
|
-
|
27
|
-
|
25
|
+
PryTimetravel.snapshot(
|
26
|
+
target,
|
27
|
+
-> { run(args.join(" ")) unless args.empty? },
|
28
|
+
-> { run('whereami') }
|
29
|
+
)
|
30
|
+
# run('whereami')
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
@@ -47,10 +50,10 @@ Pry::Commands.create_command "back", "Go back to the most recent snapshot" do
|
|
47
50
|
end
|
48
51
|
def process
|
49
52
|
if opts.h?
|
50
|
-
PryTimetravel.restore_root_snapshot
|
53
|
+
PryTimetravel.restore_root_snapshot(target)
|
51
54
|
else
|
52
|
-
|
53
|
-
PryTimetravel.restore_snapshot(
|
55
|
+
target_pid = args.first ? args.first.to_i : opts[:p]
|
56
|
+
PryTimetravel.restore_snapshot(target, target_pid)
|
54
57
|
end
|
55
58
|
end
|
56
59
|
end
|
data/pry-timetravel.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pry-timetravel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brock Wilcox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|