Class: PryTimetravel
- Inherits:
-
Object
- Object
- PryTimetravel
- Defined in:
- lib/pry-timetravel.rb
Class Method Summary collapse
- .dlog(msg) ⇒ Object
- .enter_suspended_animation ⇒ Object
- .restore_root_snapshot ⇒ Object
- .restore_snapshot(target, target_pid = nil, count = 1) ⇒ Object
- .snapshot(target, parent = -> {}, child = -> {}) ⇒ Object
- .snapshot_list(target, indent = "", node = @timetravel_root.to_s) ⇒ Object
- .start_root_parent ⇒ Object
Class Method Details
.dlog(msg) ⇒ Object
10 11 12 13 14 15 16 |
# File 'lib/pry-timetravel.rb', line 10 def dlog(msg) if ENV["TIMETRAVEL_DEBUG"] File.open("meta.log", 'a') do |file| file.puts("#{Time.now} [#{$$}] #{msg}") end end end |
.enter_suspended_animation ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/pry-timetravel.rb', line 26 def enter_suspended_animation dlog("Installing SIGCONT trap") old_sigcont_handler = Signal.trap('CONT') do dlog("Got a SIGCONT") end dlog("Installing SIGEXIT trap") old_sigexit_handler = Signal.trap('EXIT') do dlog("got EXIT") Kernel.exit! true end dlog("Stopping myself") Process.kill 'SIGSTOP', $$ dlog("Back from SIGSTOP!") @snap_tree = JSON.parse(File.read("/tmp/timetravel_#{$root_parent}.json")) dlog("Returning to old SIGCONT") Signal.trap('CONT', old_sigcont_handler || "DEFAULT") dlog("Returning to old SIGEXIT") Signal.trap('EXIT', old_sigexit_handler || "DEFAULT") end |
.restore_root_snapshot ⇒ Object
161 162 163 |
# File 'lib/pry-timetravel.rb', line 161 def restore_root_snapshot restore_snapshot(@timetravel_root) if @timetravel_root end |
.restore_snapshot(target, target_pid = nil, count = 1) ⇒ Object
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/pry-timetravel.rb', line 134 def restore_snapshot(target, target_pid = nil, count = 1) dlog("Thinking about time travel... $$"); if target_pid.nil? && @snap_tree && ! @snap_tree[$$.to_s].nil? count = 1 if count < 1 target_pid = @snap_tree[$$.to_s]["previous"] @snap_tree[$$.to_s]["file"] = target.eval('__FILE__') @snap_tree[$$.to_s]["line"] = target.eval('__LINE__') else target_pid = target_pid end if target_pid dlog("ME #{$$}: I found a target pid #{target_pid}! TIME TRAVEL TIME") File.open("/tmp/timetravel_#{$root_parent}.json", 'w') do |f| f.puts @snap_tree.to_json end Process.kill 'SIGCONT', target_pid enter_suspended_animation else dlog("I was unable to time travel. Maybe it is a myth."); puts "No previous snapshot found." end end |
.snapshot(target, parent = -> {}, child = -> {}) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/pry-timetravel.rb', line 74 def snapshot(target, parent = -> {}, child = -> {}) # We need a root-parent to keep the shell happy if ! $root_parent start_root_parent end @timetravel_root ||= $$ @snap_tree ||= { $$.to_s => { "file" => target.eval('__FILE__'), "line" => target.eval('__LINE__'), } } parent_pid = $$ child_pid = fork if child_pid dlog("I am parent #{parent_pid}: I have a child pid #{child_pid}") enter_suspended_animation # Perform child operation child.() else child_pid = $$ dlog("I am child #{child_pid}: I have a parent pid #{parent_pid}") @snap_tree[child_pid.to_s] = { "previous" => parent_pid, "file" => target.eval('__FILE__'), "line" => target.eval('__LINE__'), } # Perform parent operation parent.() end end |
.snapshot_list(target, indent = "", node = @timetravel_root.to_s) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/pry-timetravel.rb', line 117 def snapshot_list(target, indent = "", node = @timetravel_root.to_s) # @snap_tree && @snap_tree.keys.join(" ") return unless node && node != "" # This shouldn't be here @snap_tree[$$.to_s]["file"] = target.eval('__FILE__') @snap_tree[$$.to_s]["line"] = target.eval('__LINE__') out = "#{indent}#{node} #{@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) end out end |
.start_root_parent ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/pry-timetravel.rb', line 51 def start_root_parent $root_parent = $$ child_pid = fork if child_pid Signal.trap('INT') do dlog("root-parent got INT, ignoring") end Signal.trap('USR1') do dlog("root-parent got USR1, exiting") FileUtils.rm("/tmp/timetravel_#{$root_parent}.json") Kernel.exit! true end dlog "Root parent waiting on #{child_pid}" # sleep # Process.waitall Process.waitpid child_pid # Process.waitpid 0 dlog "Root parent exiting after wait" FileUtils.rm("/tmp/timetravel_#{$root_parent}.json") Kernel.exit! true end end |