summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/utils/logparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/utils/logparser.py')
-rw-r--r--meta/lib/oeqa/utils/logparser.py114
1 files changed, 73 insertions, 41 deletions
diff --git a/meta/lib/oeqa/utils/logparser.py b/meta/lib/oeqa/utils/logparser.py
index 7313df8ec3..496d9e0c90 100644
--- a/meta/lib/oeqa/utils/logparser.py
+++ b/meta/lib/oeqa/utils/logparser.py
@@ -1,8 +1,10 @@
#
+# Copyright OpenEmbedded Contributors
+#
# SPDX-License-Identifier: MIT
#
-import sys
+import enum
import os
import re
@@ -25,16 +27,25 @@ class PtestParser(object):
section_regex['exitcode'] = re.compile(r"^ERROR: Exit status is (.+)")
section_regex['timeout'] = re.compile(r"^TIMEOUT: .*/(.+)/ptest")
+ # Cache markers so we don't take the re.search() hit all the time.
+ markers = ("PASS:", "FAIL:", "SKIP:", "BEGIN:", "END:", "DURATION:", "ERROR: Exit", "TIMEOUT:")
+
def newsection():
- return { 'name': "No-section", 'log': "" }
+ return { 'name': "No-section", 'log': [] }
current_section = newsection()
with open(logfile, errors='replace') as f:
for line in f:
+ if not line.startswith(markers):
+ current_section['log'].append(line)
+ continue
+
result = section_regex['begin'].search(line)
if result:
current_section['name'] = result.group(1)
+ if current_section['name'] not in self.results:
+ self.results[current_section['name']] = {}
continue
result = section_regex['end'].search(line)
@@ -61,14 +72,20 @@ class PtestParser(object):
current_section[t] = result.group(1)
continue
- current_section['log'] = current_section['log'] + line
+ current_section['log'].append(line)
for t in test_regex:
result = test_regex[t].search(line)
if result:
- if current_section['name'] not in self.results:
- self.results[current_section['name']] = {}
- self.results[current_section['name']][result.group(1).strip()] = t
+ try:
+ self.results[current_section['name']][result.group(1).strip()] = t
+ except KeyError:
+ bb.warn("Result with no section: %s - %s" % (t, result.group(1).strip()))
+
+ # Python performance for repeatedly joining long strings is poor, do it all at once at the end.
+ # For 2.1 million lines in a log this reduces 18 hours to 12s.
+ for section in self.sections:
+ self.sections[section]['log'] = "".join(self.sections[section]['log'])
return self.results, self.sections
@@ -89,30 +106,48 @@ class PtestParser(object):
f.write(status + ": " + test_name + "\n")
-# ltp log parsing
-class LtpParser(object):
- def __init__(self):
- self.results = {}
- self.section = {'duration': "", 'log': ""}
-
+class LtpParser:
+ """
+ Parse the machine-readable LTP log output into a ptest-friendly data structure.
+ """
def parse(self, logfile):
- test_regex = {}
- test_regex['PASSED'] = re.compile(r"PASS")
- test_regex['FAILED'] = re.compile(r"FAIL")
- test_regex['SKIPPED'] = re.compile(r"SKIP")
-
- with open(logfile, errors='replace') as f:
+ results = {}
+ # Aaccumulate the duration here but as the log rounds quick tests down
+ # to 0 seconds this is very much a lower bound. The caller can replace
+ # the value.
+ section = {"duration": 0, "log": ""}
+
+ class LtpExitCode(enum.IntEnum):
+ # Exit codes as defined in ltp/include/tst_res_flags.h
+ TPASS = 0 # Test passed flag
+ TFAIL = 1 # Test failed flag
+ TBROK = 2 # Test broken flag
+ TWARN = 4 # Test warning flag
+ TINFO = 16 # Test information flag
+ TCONF = 32 # Test not appropriate for configuration flag
+
+ with open(logfile, errors="replace") as f:
+ # Lines look like this:
+ # tag=cfs_bandwidth01 stime=1689762564 dur=0 exit=exited stat=32 core=no cu=0 cs=0
for line in f:
- for t in test_regex:
- result = test_regex[t].search(line)
- if result:
- self.results[line.split()[0].strip()] = t
+ if not line.startswith("tag="):
+ continue
- for test in self.results:
- result = self.results[test]
- self.section['log'] = self.section['log'] + ("%s: %s\n" % (result.strip()[:-2], test.strip()))
+ values = dict(s.split("=") for s in line.strip().split())
- return self.results, self.section
+ section["duration"] += int(values["dur"])
+ exitcode = int(values["stat"])
+ if values["exit"] == "exited" and exitcode == LtpExitCode.TCONF:
+ # Exited normally with the "invalid configuration" code
+ results[values["tag"]] = "SKIPPED"
+ elif exitcode == LtpExitCode.TPASS:
+ # Successful exit
+ results[values["tag"]] = "PASSED"
+ else:
+ # Other exit
+ results[values["tag"]] = "FAILED"
+
+ return results, section
# ltp Compliance log parsing
@@ -123,30 +158,27 @@ class LtpComplianceParser(object):
def parse(self, logfile):
test_regex = {}
- test_regex['PASSED'] = re.compile(r"^PASS")
- test_regex['FAILED'] = re.compile(r"^FAIL")
- test_regex['SKIPPED'] = re.compile(r"(?:UNTESTED)|(?:UNSUPPORTED)")
+ test_regex['FAILED'] = re.compile(r"FAIL")
section_regex = {}
- section_regex['test'] = re.compile(r"^Testing")
+ section_regex['test'] = re.compile(r"^Executing")
with open(logfile, errors='replace') as f:
+ name = logfile
+ result = "PASSED"
for line in f:
- result = section_regex['test'].search(line)
- if result:
- self.name = ""
- self.name = line.split()[1].strip()
- self.results[self.name] = "PASSED"
- failed = 0
+ regex_result = section_regex['test'].search(line)
+ if regex_result:
+ name = line.split()[1].strip()
- failed_result = test_regex['FAILED'].search(line)
- if failed_result:
- failed = line.split()[1].strip()
- if int(failed) > 0:
- self.results[self.name] = "FAILED"
+ regex_result = test_regex['FAILED'].search(line)
+ if regex_result:
+ result = "FAILED"
+ self.results[name] = result
for test in self.results:
result = self.results[test]
+ print (self.results)
self.section['log'] = self.section['log'] + ("%s: %s\n" % (result.strip()[:-2], test.strip()))
return self.results, self.section