diff options
Diffstat (limited to 'meta/lib/oeqa/runtime/parselogs.py')
-rw-r--r-- | meta/lib/oeqa/runtime/parselogs.py | 178 |
1 files changed, 178 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..42cb1b5e6f --- /dev/null +++ b/meta/lib/oeqa/runtime/parselogs.py | |||
@@ -0,0 +1,178 @@ | |||
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 | common_errors = [ | ||
10 | '(WW) warning, (EE) error, (NI) not implemented, (??) unknown.', | ||
11 | 'dma timeout', | ||
12 | 'can\'t add hid device:', | ||
13 | 'usbhid: probe of ', | ||
14 | ] | ||
15 | |||
16 | x86_common = [ | ||
17 | '[drm:psb_do_init] *ERROR* Debug is', | ||
18 | 'wrong ELF class', | ||
19 | 'Could not enable PowerButton event', | ||
20 | 'probe of LNXPWRBN:00 failed with error -22', | ||
21 | ] + common_errors | ||
22 | |||
23 | qemux86_common = [ | ||
24 | 'Fast TSC calibration', | ||
25 | '_OSC failed (AE_NOT_FOUND); disabling ASPM', | ||
26 | 'Open ACPI failed (/var/run/acpid.socket) (No such file or directory)', | ||
27 | 'Failed to load module "vesa"', | ||
28 | 'Failed to load module "modesetting"', | ||
29 | 'Failed to load module "glx"', | ||
30 | 'wrong ELF class', | ||
31 | ] + common_errors | ||
32 | |||
33 | ignore_errors = { | ||
34 | 'default' : common_errors, | ||
35 | 'qemux86' : [ | ||
36 | 'Failed to access perfctr msr (MSR c1 is 0)', | ||
37 | "fail to add MMCONFIG information, can't access extended PCI configuration space under this bridge.", | ||
38 | ] + qemux86_common, | ||
39 | 'qemux86-64' : qemux86_common, | ||
40 | 'qemumips' : [ | ||
41 | 'Failed to load module "glx"', | ||
42 | ] + common_errors, | ||
43 | 'qemuppc' : [ | ||
44 | 'PCI 0000:00 Cannot reserve Legacy IO [io 0x0000-0x0fff]', | ||
45 | 'mode "640x480" test failed', | ||
46 | 'Failed to load module "glx"', | ||
47 | ] + common_errors, | ||
48 | 'qemuarm' : [ | ||
49 | 'mmci-pl18x: probe of fpga:05 failed with error -22', | ||
50 | 'mmci-pl18x: probe of fpga:0b failed with error -22', | ||
51 | 'Failed to load module "glx"' | ||
52 | ] + common_errors, | ||
53 | 'emenlow' : x86_common, | ||
54 | 'crownbay' : x86_common, | ||
55 | 'genericx86' : x86_common, | ||
56 | 'genericx86-64' : x86_common, | ||
57 | } | ||
58 | |||
59 | log_locations = ["/var/log/","/var/log/dmesg", "/tmp/dmesg_output.log"] | ||
60 | |||
61 | class ParseLogsTest(oeRuntimeTest): | ||
62 | |||
63 | @classmethod | ||
64 | def setUpClass(self): | ||
65 | self.errors = errors | ||
66 | self.ignore_errors = ignore_errors | ||
67 | self.log_locations = log_locations | ||
68 | self.msg = "" | ||
69 | |||
70 | def getMachine(self): | ||
71 | (status, output) = self.target.run("uname -n") | ||
72 | return output | ||
73 | |||
74 | #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. | ||
75 | def getHardwareInfo(self): | ||
76 | hwi = "" | ||
77 | (status, cpu_name) = self.target.run("cat /proc/cpuinfo | grep \"model name\" | head -n1 | awk 'BEGIN{FS=\":\"}{print $2}'") | ||
78 | (status, cpu_physical_cores) = self.target.run("cat /proc/cpuinfo | grep \"cpu cores\" | head -n1 | awk {'print $4'}") | ||
79 | (status, cpu_logical_cores) = self.target.run("cat /proc/cpuinfo | grep \"processor\" | wc -l") | ||
80 | (status, cpu_arch) = self.target.run("uname -m") | ||
81 | hwi += "Machine information: \n" | ||
82 | hwi += "*******************************\n" | ||
83 | hwi += "Machine name: "+self.getMachine()+"\n" | ||
84 | hwi += "CPU: "+str(cpu_name)+"\n" | ||
85 | hwi += "Arch: "+str(cpu_arch)+"\n" | ||
86 | hwi += "Physical cores: "+str(cpu_physical_cores)+"\n" | ||
87 | hwi += "Logical cores: "+str(cpu_logical_cores)+"\n" | ||
88 | hwi += "*******************************\n" | ||
89 | return hwi | ||
90 | |||
91 | #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 | ||
92 | #it to that list | ||
93 | def getLogList(self, log_locations): | ||
94 | logs = [] | ||
95 | for location in log_locations: | ||
96 | (status, output) = self.target.run("test -f "+str(location)) | ||
97 | if (status == 0): | ||
98 | logs.append(str(location)) | ||
99 | else: | ||
100 | (status, output) = self.target.run("test -d "+str(location)) | ||
101 | if (status == 0): | ||
102 | (status, output) = self.target.run("find "+str(location)+"/*.log -maxdepth 1 -type f") | ||
103 | output = output.splitlines() | ||
104 | for logfile in output: | ||
105 | logs.append(os.path.join(location,str(logfile))) | ||
106 | return logs | ||
107 | |||
108 | #build the grep command to be used with filters and exclusions | ||
109 | def build_grepcmd(self, errors, ignore_errors, log): | ||
110 | grepcmd = "grep " | ||
111 | grepcmd +="-Ei \"" | ||
112 | for error in errors: | ||
113 | grepcmd += error+"|" | ||
114 | grepcmd = grepcmd[:-1] | ||
115 | grepcmd += "\" "+str(log)+" | grep -Eiv \'" | ||
116 | try: | ||
117 | errorlist = ignore_errors[self.getMachine()] | ||
118 | except KeyError: | ||
119 | self.msg += "No ignore list found for this machine, using default\n" | ||
120 | errorlist = ignore_errors['default'] | ||
121 | for ignore_error in errorlist: | ||
122 | ignore_error = ignore_error.replace("(", "\(") | ||
123 | ignore_error = ignore_error.replace(")", "\)") | ||
124 | ignore_error = ignore_error.replace("'", ".") | ||
125 | ignore_error = ignore_error.replace("?", "\?") | ||
126 | ignore_error = ignore_error.replace("[", "\[") | ||
127 | ignore_error = ignore_error.replace("]", "\]") | ||
128 | ignore_error = ignore_error.replace("*", "\*") | ||
129 | grepcmd += ignore_error+"|" | ||
130 | grepcmd = grepcmd[:-1] | ||
131 | grepcmd += "\'" | ||
132 | return grepcmd | ||
133 | |||
134 | #grep only the errors so that their context could be collected. Default context is 10 lines before and after the error itself | ||
135 | def parse_logs(self, errors, ignore_errors, logs, lines_before = 10, lines_after = 10): | ||
136 | results = {} | ||
137 | rez = [] | ||
138 | for log in logs: | ||
139 | thegrep = self.build_grepcmd(errors, ignore_errors, log) | ||
140 | try: | ||
141 | (status, result) = self.target.run(thegrep) | ||
142 | except: | ||
143 | pass | ||
144 | if result: | ||
145 | results[log] = {} | ||
146 | rez = result.splitlines() | ||
147 | for xrez in rez: | ||
148 | command = "grep \"\\"+str(xrez)+"\" -B "+str(lines_before)+" -A "+str(lines_after)+" "+str(log) | ||
149 | try: | ||
150 | (status, yrez) = self.target.run(command) | ||
151 | except: | ||
152 | pass | ||
153 | results[log][xrez]=yrez | ||
154 | return results | ||
155 | |||
156 | #get the output of dmesg and write it in a file. This file is added to log_locations. | ||
157 | def write_dmesg(self): | ||
158 | (status, dmesg) = self.target.run("dmesg") | ||
159 | (status, dmesg2) = self.target.run("echo \""+str(dmesg)+"\" > /tmp/dmesg_output.log") | ||
160 | |||
161 | @skipUnlessPassed('test_ssh') | ||
162 | def test_parselogs(self): | ||
163 | self.write_dmesg() | ||
164 | log_list = self.getLogList(self.log_locations) | ||
165 | result = self.parse_logs(self.errors, self.ignore_errors, log_list) | ||
166 | print self.getHardwareInfo() | ||
167 | errcount = 0 | ||
168 | for log in result: | ||
169 | self.msg += "Log: "+log+"\n" | ||
170 | self.msg += "-----------------------\n" | ||
171 | for error in result[log]: | ||
172 | errcount += 1 | ||
173 | self.msg += "Central error: "+str(error)+"\n" | ||
174 | self.msg += "***********************\n" | ||
175 | self.msg += result[str(log)][str(error)]+"\n" | ||
176 | self.msg += "***********************\n" | ||
177 | self.msg += "%s errors found in logs." % errcount | ||
178 | self.assertEqual(errcount, 0, msg=self.msg) | ||