pry-timetravel 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.MIT +1 -1
- data/README.md +18 -6
- data/Rakefile +2 -2
- data/lib/pry-timetravel.rb +89 -60
- data/lib/pry-timetravel/commands.rb +37 -29
- data/pry-timetravel.gemspec +1 -1
- metadata +2 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d28f753687e1f5f96c87e7ce51949dc68d8fab6
|
4
|
+
data.tar.gz: be676c5cc7e8ff236314b31e6161aef76ce017a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c42224c2d9c89770cf5f22c861b37fa254a71713d11c46696fbc6d912027f21454a0ea98688f13f690a69e7fc7a5e12b8a1c63e2b2d61603c7c192bbebfb0c01
|
7
|
+
data.tar.gz: 27bfcc69e86ff9838820f350c5fb617e24d6d1c456e702b670c8c25d7156b9afdb5be0c728b9416f8645696759c4482930e3f0f9e309344635fc9638b2636fed
|
data/LICENSE.MIT
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2014 Brock Wilcox <awwaiid@thelackthereof.org>
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
4
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,21 +1,33 @@
|
|
1
1
|
# pry-timetravel
|
2
2
|
|
3
|
-
DOES NOT WORK. DO NOT USE.
|
4
|
-
|
5
3
|
Time travel! For your REPL!
|
6
4
|
|
7
5
|
> x = 5
|
8
6
|
5
|
9
|
-
>
|
7
|
+
> snap
|
10
8
|
> x = 10
|
11
9
|
10
|
12
|
-
>
|
10
|
+
> back
|
13
11
|
> x
|
14
12
|
5
|
15
13
|
|
16
14
|
This is an attempt to package the proof of concept. API WILL CHANGE!
|
17
15
|
|
16
|
+
## How it works
|
17
|
+
|
18
|
+
The 'snap' command causes a fork, and the child is sent a SIGSTOP to freeze the
|
19
|
+
process. The parent continues on. Later when you do 'back' the saved child is
|
20
|
+
sent SIGCONT and the current process is sent a SIGSTOP. Ultimately you can have
|
21
|
+
a whole pool of frozen snapshots and can resume any of them.
|
22
|
+
|
23
|
+
In theory copy-on-write semantics makes this a tollerable thing to do :)
|
24
|
+
|
25
|
+
There are a few more details, but that is the important bit.
|
26
|
+
|
27
|
+
WARNING: Time travel may cause zombies.
|
28
|
+
|
18
29
|
## Meta
|
19
30
|
|
20
|
-
Released under the MIT license, see LICENSE.MIT for details.
|
21
|
-
are welcome
|
31
|
+
Released under the MIT license, see LICENSE.MIT for details. License is
|
32
|
+
negotiable. Contributions and bug-reports are welcome!
|
33
|
+
|
data/Rakefile
CHANGED
data/lib/pry-timetravel.rb
CHANGED
@@ -7,81 +7,110 @@ class PryTimetravel
|
|
7
7
|
class << self
|
8
8
|
|
9
9
|
def dlog(msg)
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
if ENV["TIMETRAVEL_DEBUG"]
|
11
|
+
File.open("meta.log", 'a') do |file|
|
12
|
+
file.puts("#{Time.now} [#{$$}] #{msg}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
at_exit do
|
18
|
+
PryTimetravel.dlog "at_exit"
|
19
|
+
if $root_parent && $$ != $root_parent
|
20
|
+
PryTimetravel.dlog "Sending SIGUSR1 up to #{$root_parent}"
|
21
|
+
Process.kill 'SIGUSR1', $root_parent
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def enter_suspended_animation
|
26
|
+
old_sigcont_handler = Signal.trap('CONT') do
|
27
|
+
dlog("Got a SIGCONT")
|
28
|
+
end
|
29
|
+
|
30
|
+
old_sigexit_handler = Signal.trap('EXIT') do
|
31
|
+
dlog("got EXIT")
|
32
|
+
Kernel.exit!
|
33
|
+
end
|
34
|
+
|
35
|
+
dlog("Stopping myself")
|
36
|
+
Process.kill 'SIGSTOP', $$
|
37
|
+
dlog("Back from SIGSTOP!")
|
38
|
+
|
39
|
+
dlog("Returning to old SIGCONT")
|
40
|
+
Signal.trap('CONT', old_sigcont_handler)
|
41
|
+
dlog("Returning to old SIGEXIT")
|
42
|
+
Signal.trap('EXIT', old_sigexit_handler)
|
13
43
|
end
|
14
44
|
|
15
|
-
def
|
45
|
+
def snapshot
|
46
|
+
|
47
|
+
# We need a root-parent to keep the shell happy
|
48
|
+
if ! $root_parent
|
49
|
+
$root_parent = $$
|
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
|
64
|
+
end
|
65
|
+
|
66
|
+
$timetravel_root ||= $$
|
67
|
+
|
16
68
|
parent_pid = $$
|
17
69
|
child_pid = fork
|
18
70
|
if child_pid
|
19
|
-
dlog("
|
20
|
-
|
21
|
-
# Method
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
# Problem: What if the child exits in a bad way? Check out '$?'
|
28
|
-
Process.waitpid(child_pid)
|
29
|
-
dlog("ME #{$$}: Previous exit: #{$?.inspect}")
|
30
|
-
if $?.exitstatus != 42
|
31
|
-
# Since this wasn't a timetravel return, we must unravel this world
|
32
|
-
Kernel.exit! $?.exitstatus
|
33
|
-
end
|
71
|
+
dlog("I am parent #{parent_pid}: I have a child pid #{child_pid}")
|
72
|
+
|
73
|
+
# Method 3: Child suspends themselves, parent adds them to list
|
74
|
+
@previous_pid ||= []
|
75
|
+
@previous_pid.push child_pid
|
76
|
+
|
77
|
+
# Perform operation
|
78
|
+
yield
|
34
79
|
|
35
|
-
# The parent universe freezes
|
36
|
-
# dlog("PARENT #{$$}: I am suspending. My child is #{parent_pid}")
|
37
|
-
# dlog("PARENT #{$$}: suspending")
|
38
|
-
# Process.setpgrp
|
39
|
-
# Process.setsid
|
40
|
-
# dlog("PARENT #{$$}: resumed!")
|
41
80
|
else
|
42
81
|
child_pid = $$
|
43
|
-
dlog("
|
82
|
+
dlog("I am child #{child_pid}: I have a parent pid #{parent_pid}")
|
83
|
+
enter_suspended_animation
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def snapshot_list
|
88
|
+
@previous_pid && @previous_pid.join(" ")
|
89
|
+
end
|
44
90
|
|
45
|
-
|
46
|
-
|
47
|
-
# Process.kill 'SIGSTOP', child_pid
|
48
|
-
# dlog("CHILD #{child_pid}: resumed!")
|
91
|
+
def restore_snapshot(target_pid = nil, count = nil)
|
92
|
+
dlog("Thinking about time travel...");
|
49
93
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
94
|
+
if target_pid.nil? && @previous_pid && ! @previous_pid.empty?
|
95
|
+
count = 1 if count == 0
|
96
|
+
target_pid = @previous_pid[-count]
|
97
|
+
else
|
98
|
+
target_pid = target_pid
|
99
|
+
end
|
55
100
|
|
101
|
+
if target_pid
|
102
|
+
dlog("ME #{$$}: I found a target pid #{target_pid}! TIME TRAVEL TIME")
|
103
|
+
Process.kill 'SIGCONT', target_pid
|
104
|
+
enter_suspended_animation
|
105
|
+
else
|
106
|
+
dlog("I was unable to time travel. Maybe it is a myth.");
|
56
107
|
end
|
57
108
|
end
|
58
109
|
|
59
|
-
def
|
60
|
-
|
61
|
-
dlog("ME #{$$}: previous_pid = #{ @previous_pid }");
|
62
|
-
if @previous_pid && ! @previous_pid.empty?
|
63
|
-
previous_pid = @previous_pid.pop
|
64
|
-
dlog("ME #{$$}: I found a previous pid #{previous_pid}! TIME TRAVEL TIME")
|
65
|
-
|
66
|
-
# Method 1: Awaken the child and let them take over
|
67
|
-
# Main parent can't exit or shell will get upset, so wait for all children
|
68
|
-
# Once all children are done, kill ourself
|
69
|
-
# Process.kill 'SIGCONT', previous_pid
|
70
|
-
# dlog("ME #{$$}: I resumed pid #{previous_pid}... now time to wait")
|
71
|
-
# # Process.waitpid(previous_pid)
|
72
|
-
# Process.waitall
|
73
|
-
# dlog("ME #{$$}: If you meet your previous self, kill yourself.")
|
74
|
-
# # Process.kill 'SIGKILL', $$
|
75
|
-
# Kernel.exit!
|
76
|
-
|
77
|
-
# Method 2: Kill ourself and let the parent take over
|
78
|
-
# The parent was just doing a waitpid, so it is ready
|
79
|
-
# Process.kill 'SIGKILL', $$
|
80
|
-
Process.exit! 42
|
81
|
-
|
82
|
-
end
|
83
|
-
dlog("ME #{$$}: I was unable to time travel. Maybe it is a myth.");
|
110
|
+
def restore_root_snapshot
|
111
|
+
restore_snapshot($timetravel_root) if $timetravel_root
|
84
112
|
end
|
113
|
+
|
85
114
|
end
|
86
115
|
end
|
87
116
|
|
@@ -1,49 +1,57 @@
|
|
1
1
|
|
2
|
-
Pry::Commands.create_command "
|
3
|
-
match '
|
2
|
+
Pry::Commands.create_command "snap", "Create a snapshot that you can later return to" do
|
3
|
+
match 'snap'
|
4
4
|
group 'Timetravel'
|
5
|
-
# description 'Snapshot the world so we can timetravel back here later'
|
6
5
|
banner <<-'BANNER'
|
7
|
-
Usage:
|
8
|
-
|
9
|
-
checkpoint --delete [INDEX]
|
6
|
+
Usage: snap [cmd]
|
7
|
+
snap --list
|
10
8
|
|
11
|
-
This will add a
|
9
|
+
This will add a snapshot which you can return to later.
|
10
|
+
|
11
|
+
If you provide [cmd] then that command will also be run -- nice for "snap next" in conjunction with pry-byebug.
|
12
12
|
BANNER
|
13
13
|
|
14
|
-
|
15
|
-
# opt.on :d, :delete
|
16
|
-
# "Delete the
|
14
|
+
def options(opt)
|
15
|
+
# opt.on :d, :delete=,
|
16
|
+
# "Delete the snapshot with the given index. If no index is given delete them all",
|
17
17
|
# :optional_argument => true, :as => Integer
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
opt.on :l, :list,
|
19
|
+
"Show a list of existing snapshots"
|
20
|
+
end
|
21
21
|
def process
|
22
|
-
|
22
|
+
if opts.l?
|
23
|
+
output.puts PryTimetravel.snapshot_list
|
24
|
+
else
|
25
|
+
PryTimetravel.snapshot do
|
26
|
+
run(args.join(" ")) unless args.empty?
|
27
|
+
end
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
|
26
|
-
Pry::Commands.create_command "
|
27
|
-
match '
|
32
|
+
Pry::Commands.create_command "back", "Go back to the most recent snapshot" do
|
33
|
+
match 'back'
|
28
34
|
group 'Timetravel'
|
29
|
-
# description 'Snapshot the world so we can timetravel back here later'
|
30
35
|
banner <<-'BANNER'
|
31
|
-
Usage:
|
32
|
-
|
33
|
-
|
36
|
+
Usage: back [count]
|
37
|
+
back --pid pid
|
38
|
+
back --home
|
34
39
|
|
35
|
-
|
40
|
+
Go back to a previous snapshot.
|
36
41
|
BANNER
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# "Show all checkpoints"
|
44
|
-
# end
|
43
|
+
def options(opt)
|
44
|
+
opt.on :p, :pid=, "Jump (back) to a specific snapshot identified by [pid]",
|
45
|
+
:as => Integer
|
46
|
+
opt.on :home, "Jump to the end of the original execution sequence"
|
47
|
+
end
|
45
48
|
def process
|
46
|
-
|
49
|
+
if opts.h?
|
50
|
+
PryTimetravel.restore_root_snapshot
|
51
|
+
else
|
52
|
+
count = args.first.to_i
|
53
|
+
PryTimetravel.restore_snapshot(opts[:p], count)
|
54
|
+
end
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
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.2
|
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-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -81,4 +81,3 @@ signing_key:
|
|
81
81
|
specification_version: 4
|
82
82
|
summary: Timetravel
|
83
83
|
test_files: []
|
84
|
-
has_rdoc:
|