-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdebug_until.py
368 lines (290 loc) · 7.66 KB
/
debug_until.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
import gdb
import os
from enum import Enum
cmd = ''
exp = ''
start_point = None
triggered = 0
break_hit = 0
subscribed = 0
runable_after_exit = 1
rec = 1
iter = 0
arg_dict = {}
LOG_FILE = 'log.txt'
CMD = 'debug-until'
NEXT = 'next'
CONTINUE = 'continue'
RUN = 'run'
LS_ERR = 'ls: cannot access'
PRINT = 'print '
SHELL = 'shell'
HELP = '--help'
ARGS = '--args'
EXP = '--exp'
CMP = '--cmp'
FILE_CREATED = '--file-created'
FILE_DELETED = '--file-deleted'
REC = '-r'
VAR_EQ = '--var-eq'
commands = []
commands.append(HELP)
commands.append(ARGS)
commands.append(EXP)
commands.append(CMP)
commands.append(FILE_CREATED)
commands.append(FILE_DELETED)
commands.append(VAR_EQ)
commands.append(REC)
def get_arg_value(key):
arg = get_arg(key)
arr = arg.split('=')
if len(arr) == 2 and arr[1]:
return arr[1]
else:
key_arr = arg.split(key)
if not key_arr[1]:
finish_debug('"{0}" parameter wasn`t assinged.'.format(key))
else:
return key_arr[1]
class DEBUG_ST(Enum):
COMPLETED = 0
RUNNING = 1
def cmp_command_output(res):
global exp
return exp == res or res.startswith(exp)
def cmp_var(res):
res_val = res.split('=')[1].strip().strip()
global exp
return exp == res_val
def die(error):
if error:
raise gdb.GdbError('error: ' + error)
def event_triggered():
gdb.write('\n----------------\n')
gdb.write('Event triggered!\n')
gdb.write('----------------\n \n')
def report_debug_failure():
gdb.write('\n-------------------------------------------------------------\n')
gdb.write('Passed condition wasn`t met. Try setting different parameters\n')
gdb.write('-------------------------------------------------------------\n \n')
def print_usage():
gdb.write('Usage:\n')
gdb.write('debug-until [<starting breakpoint>] [--args=<inferior args>] [-r=<number of times program should be executed>] \n')
gdb.write(' [[--cmp=<shell command> --exp=<expected output>]\n')
gdb.write(' [--file-created=<file>]\n')
gdb.write(' [--file-deleted=<file>]\n')
gdb.write(' [--var-eq=<variable>:<expected value>]]\n\n')
gdb.write('[starting break point] - should be passed in the format that is accepted by GDB\
(e.g. <filename>:<line> or <function name>).\n')
gdb.write('[inferior args] - arguments for GDB`s run command required run debugged program.\n')
gdb.write('[shell command] - the shell command that will be executed after each line of code.\n')
gdb.write('The output of the <shell command> will be compared with <expected output>\
and in case if they are equal debug-until will report about triggering of an event.\n')
gdb.write('\nGet more info on https://github.com/Viaceslavus/gdb-debug-until\n')
def print_full_info():
gdb.write('\Decription:\n')
gdb.write('\nDebug through the code until the passed event will be triggered.\n\n')
print_usage()
def run_shell_command():
try:
if cmd.startswith(SHELL):
shell_cmd = '{0} > {1} 2>&1'.format(cmd, LOG_FILE)
gdb.execute(shell_cmd, from_tty=True, to_string=True)
return open(LOG_FILE).read()
else:
return gdb.execute(cmd, from_tty=True, to_string=True)
except Exception:
pass
def condition_satisfied():
gdb.write('\nCondition is already satisfied\n')
def get_triggered():
global triggered
return triggered
def dispose():
global subscribed
global triggered
global cmd
global exp
global start_point
global rec
global iter
global comparer
global arg_dict
global runable_after_exit
global break_hit
if subscribed:
global handler
gdb.events.stop.disconnect(handler)
gdb.events.exited.disconnect(handler)
global start_point
if start_point != None:
start_point.delete()
if os.path.isfile(LOG_FILE):
gdb.execute('shell rm ' + LOG_FILE)
gdb.execute('set pagination on')
subscribed = 0
triggered = 0
rec = 1
iter = 0
break_hit = 0
runable_after_exit = 1
cmd = ''
exp = ''
start_point = None
arg_dict = {}
comparer = cmp_command_output
def finish_debug(error_msg=None):
dispose()
die(error_msg)
def check_trig_event():
global triggered
if not triggered:
event_triggered()
triggered = 1
gdb.execute(NEXT)
gdb.execute(CONTINUE)
def run():
res = run_shell_command()
if comparer(res):
check_trig_event()
else:
gdb.execute(NEXT)
def check_st(event):
global break_hit
if hasattr(event, 'breakpoints'):
break_hit = 1
run()
elif hasattr(event, 'inferior'):
if not break_hit:
gdb.write('error: Breakpoint wasn`t initialized.\n')
finish_debug()
else:
break_hit = 0
global runable_after_exit
if not triggered:
global iter
global rec
if runable_after_exit:
res = run_shell_command()
if comparer(res):
check_trig_event()
elif rec <= iter + 1:
report_debug_failure()
finish_debug()
else:
if rec <= iter + 1:
report_debug_failure()
finish_debug()
else:
run()
def subscribe():
event_subscribe()
return DEBUG_ST.RUNNING
def check_if_true_on_start(res):
if comparer(res):
condition_satisfied()
finish_debug()
return DEBUG_ST.COMPLETED
return subscribe()
handler = check_st
comparer = cmp_command_output
def event_subscribe():
global handler
global subscribed
subscribed = 1
gdb.events.stop.connect(handler)
gdb.events.exited.connect(handler)
def start_debug():
global cmd
global exp
global comparer
if has_arg(CMP) and has_arg(EXP):
cmd = get_arg_value(CMP)
exp = get_arg_value(EXP)
res = run_shell_command()
comparer = cmp_command_output
return check_if_true_on_start(res)
elif has_arg(FILE_CREATED):
target_file = get_arg_value(FILE_CREATED)
cmd = SHELL + ' ls ' + target_file
exp = target_file
res = run_shell_command()
comparer = cmp_command_output
return check_if_true_on_start(res)
elif has_arg(FILE_DELETED):
target_file = get_arg_value(FILE_DELETED)
cmd = SHELL + ' ls ' + target_file
exp = LS_ERR
res = run_shell_command()
comparer = cmp_command_output
return check_if_true_on_start(res)
elif has_arg(VAR_EQ):
arg = get_arg_value(VAR_EQ)
var = arg.split(':')[0]
val = arg.split(':')[1]
cmd = PRINT + var
exp = val
global runable_after_exit
runable_after_exit = 0
comparer = cmp_var
return subscribe()
else:
global arg_dict
gdb.write('error: The event isn`t specified.\n\n')
print_usage()
return DEBUG_ST.COMPLETED
def get_arg(com):
global arg_dict
return arg_dict.get(com)
def has_arg(com):
return get_arg(com) is not None
def args_to_dictionary(args):
global arg_dict
global commands
for arg in args:
for com in commands:
if arg is not None and arg.startswith(com):
arg_dict.setdefault(com, arg)
break
class DebugUntil (gdb.Command):
def __init__(self):
super(DebugUntil, self).__init__(CMD, gdb.COMMAND_USER)
def complete(self, text, word):
finish_debug()
def invoke(self, arg, from_tty):
if not arg:
print_full_info()
return
args = gdb.string_to_argv(arg)
args_to_dictionary(args)
if has_arg(HELP):
print_full_info()
return
global rec
if has_arg(REC):
rec = int(get_arg_value(REC))
if rec <= 0:
finish_debug('invalid "-r" parameter.')
return
if start_debug() == DEBUG_ST.COMPLETED:
return
global start_point
start_point = gdb.Breakpoint(args[0])
gdb.execute('set pagination off')
global iter
if not has_arg(ARGS):
for it in range(0, rec):
if get_triggered():
break
iter = it
gdb.execute(RUN)
else:
p_args = get_arg_value(ARGS)
for it in range(0, rec):
if get_triggered():
break
iter = it
gdb.execute(RUN + ' ' + p_args)
finish_debug()
DebugUntil()