diff options
| -rw-r--r-- | meta/lib/oeqa/runtime/parselogs.py | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/meta/lib/oeqa/runtime/parselogs.py b/meta/lib/oeqa/runtime/parselogs.py new file mode 100644 index 0000000000..4d4a21b12f --- /dev/null +++ b/meta/lib/oeqa/runtime/parselogs.py | |||
| @@ -0,0 +1,128 @@ | |||
| 1 | import os | ||
| 2 | import unittest | ||
| 3 | from oeqa.oetest import oeRuntimeTest | ||
| 4 | from oeqa.utils.decorators import * | ||
| 5 | |||
| 6 | #in the future these lists could be moved outside of module | ||
| 7 | errors = ["error", "cannot", "can\'t", "failed"] | ||
| 8 | |||
| 9 | ignore_errors = { 'genericx86-64': ['mmci-pl18x', 'error changing net interface name', 'dma timeout'], \ | ||
| 10 | 'genericx86': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'AE_ALREADY_EXISTS'], \ | ||
| 11 | 'emenlow': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'AE_ALREADY_EXISTS', '\[drm:psb_do_init\] \*ERROR\* Debug is'], \ | ||
| 12 | 'crownbay': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'AE_ALREADY_EXISTS', 'Could not enable PowerButton event', 'probe of LNXPWRBN:00 failed with error -22'], \ | ||
| 13 | 'qemuarm': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'mmci-pl18x: probe of fpga:[0-f][0-f] failed with error -38', 'wrong ELF class', 'Fast TSC calibration', 'AE_NOT_FOUND', 'Open ACPI failed', 'Failed to load module "glx"', '\(EE\) error', 'perfctr msr \(MSR c1 is 0\)', 'MMCONFIG information'], \ | ||
| 14 | 'qemux86-64': ['mmci-pl18x', 'error changing net interface name', 'dma timeout', 'wrong ELF class', 'Fast TSC calibration', 'AE_NOT_FOUND', 'Open ACPI failed', 'Failed to load module "glx"', '\(EE\) error', 'perfctr msr \(MSR c1 is 0\)', 'MMCONFIG information'] } | ||
| 15 | |||
| 16 | log_locations = ["/var/log/","/var/log/dmesg", "/tmp/dmesg_output.log"] | ||
| 17 | |||
| 18 | class ParseLogsTest(oeRuntimeTest): | ||
| 19 | |||
| 20 | @classmethod | ||
| 21 | def setUpClass(self): | ||
| 22 | self.errors = errors | ||
| 23 | self.ignore_errors = ignore_errors | ||
| 24 | self.log_locations = log_locations | ||
| 25 | self.msg = "" | ||
| 26 | |||
| 27 | def getMachine(self): | ||
| 28 | (status, output) = self.target.run("uname -n") | ||
| 29 | return output | ||
| 30 | |||
| 31 | #get some information on the CPU of the machine to display at the beginning of the output. This info might be useful in some cases. | ||
| 32 | def getHardwareInfo(self): | ||
| 33 | hwi = "" | ||
| 34 | (status, cpu_name) = self.target.run("cat /proc/cpuinfo | grep \"model name\" | head -n1 | awk 'BEGIN{FS=\":\"}{print $2}'") | ||
| 35 | (status, cpu_physical_cores) = self.target.run("cat /proc/cpuinfo | grep \"cpu cores\" | head -n1 | awk {'print $4'}") | ||
| 36 | (status, cpu_logical_cores) = self.target.run("cat /proc/cpuinfo | grep \"processor\" | wc -l") | ||
| 37 | (status, cpu_arch) = self.target.run("uname -m") | ||
| 38 | hwi += "Machine information: \n" | ||
| 39 | hwi += "*******************************\n" | ||
| 40 | hwi += "Machine name: "+self.getMachine()+"\n" | ||
| 41 | hwi += "CPU: "+str(cpu_name)+"\n" | ||
| 42 | hwi += "Arch: "+str(cpu_arch)+"\n" | ||
| 43 | hwi += "Physical cores: "+str(cpu_physical_cores)+"\n" | ||
| 44 | hwi += "Logical cores: "+str(cpu_logical_cores)+"\n" | ||
| 45 | hwi += "*******************************\n" | ||
| 46 | return hwi | ||
| 47 | |||
| 48 | #go through the log locations provided and if it's a folder create a list with all the .log files in it, if it's a file just add | ||
| 49 | #it to that list | ||
| 50 | def getLogList(self, log_locations): | ||
| 51 | logs = [] | ||
| 52 | for location in log_locations: | ||
| 53 | (status, output) = self.target.run("test -f "+str(location)) | ||
| 54 | if (status == 0): | ||
| 55 | logs.append(str(location)) | ||
| 56 | else: | ||
| 57 | (status, output) = self.target.run("test -d "+str(location)) | ||
| 58 | if (status == 0): | ||
| 59 | (status, output) = self.target.run("find "+str(location)+"/*.log -maxdepth 1 -type f") | ||
| 60 | output = output.splitlines() | ||
| 61 | for logfile in output: | ||
| 62 | logs.append(os.path.join(location,str(logfile))) | ||
| 63 | return logs | ||
| 64 | |||
| 65 | #build the grep command to be used with filters and exclusions | ||
| 66 | def build_grepcmd(self, errors, ignore_errors, log): | ||
| 67 | grepcmd = "grep " | ||
| 68 | grepcmd +="-Ei \"" | ||
| 69 | for error in errors: | ||
| 70 | grepcmd += error+"|" | ||
| 71 | grepcmd = grepcmd[:-1] | ||
| 72 | grepcmd += "\" "+str(log)+" | grep -Eiv \'" | ||
| 73 | try: | ||
| 74 | errorlist = ignore_errors[self.getMachine()] | ||
| 75 | except KeyError: | ||
| 76 | self.msg += "No ignore list found for this machine, using generic\n" | ||
| 77 | errorlist = ignore_errors['genericx86'] | ||
| 78 | for ignore_error in errorlist: | ||
| 79 | grepcmd += ignore_error+"|" | ||
| 80 | grepcmd = grepcmd[:-1] | ||
| 81 | grepcmd += "\'" | ||
| 82 | return grepcmd | ||
| 83 | |||
| 84 | #grep only the errors so that their context could be collected. Default context is 10 lines before and after the error itself | ||
| 85 | def parse_logs(self, errors, ignore_errors, logs, lines_before = 10, lines_after = 10): | ||
| 86 | results = {} | ||
| 87 | rez = [] | ||
| 88 | for log in logs: | ||
| 89 | thegrep = self.build_grepcmd(errors, ignore_errors, log) | ||
| 90 | try: | ||
| 91 | (status, result) = self.target.run(thegrep) | ||
| 92 | except: | ||
| 93 | pass | ||
| 94 | if result: | ||
| 95 | results[log] = {} | ||
| 96 | rez = result.splitlines() | ||
| 97 | for xrez in rez: | ||
| 98 | command = "grep \"\\"+str(xrez)+"\" -B "+str(lines_before)+" -A "+str(lines_after)+" "+str(log) | ||
| 99 | try: | ||
| 100 | (status, yrez) = self.target.run(command) | ||
| 101 | except: | ||
| 102 | pass | ||
| 103 | results[log][xrez]=yrez | ||
| 104 | return results | ||
| 105 | |||
| 106 | #get the output of dmesg and write it in a file. This file is added to log_locations. | ||
| 107 | def write_dmesg(self): | ||
| 108 | (status, dmesg) = self.target.run("dmesg") | ||
| 109 | (status, dmesg2) = self.target.run("echo \""+str(dmesg)+"\" > /tmp/dmesg_output.log") | ||
| 110 | |||
| 111 | @skipUnlessPassed('test_ssh') | ||
| 112 | def test_parselogs(self): | ||
| 113 | self.write_dmesg() | ||
| 114 | log_list = self.getLogList(self.log_locations) | ||
| 115 | result = self.parse_logs(self.errors, self.ignore_errors, log_list) | ||
| 116 | print self.getHardwareInfo() | ||
| 117 | errcount = 0 | ||
| 118 | for log in result: | ||
| 119 | self.msg += "Log: "+log+"\n" | ||
| 120 | self.msg += "-----------------------\n" | ||
| 121 | for error in result[log]: | ||
| 122 | errcount += 1 | ||
| 123 | self.msg += "Central error: "+str(error)+"\n" | ||
| 124 | self.msg += "***********************\n" | ||
| 125 | self.msg += result[str(log)][str(error)]+"\n" | ||
| 126 | self.msg += "***********************\n" | ||
| 127 | self.msg += "%s errors found in logs." % errcount | ||
| 128 | self.assertEqual(errcount, 0, msg=self.msg) \ No newline at end of file | ||
