6Base classes for steps in Trigger ART tests
16from threading
import Timer
17from TrigValTools.TrigValSteering.Common
import get_logger, art_result, running_in_CI
18from TestTools.logfiles
import grep_with_context
19from TrigValTools.TrigARTUtils
import remember_cwd
23 '''Base class for a step of a Trigger ART test'''
32 self.
log = get_logger()
56 Can be implemented by derived classes.
57 Base class implementation only prints the configuration to debug log.
60 'Step configuration:\n-- %s\n',
61 '\n-- '.join([
'{}: {}'.format(k, v)
for k, v
in self.__dict__.items()]))
65 if self.
result is not None:
71 if self.
name is not None:
76 art_result(result, name)
80 Print an error message (arguments passed to logging.error),
81 report non-zero art-result and exit the process with non-zero code
83 self.
log.
error(
'Misconfiguration in %s: '+error_msg, self.
name, *args, **kwargs)
89 Produce a backtrace for a process and its children, then call
90 os.killpg on the process. The last argument is a list of strings
91 where the first is filled with the backtrace by this function
92 (it has to be a list to be mutable).
97 parent = psutil.Process(pid)
99 for proc
in [parent] + parent.children(recursive=
True):
100 backtrace +=
'\nTraceback for {} PID {}:\n'.format(proc.name(), proc.pid)
101 backtrace += subprocess.check_output(
'$ROOTSYS/etc/gdb-backtrace.sh {}'.format(proc.pid),
102 stderr=subprocess.STDOUT, shell=
True).decode(
'utf-8')
103 backtrace_list[0] = backtrace
106 os.killpg(pid, signal)
108 except Exception
as e:
110 msg =
'Caught exception while generating backtrace: ' + str(e)
111 backtrace_list[0] = msg
116 Execute a shell process and kill it if it doesn't finish
117 before timeout_sec seconds pass. The implementation is based on
118 https://stackoverflow.com/a/10012262 and https://stackoverflow.com/a/4791612
119 In addition, a backtrace is produced for the timed out process and its children.
121 proc = subprocess.Popen(cmd, shell=
True, preexec_fn=os.setsid)
122 backtrace_list = [
'']
124 [os.getpgid(proc.pid), signal.SIGKILL, backtrace_list])
131 if proc.returncode == -signal.SIGKILL:
132 self.
log.
error(
'%s killed on timeout of %s seconds',
138 'ERROR process killed on timeout '
139 'of {} seconds, command was {}\n'.format(
141 log_file.write(backtrace_list[0])
142 return signal.SIGKILL
144 return proc.returncode
147 self.
log.
debug(
'Starting prmon for pid %d', os.getpid())
148 prmon_cmd =
'prmon --pid {:d} --interval {:d}'.format(os.getpid(), self.
prmon_interval)
149 prmon_cmd +=
' --filename prmon.{name:s}.txt --json-summary prmon.summary.{name:s}.json'.format(name=self.
name)
150 prmon_cmd +=
' --log-filename prmon.{name:s}.log'.format(name=self.
name)
151 return subprocess.Popen(prmon_cmd, shell=
True)
156 prmon_proc.send_signal(signal.SIGUSR1)
158 while (
not prmon_proc.poll())
and countWait < 10:
161 except OSError
as err:
162 self.
log.warning(
'Error while stopping prmon: %s', err)
165 def run(self, dry_run=False):
172 cmd +=
' >/dev/null 2>&1'
176 cmd +=
' 2>&1 | tee {}; exit ${{PIPESTATUS[0]}}'.format(self.
get_log_file_name())
180 self.
log.info(
'Running %s step using command:\n%s', self.
name, cmd)
186 assert '..' not in self.
workdir,
"Illegal path for workdir -- must be a subdirectory of CWD"
187 assert not self.
workdir.startswith(
'/'),
"Illegal path for workdir -- no absolute paths!"
188 os.makedirs(self.
workdir,exist_ok=
True)
196 self.
result = subprocess.call(cmd, shell=
True)
206 and running_in_CI() \
211 error_patterns =
'^ERROR| ERROR | FATAL |[Tt]raceback'
217 step_matches = re.findall(
'Logs for (.*) are in (.*)', log)
219 self.
log.warning(
'Failed to determine sub-step log names, cannot print the full sub-step logs')
221 step_log_names = [m[1]
for m
in step_matches]
222 for step_log_name
in step_log_names:
223 if os.path.isfile(step_log_name):
224 self.
log.info(
'Printing partial sub-step log file %s', step_log_name)
225 grep_with_context(open(step_log_name), error_patterns, lines=100)
232 Retrieve the first test matching the name from the list. Returns None if
235 for step
in step_list:
236 if step.name
is not None and step_name
in step.name:
243 Retrieve the first test matching the type from the list. Returns None if
246 for step
in step_list:
247 if isinstance(step, step_type):
misconfig_abort(self, error_msg, *args, **kwargs)
__stop_prmon(self, prmon_proc)
configure(self, test=None)
__execute_with_timeout(self, cmd, timeout_sec)
__init__(self, name=None)
report_result(self, result=None, name=None)
__trace_and_kill(self, pid, signal, backtrace_list)
get_step_from_list(step_name, step_list)
get_step_type_from_list(step_type, step_list)
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)