blob: d7bfef6c1017c01aabce8474da64076a3784a704 [file] [log] [blame]
Klement Sekeraf62ae122016-10-11 11:47:09 +02001import signal
2import os
Klement Sekera13a83ef2018-03-21 12:35:51 +01003import sys
Klement Sekera277b89c2016-10-28 13:20:27 +02004import traceback
Klement Sekera909a6a12017-08-08 04:33:53 +02005from log import RED, single_line_delim, double_line_delim
Klement Sekera75e7d132017-09-20 08:26:30 +02006from debug import spawn_gdb
Klement Sekera9b6ece72018-03-23 10:50:11 +01007from subprocess import check_output, CalledProcessError
Andrew Yourtchenko57612eb2018-03-28 15:32:10 +02008from util import check_core_path
Klement Sekeraf62ae122016-10-11 11:47:09 +02009
10
11class Hook(object):
12 """
13 Generic hooks before/after API/CLI calls
14 """
15
Klement Sekera277b89c2016-10-28 13:20:27 +020016 def __init__(self, logger):
17 self.logger = logger
18
Klement Sekeraf62ae122016-10-11 11:47:09 +020019 def before_api(self, api_name, api_args):
20 """
21 Function called before API call
22 Emit a debug message describing the API name and arguments
23
24 @param api_name: name of the API
25 @param api_args: tuple containing the API arguments
26 """
Klement Sekera277b89c2016-10-28 13:20:27 +020027 self.logger.debug("API: %s (%s)" %
28 (api_name, api_args), extra={'color': RED})
Klement Sekeraf62ae122016-10-11 11:47:09 +020029
30 def after_api(self, api_name, api_args):
31 """
32 Function called after API call
33
34 @param api_name: name of the API
35 @param api_args: tuple containing the API arguments
36 """
37 pass
38
39 def before_cli(self, cli):
40 """
41 Function called before CLI call
42 Emit a debug message describing the CLI
43
44 @param cli: CLI string
45 """
Klement Sekera277b89c2016-10-28 13:20:27 +020046 self.logger.debug("CLI: %s" % (cli), extra={'color': RED})
Klement Sekeraf62ae122016-10-11 11:47:09 +020047
48 def after_cli(self, cli):
49 """
50 Function called after CLI call
51 """
52 pass
53
54
55class VppDiedError(Exception):
56 pass
57
58
59class PollHook(Hook):
60 """ Hook which checks if the vpp subprocess is alive """
61
62 def __init__(self, testcase):
Klement Sekeraf62ae122016-10-11 11:47:09 +020063 self.testcase = testcase
Klement Sekera277b89c2016-10-28 13:20:27 +020064 self.logger = testcase.logger
Klement Sekeraf62ae122016-10-11 11:47:09 +020065
Klement Sekeraf62ae122016-10-11 11:47:09 +020066 def on_crash(self, core_path):
Klement Sekera01bbbe92016-11-02 09:25:05 +010067 if self.testcase.debug_core:
Klement Sekera05742262018-03-14 18:14:49 +010068 # notify parent process that we're handling a core file
69 open('%s/_core_handled' % self.testcase.tempdir, 'a').close()
Klement Sekera75e7d132017-09-20 08:26:30 +020070 spawn_gdb(self.testcase.vpp_bin, core_path, self.logger)
71 else:
Klement Sekera9b6ece72018-03-23 10:50:11 +010072 self.logger.error("Core file present, debug with: gdb %s %s" %
73 (self.testcase.vpp_bin, core_path))
Andrew Yourtchenko57612eb2018-03-28 15:32:10 +020074 check_core_path(self.logger, core_path)
Klement Sekera9b6ece72018-03-23 10:50:11 +010075 self.logger.error("Running `file %s':" % core_path)
76 try:
77 info = check_output(["file", core_path])
78 self.logger.error(info)
79 except CalledProcessError as e:
80 self.logger.error(
81 "Could not run `file' utility on core-file, "
82 "rc=%s" % e.returncode)
83 pass
Klement Sekeraf62ae122016-10-11 11:47:09 +020084
85 def poll_vpp(self):
86 """
87 Poll the vpp status and throw an exception if it's not running
88 :raises VppDiedError: exception if VPP is not running anymore
89 """
Klement Sekera085f5c02016-11-24 01:59:16 +010090 if self.testcase.vpp_dead:
Klement Sekeraf62ae122016-10-11 11:47:09 +020091 # already dead, nothing to do
92 return
93
94 self.testcase.vpp.poll()
95 if self.testcase.vpp.returncode is not None:
96 signaldict = dict(
97 (k, v) for v, k in reversed(sorted(signal.__dict__.items()))
98 if v.startswith('SIG') and not v.startswith('SIG_'))
Klement Sekera0e3c0de2016-09-29 14:43:44 +020099
100 if self.testcase.vpp.returncode in signaldict:
101 s = signaldict[abs(self.testcase.vpp.returncode)]
102 else:
103 s = "unknown"
Klement Sekerada505f62017-01-04 12:58:53 +0100104 msg = "VPP subprocess died unexpectedly with returncode %d [%s]" %\
105 (self.testcase.vpp.returncode, s)
Klement Sekera277b89c2016-10-28 13:20:27 +0200106 self.logger.critical(msg)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200107 core_path = self.testcase.tempdir + '/core'
108 if os.path.isfile(core_path):
109 self.on_crash(core_path)
110 self.testcase.vpp_dead = True
111 raise VppDiedError(msg)
112
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200113 def before_api(self, api_name, api_args):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200114 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200115 Check if VPP died before executing an API
Klement Sekeraf62ae122016-10-11 11:47:09 +0200116
117 :param api_name: name of the API
118 :param api_args: tuple containing the API arguments
119 :raises VppDiedError: exception if VPP is not running anymore
120
121 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200122 super(PollHook, self).before_api(api_name, api_args)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200123 self.poll_vpp()
124
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200125 def before_cli(self, cli):
Klement Sekeraf62ae122016-10-11 11:47:09 +0200126 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200127 Check if VPP died before executing a CLI
Klement Sekeraf62ae122016-10-11 11:47:09 +0200128
129 :param cli: CLI string
130 :raises Exception: exception if VPP is not running anymore
131
132 """
Klement Sekera0e3c0de2016-09-29 14:43:44 +0200133 super(PollHook, self).before_cli(cli)
Klement Sekeraf62ae122016-10-11 11:47:09 +0200134 self.poll_vpp()
Klement Sekera277b89c2016-10-28 13:20:27 +0200135
136
137class StepHook(PollHook):
138 """ Hook which requires user to press ENTER before doing any API/CLI """
139
140 def __init__(self, testcase):
141 self.skip_stack = None
142 self.skip_num = None
143 self.skip_count = 0
144 super(StepHook, self).__init__(testcase)
145
146 def skip(self):
147 if self.skip_stack is None:
148 return False
149 stack = traceback.extract_stack()
150 counter = 0
151 skip = True
152 for e in stack:
153 if counter > self.skip_num:
154 break
155 if e[0] != self.skip_stack[counter][0]:
156 skip = False
157 if e[1] != self.skip_stack[counter][1]:
158 skip = False
159 counter += 1
160 if skip:
161 self.skip_count += 1
162 return True
163 else:
164 print("%d API/CLI calls skipped in specified stack "
165 "frame" % self.skip_count)
166 self.skip_count = 0
167 self.skip_stack = None
168 self.skip_num = None
169 return False
170
171 def user_input(self):
172 print('number\tfunction\tfile\tcode')
173 counter = 0
174 stack = traceback.extract_stack()
175 for e in stack:
176 print('%02d.\t%s\t%s:%d\t[%s]' % (counter, e[2], e[0], e[1], e[3]))
177 counter += 1
178 print(single_line_delim)
juraj.linkes184870a2018-07-16 14:22:01 +0200179 print("You may enter a number of stack frame chosen from above")
Klement Sekera277b89c2016-10-28 13:20:27 +0200180 print("Calls in/below that stack frame will be not be stepped anymore")
181 print(single_line_delim)
182 while True:
juraj.linkes184870a2018-07-16 14:22:01 +0200183 print("Enter your choice, if any, and press ENTER to continue "
184 "running the testcase...")
185 choice = sys.stdin.readline()
Klement Sekera277b89c2016-10-28 13:20:27 +0200186 if choice == "":
187 choice = None
188 try:
189 if choice is not None:
190 num = int(choice)
juraj.linkes184870a2018-07-16 14:22:01 +0200191 except ValueError:
Klement Sekera277b89c2016-10-28 13:20:27 +0200192 print("Invalid input")
193 continue
194 if choice is not None and (num < 0 or num >= len(stack)):
195 print("Invalid choice")
196 continue
197 break
198 if choice is not None:
199 self.skip_stack = stack
200 self.skip_num = num
201
202 def before_cli(self, cli):
203 """ Wait for ENTER before executing CLI """
204 if self.skip():
205 print("Skip pause before executing CLI: %s" % cli)
206 else:
207 print(double_line_delim)
208 print("Test paused before executing CLI: %s" % cli)
209 print(single_line_delim)
210 self.user_input()
211 super(StepHook, self).before_cli(cli)
212
213 def before_api(self, api_name, api_args):
214 """ Wait for ENTER before executing API """
215 if self.skip():
216 print("Skip pause before executing API: %s (%s)"
217 % (api_name, api_args))
218 else:
219 print(double_line_delim)
220 print("Test paused before executing API: %s (%s)"
221 % (api_name, api_args))
222 print(single_line_delim)
223 self.user_input()
224 super(StepHook, self).before_api(api_name, api_args)