Skip to content

Commit

Permalink
Add docs for auto-snap and better tree list
Browse files Browse the repository at this point in the history
  • Loading branch information
Brock Wilcox committed Jan 3, 2017
1 parent 65cfdd1 commit 853b419
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 29 deletions.
92 changes: 70 additions & 22 deletions lib/pry-timetravel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def enter_suspended_animation
Process.kill 'SIGSTOP', $$

dlog("Resume: Back from SIGSTOP! Loading snapshot tree")
load_snap_tree
load_global_timetravel_state

dlog("Resume: Returning to old SIGCONT")
Signal.trap('CONT', old_sigcont_handler || "DEFAULT")
Expand All @@ -71,25 +71,57 @@ def start_root_parent
child_pid = fork
if child_pid
Signal.trap('INT') do
dlog("root-parent got INT, ignoring")
dlog("Root-parent: Got INT, ignoring")
end
Signal.trap('USR1') do
dlog("Root-parent: Got USR1, exiting")
cleanup_snap_tree
cleanup_global_timetravel_state
Kernel.exit! true
end
dlog "Root parent: Waiting on child pid #{child_pid}"
Process.waitpid child_pid
dlog "Root parent: Exiting after wait"
cleanup_snap_tree
cleanup_global_timetravel_state
FileUtils.rm("/tmp/timetravel_#{$root_parent}.json")
Kernel.exit! true
end
end

def auto_snapshot
@trace = TracePoint.new() do |tp|
p [tp.lineno, tp.event, tp.raised_exception]
def auto_snapshot(target)
if !@do_trace
@trace = TracePoint.new(:line) do |tp|
# puts "trace status: #{$in_trace}"
if !$in_trace
$in_trace = true
# puts "path: #{File.expand_path(tp.path)}"
# puts "snapshotting trace"
# if ! (tp.defined_class.to_s =~ /Pry/)
# if tp.path =~ /<main>/
# if tp.path =~ /<main>/
# p [tp.path, tp.lineno, tp.event, tp.defined_class, Dir.pwd] # , tp.raised_exception]
# end
# if tp.path.include?(Dir.pwd) || tp.path =~ /<main>/
if File.expand_path(tp.path).include?(Dir.pwd)
p [tp.path, tp.lineno, tp.event, tp.defined_class, Dir.pwd] # , tp.raised_exception]
# p caller
# if tp.path =~ /<main>/
# # p tp
# p [tp.path, tp.lineno, tp.event] # , tp.raised_exception]
PryTimetravel.snapshot(
tp.binding,
# now_do: -> { run(args.join(" ")) unless args.empty? },
# on_return_do: -> { run('whereami') }
)
# end
end
$in_trace = false
end
end
@trace.enable
@do_trace = true
else
@trace.disable
@do_trace = false
end
end

Expand Down Expand Up @@ -144,7 +176,7 @@ def snapshot(target, opts = {})
end
end

def snapshot_list(target, indent = "", node = @timetravel_root.to_s)
def snapshot_list(target, indent = "", node = @timetravel_root.to_s, my_indent = nil)
if node == ""
return "No snapshots"
end
Expand All @@ -153,12 +185,22 @@ def snapshot_list(target, indent = "", node = @timetravel_root.to_s)
# Freshen the current snapshot so it looks right
update_current_snapshot_info(target)

out = "#{indent}#{node} (#{@snap_tree[node]["id"]}) #{@snap_tree[node]["file"]} #{@snap_tree[node]["line"]} #{ node == $$.to_s ? '***' : ''}\n"
@snap_tree.keys.select { |n|
@snap_tree[n]["previous"] == node.to_i
}.each do |n|
out += snapshot_list(target, indent + " ", n)
code_line = IO.readlines(@snap_tree[node]["file"])[@snap_tree[node]["line"].to_i - 1].chomp

out = "#{my_indent || indent}#{node} (#{@snap_tree[node]["id"]}) #{@snap_tree[node]["file"]} #{@snap_tree[node]["line"]} #{ node == $$.to_s ? '***' : ''} #{code_line}\n"
children = @snap_tree.keys.select { |n| @snap_tree[n]["previous"] == node.to_i }
if children.length == 1
out += snapshot_list(target, indent, children[0])
else
children.each { |n|
out += snapshot_list(target, indent + " ", n, indent + "> ")
}
end
# @snap_tree.keys.select { |n|
# @snap_tree[n]["previous"] == node.to_i
# }.each do |n|
# out += snapshot_list(target, indent + " ", n)
# end
out
end

Expand All @@ -177,7 +219,7 @@ def restore_snapshot(target, target_pid = nil)
# Update our current information of our current running snapshot
update_current_snapshot_info(target)

save_snap_tree
save_global_timetravel_state

# Bring our target back to life
Process.kill 'SIGCONT', target_pid
Expand All @@ -194,24 +236,30 @@ def restore_root_snapshot
restore_snapshot(@timetravel_root) if @timetravel_root
end

