-
Notifications
You must be signed in to change notification settings - Fork 1
/
recoverable_pdb.py
175 lines (143 loc) · 4.84 KB
/
recoverable_pdb.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import pdb
import copy
import sys
import os
def experiment(func):
def inner_func(*args, **kwargs):
print 'this is experiment function.'
return func(*args, **kwargs)
return inner_func
def try_copy(data):
# some inner structure use dict.
if isinstance(data, dict):
ret = {}
for i in data:
new_copy = try_copy(data[i])
if new_copy:
ret[i] = new_copy
return ret
else:
try:
# copy data
return copy.deepcopy(data)
except Exception as e:
# if the data can't be copied
pass
# just return data.
return data
def diff_dict(old, new):
ret = {}
create_dict = lambda o, n: {'old': o, 'new': n}
for i in old:
if i not in new:
ret[i] = create_dict(old[i], 'not defined')
elif old[i] != new[i]:
ret[i] = create_dict(old[i], new[i])
for i in new:
if i not in old:
ret[i] = create_dict('not defined', new[i])
return ret
class RecoverablePdb(pdb.Pdb):
def __init__(self, *argss, **kwargss):
pdb.Pdb.__init__(self, *argss, **kwargss)
self.snapshot = {}
self.undo_stack = []
self.load_bp_info_from_file('breakpoint.info')
def do_save(self, args):
backup_locals = try_copy(self.curframe_locals)
lineno = self.curframe.f_lineno
self.snapshot[args] = (backup_locals, lineno)
def do_restore(self, args):
if args not in self.snapshot:
print 'no such name'
return
if not self.jump_to(self.snapshot[args][1]):
print 'can not jump'
return
self.restore_env(self.snapshot[args][0])
def do_diff(self, args):
if args not in self.snapshot:
print 'no such name'
return
result = diff_dict(self.snapshot[args][0], self.curframe_locals)
for i in result:
print 'variable name: ', i
print '------------------'
print 'old value: '
print result[i]['old']
print '------------------'
print 'new value: '
print result[i]['new']
print '=================='
def restore_env(self, env):
# some variable is defined latter.
# so we need to delete it if we restore runtime env.
need_to_delete = []
for i in self.curframe_locals:
if i not in env:
need_to_delete.append(i)
else:
self.curframe_locals[i] = env[i]
for i in need_to_delete:
del self.curframe_locals[i]
def jump_to(self, args):
if self.curindex + 1 != len(self.stack):
print >>self.stdout, "*** You can only jump within the bottom frame"
return False
try:
args = int(args)
except ValueError:
print >>self.stdout, "*** The 'jump' command requires a line number."
return False
else:
try:
# Do the jump, fix up our copy of the stack, and display the
# new position
self.curframe.f_lineno = args
self.stack[self.curindex] = self.stack[self.curindex][0], args
self.print_stack_entry(self.stack[self.curindex])
except ValueError, e:
print >>self.stdout, '*** Jump failed:', e
return False
return True
def save_env_for_undo(self):
backup_locals = try_copy(self.curframe_locals)
lineno = self.curframe.f_lineno
self.undo_stack.append((backup_locals, lineno))
def do_step(self, args):
self.save_env_for_undo()
# remember return 1
return pdb.Pdb.do_step(self, args)
do_s = do_step
def do_next(self, args):
self.save_env_for_undo()
# remember return 1
return pdb.Pdb.do_next(self, args)
def do_undo(self, args):
if len(self.undo_stack) > 0:
env = self.undo_stack.pop()
if not self.jump_to(env[1]):
print 'can not jump'
return
self.restore_env(env[0])
else:
print 'out of stack'
#@experiment
def do_load(self, args):
self.load_bp_info_from_file(args)
def load_bp_info_from_file(self, file_name):
if os.path.exists(file_name):
ftr = open(file_name, 'r')
bp_info = ftr.readlines()
ftr.close()
for line in bp_info:
if line[0] == '#':
pass
elif (len(line) < 3 and line[-1] == '\n'):
pass
else:
print line
self.onecmd(line)
print 'loaded breakpoint infomation.'
def set_trace():
RecoverablePdb().set_trace(sys._getframe().f_back)