pry-timetravel 0.0.2 → 0.0.3
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 +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
|