def snap_tree_filename
def global_timetravel_state_filename
"/tmp/timetravel_#{$root_parent}.json"
end

def save_snap_tree
File.open(snap_tree_filename, 'w') do |f|
f.puts @snap_tree.to_json
def save_global_timetravel_state
File.open(global_timetravel_state_filename, 'w') do |f|
global_state = {
snap_tree: @snap_tree,
do_trace: @do_trace
}
f.puts global_state.to_json
end
end

def load_snap_tree
@snap_tree = JSON.parse(File.read(snap_tree_filename))
def load_global_timetravel_state
global_state = JSON.parse(File.read(global_timetravel_state_filename))
@snap_tree = global_state["snap_tree"]
@do_trace = global_state["do_trace"]
dlog("Loaded: " + @snap_tree.to_json)
@id = (@snap_tree.values.map{|snap| snap['id']}.max || 0) + 1
end

def cleanup_snap_tree
FileUtils.rm(snap_tree_filename)
def cleanup_global_timetravel_state
FileUtils.rm(global_timetravel_state_filename)
end

end
Expand Down
11 changes: 9 additions & 2 deletions lib/pry-timetravel/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
group 'Timetravel'
banner <<-'BANNER'
Usage: snap [cmd]
snap --list
snap -l|--list List existing snapshots
snap -a|--auto Automatically take new snapshots
This will add a snapshot which you can return to later.
Expand All @@ -17,10 +18,13 @@ def options(opt)
# :optional_argument => true, :as => Integer
opt.on :l, :list,
"Show a list of existing snapshots"
opt.on :a, :auto, "Automatically take snapshots!"
end
def process
if opts.l?
output.puts PryTimetravel.snapshot_list(target)
elsif opts.a?
output.puts PryTimetravel.auto_snapshot(target)
else
PryTimetravel.snapshot(
target,
Expand Down Expand Up @@ -48,7 +52,7 @@ def options(opt)
opt.on :home, "Jump to the end of the original execution sequence"
end
def process
if opts.h?
if opts.home?
PryTimetravel.restore_root_snapshot(target)
else
target_pid = args.first ? args.first.to_i : opts[:p]
Expand All @@ -57,3 +61,6 @@ def process
end
end

Pry::Commands.alias_command 'n', 'snap next'
Pry::Commands.alias_command 's', 'snap step'
Pry::Commands.alias_command 'p', 'back'
1 change: 1 addition & 0 deletions pry-timetravel.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Gem::Specification.new do |s|

s.add_development_dependency 'rake'
s.add_development_dependency 'rspec'
s.add_development_dependency 'pry-byebug'
# s.add_development_dependency 'yard'
# s.add_development_dependency 'redcarpet'
# s.add_development_dependency 'capybara'
Expand Down
29 changes: 24 additions & 5 deletions spec/commands_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
sleep 0.1 # Give time to exit?
end

pid_list = `ps h -o pid,ppid -g #{saved_pid}`.split(/\n/)
pid_list = `ps h -o pid,ppid -s #{saved_pid}`.split(/\n/)
expect(pid_list.count).to be == 0
end

Expand All @@ -39,11 +39,12 @@
expect(pid1.to_i).to be > 0
expect(pid2.to_i).to be > 0

pid_list = `ps h -o pid,ppid -g #{cmd_pid}`.split(/\n/)
pid_list = `ps h -o pid,ppid,sess -s #{cmd_pid}`.split(/\n/)
# 0: shell
# 1: root
# 2: base snapshot
# 3: current running branch
expect(pid_list.count).to be == 3
expect(pid_list.count).to be == 4
end
end

Expand All @@ -61,12 +62,13 @@
expect(pid2.to_i).to be > 0
expect(pid3.to_i).to be > 0

pid_list = `ps h -o pid,ppid -g #{cmd_pid}`.split(/\n/)
pid_list = `ps h -o pid,ppid -s #{cmd_pid}`.split(/\n/)
# 0: shell
# 1: root
# 2: base snapshot
# 2: second snapshot
# 3: current running branch
expect(pid_list.count).to be == 4
expect(pid_list.count).to be == 5
end
end

Expand Down Expand Up @@ -103,5 +105,22 @@
end
end

it "Can auto-checkpoint" do
PTY.spawn(pry_timetravel_cmd) do |reader, writer, cmd_pid|
writer.puts("snap -a")
writer.puts("x = 7")
expect(reader.expect(/^=> 7/,1)).to be_truthy
writer.puts("x")
expect(reader.expect(/^=> 7/,1)).to be_truthy
writer.puts("x = 13")
expect(reader.expect(/^=> 13/,1)).to be_truthy
writer.puts("back")
expect(reader.expect(/^At the top level\./,1)).to be_truthy
writer.puts("x")
result = reader.expect(/^=> 7/,1)
expect(result).to be_truthy
end
end

end

0 comments on commit 853b419

Please sign in to comment.