]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - scripts/kmsg-doc
UBUNTU: Start new release
[mirror_ubuntu-zesty-kernel.git] / scripts / kmsg-doc
CommitLineData
aea13bde
MS
1#!/usr/bin/perl -w
2#
3# kmsg kernel messages check and print tool.
4#
5# To check the source code for missing messages the script is called
6# with check, the name compiler and the compile parameters
7# kmsg-doc check $(CC) $(c_flags) $<
8# To create man pages for the messages the script is called with
9# kmsg-doc print $(CC) $(c_flags) $<
10#
11# Copyright IBM Corp. 2008
12# Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
13# Michael Holzheu <holzheu@linux.vnet.ibm.com>
14#
15
16use Cwd;
17use bigint;
18
19my $errors = 0;
20my $warnings = 0;
21my $srctree = "";
22my $objtree = "";
23my $kmsg_count = 0;
24
25sub remove_quotes($)
26{
27 my ($string) = @_;
28 my $inside = 0;
29 my $slash = 0;
30 my $result = "";
31
32 foreach my $str (split(/([\\"])/, $string)) {
33 if ($inside && ($str ne "\"" || $slash)) {
34 $result .= $str;
35 }
36 # Check for backslash before quote
37 if ($str eq "\"") {
38 if (!$slash) {
39 $inside = !$inside;
40 }
41 $slash = 0;
42 } elsif ($str eq "\\") {
43 $slash = !$slash;
44 } elsif ($str ne "") {
45 $slash = 0;
46 }
47 }
48 return $result;
49}
50
51sub string_to_bytes($)
52{
53 my ($string) = @_;
54 my %is_escape = ('"', 0x22, '\'', 0x27, 'n', 0x0a, 'r', 0x0d, 'b', 0x08,
55 't', 0x09, 'f', 0x0c, 'a', 0x07, 'v', 0x0b, '?', 0x3f);
56 my (@ar, $slash, $len);
57
58 # scan string, interpret backslash escapes and write bytes to @ar
59 $len = 0;
60 foreach my $ch (split(//, $string)) {
61 if ($ch eq '\\') {
62 $slash = !$slash;
63 if (!$slash) {
64 $ar[$len] = ord('\\');
65 $len++;
66 }
67 } elsif ($slash && defined $is_escape{$ch}) {
68 # C99 backslash escapes: \\ \" \' \n \r \b \t \f \a \v \?
69 $ar[$len] = $is_escape{$ch};
70 $len++;
71 $slash = 0;
72 } elsif ($slash) {
73 # FIXME: C99 backslash escapes \nnn \xhh
74 die("Unknown backslash escape in message $string.");
75 } else {
76 # normal character
77 $ar[$len] = ord($ch);
78 $len++;
79 }
80 }
81 return @ar;
82}
83
84sub calc_jhash($)
85{
86 my ($string) = @_;
87 my @ar;
88 my ($a, $b, $c, $i, $length, $len);
89
90 @ar = string_to_bytes($string);
91 $length = @ar;
92 # add dummy elements to @ar to avoid if then else hell
93 push @ar, (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
94 $a = 0x9e3779b9;
95 $b = 0x9e3779b9;
96 $c = 0;
97 $i = 0;
98 for ($len = $length + 12; $len >= 12; $len -= 12) {
99 if ($len < 24) {
100 # add length for last round
101 $c += $length;
102 }
103 $a += $ar[$i] + ($ar[$i+1]<<8) + ($ar[$i+2]<<16) + ($ar[$i+3]<<24);
104 $b += $ar[$i+4] + ($ar[$i+5]<<8) + ($ar[$i+6]<<16) + ($ar[$i+7]<<24);
105 if ($len >= 24) {
106 $c += $ar[$i+8] + ($ar[$i+9]<<8) + ($ar[$i+10]<<16) + ($ar[$i+11]<<24);
107 } else {
108 $c += ($ar[$i+8]<<8) + ($ar[$i+9]<<16) + ($ar[$i+10]<<24);
109 }
110 $a &= 0xffffffff; $b &= 0xffffffff; $c &= 0xffffffff;
111 $a -= $b; $a -= $c; $a ^= ($c >> 13); $a &= 0xffffffff;
112 $b -= $c; $b -= $a; $b ^= ($a << 8); $b &= 0xffffffff;
113 $c -= $a; $c -= $b; $c ^= ($b >> 13); $c &= 0xffffffff;
114 $a -= $b; $a -= $c; $a ^= ($c >> 12); $a &= 0xffffffff;
115 $b -= $c; $b -= $a; $b ^= ($a << 16); $b &= 0xffffffff;
116 $c -= $a; $c -= $b; $c ^= ($b >> 5); $c &= 0xffffffff;
117 $a -= $b; $a -= $c; $a ^= ($c >> 3); $a &= 0xffffffff;
118 $b -= $c; $b -= $a; $b ^= ($a << 10); $b &= 0xffffffff;
119 $c -= $a; $c -= $b; $c ^= ($b >> 15); $c &= 0xffffffff;
120 $i += 12;
121 }
122 return $c;
123}
124
125sub add_kmsg_desc($$$$$$)
126{
127 my ($component, $text, $sev, $argv, $desc, $user) = @_;
128 my ($hash, $tag);
129
130 $text = remove_quotes($text);
131 $hash = substr(sprintf("%08x", calc_jhash($text)), 2, 6);
132 $tag = $component . "." . $hash;
133
134 if ($kmsg_desc{$tag}) {
135 if ($text ne $kmsg_desc{$tag}->{'TEXT'}) {
136 warn "Duplicate message with tag $tag\n";
137 warn " --- $kmsg_desc{$tag}->{'TEXT'}\n";
138 warn " +++ $text\n";
139 } else {
140 warn "Duplicate message description for \"$text\"\n";
141 }
142 $errors++;
143 return;
144 }
145 $kmsg_desc{$tag}->{'TEXT'} = $text;
146 $kmsg_desc{$tag}->{'SEV'} = $sev;
147 $kmsg_desc{$tag}->{'ARGV'} = $argv;
148 $kmsg_desc{$tag}->{'DESC'} = $desc;
149 $kmsg_desc{$tag}->{'USER'} = $user;
150}
151
152sub add_kmsg_print($$$$)
153{
154 my ($component, $sev, $text, $argv) = @_;
155 my ($hash, $tag, $count, $parm);
156
157 $text = remove_quotes($text);
158 $hash = substr(sprintf("%08x", calc_jhash($text)), 2, 6);
159 $tag = $component . "." . $hash;
160
161 # Pretty print severity
162 $sev =~ s/"0"/Emerg/;
163 $sev =~ s/"1"/Alert/;
164 $sev =~ s/"2"/Critical/;
165 $sev =~ s/"3"/Error/;
166 $sev =~ s/"4"/Warning/;
167 $sev =~ s/"5"/Notice/;
168 $sev =~ s/"6"/Informational/;
169 $sev =~ s/"7"/Debug/;
170 $kmsg_print{$kmsg_count}->{'TAG'} = $tag;
171 $kmsg_print{$kmsg_count}->{'TEXT'} = $text;
172 $kmsg_print{$kmsg_count}->{'SEV'} = $sev;
173 $kmsg_print{$kmsg_count}->{'ARGV'} = $argv;
174 $kmsg_count += 1;
175}
176
177sub process_source_file($$)
178{
179 my ($component, $file) = @_;
180 my $state;
181 my ($text, $sev, $argv, $desc, $user);
182
183 if (!open(FD, "$file")) {
184 return "";
185 }
186
187 $state = 0;
188 while (<FD>) {
189 chomp;
190 # kmsg message component: #define KMSG_COMPONENT "<component>"
191 if (/^#define\s+KMSG_COMPONENT\s+\"(.*)\"[^\"]*$/o) {
192 $component = $1;
193 }
194 if ($state == 0) {
195 # single line kmsg for undocumented messages, format:
196 # /*? Text: "<message>" */
197 if (/^\s*\/\*\?\s*Text:\s*(\".*\")\s*\*\/\s*$/o) {
198 add_kmsg_desc($component, $1, "", "", "", "");
199 }
200 # kmsg message start: '/*?'
201 if (/^\s*\/\*\?\s*$/o) {
202 $state = 1;
203 ($text, $sev, $argv, $desc, $user) = ( "", "", "", "", "" );
204 }
205 } elsif ($state == 1) {
206 # kmsg message end: ' */'
207 if (/^\s*\*\/\s*/o) {
208 add_kmsg_desc($component, $text, $sev, $argv, $desc, $user);
209 $state = 0;
210 }
211 # kmsg message text: ' * Text: "<message>"'
212 elsif (/^\s*\*\s*Text:\s*(\".*\")\s*$/o) {
213 $text = $1;
214 }
215 # kmsg message severity: ' * Severity: <sev>'
216 elsif (/^\s*\*\s*Severity:\s*(\S*)\s*$/o) {
217 $sev = $1;
218 }
219 # kmsg message parameter: ' * Parameter: <argv>'
220 elsif (/^\s*\*\s*Parameter:\s*(\S*)\s*$/o) {
221 if (!defined($1)) {
222 $argv = "";
223 } else {
224 $argv = $1;
225 }
226 $state = 2;
227 }
228 # kmsg message description start: ' * Description:'
229 elsif (/^\s*\*\s*Description:\s*(\S*)\s*$/o) {
230 if (!defined($1)) {
231 $desc = "";
232 } else {
233 $desc = $1;
234 }
235 $state = 3;
236 }
237 # kmsg has unrecognizable lines
238 else {
239 warn "Warning(${file}:$.): Cannot understand $_";
240 $warnings++;
241 $state = 0;
242 }
243 } elsif ($state == 2) {
244 # kmsg message end: ' */'
245 if (/^\s*\*\//o) {
246 warn "Warning(${file}:$.): Missing description, skipping message";
247 $warnings++;
248 $state = 0;
249 }
250 # kmsg message description start: ' * Description:'
251 elsif (/^\s*\*\s*Description:\s*$/o) {
252 $desc = $1;
253 $state = 3;
254 }
255 # kmsg message parameter line: ' * <argv>'
256 elsif (/^\s*\*(.*)$/o) {
257 $argv .= "\n" . $1;
258 } else {
259 warn "Warning(${file}:$.): Cannot understand $_";
260 $warnings++;
261 $state = 0;
262 }
263 } elsif ($state == 3) {
264 # kmsg message end: ' */'
265 if (/^\s*\*\/\s*/o) {
266 add_kmsg_desc($component, $text, $sev, $argv, $desc, $user);
267 $state = 0;
268 }
269 # kmsg message description start: ' * User action:'
270 elsif (/^\s*\*\s*User action:\s*$/o) {
271 $user = $1;
272 $state = 4;
273 }
274 # kmsg message description line: ' * <text>'
275 elsif (/^\s*\*\s*(.*)$/o) {
276 $desc .= "\n" . $1;
277 } else {
278 warn "Warning(${file}:$.): Cannot understand $_";
279 $warnings++;
280 $state = 0;
281 }
282 } elsif ($state == 4) {
283 # kmsg message end: ' */'
284 if (/^\s*\*\/\s*/o) {
285 add_kmsg_desc($component, $text, $sev, $argv, $desc, $user);
286 $state = 0;
287 }
288 # kmsg message user action line: ' * <text>'
289 elsif (/^\s*\*\s*(.*)$/o) {
290 $user .= "\n" . $1;
291 } else {
292 warn "Warning(${file}:$.): Cannot understand $_";
293 $warnings++;
294 $state = 0;
295 }
296 }
297 }
298 return $component;
299}
300
301sub process_cpp_file($$$$)
302{
303 my ($cc, $options, $file, $component) = @_;
304
305 open(FD, "$cc $gcc_options|") or die ("Preprocessing failed.");
306
307 while (<FD>) {
308 chomp;
309 if (/.*__KMSG_PRINT\(\s*(\S*)\s*(\S*)\s*_FMT_(.*)_ARGS_\s*(.*)?_END_\s*\)/o) {
310 if ($component ne "") {
311 add_kmsg_print($component, $2, $3, $4);
312 } else {
313 warn "Error(${file}:$.): kmsg without component\n";
314 $errors++;
315 }
316 } elsif (/.*__KMSG_DEV\(\s*(\S*)\s*(\S*)\s*_FMT_(.*)_ARGS_\s*(.*)?_END_\s*\)/o) {
317 if ($component ne "") {
318 add_kmsg_print($component, $2, "\"%s: \"" . $3, $4);
319 } else {
320 warn "Error(${file}:$.): kmsg without component\n";
321 $errors++;
322 }
323 }
324 }
325}
326
327sub check_messages($)
328{
329 my $component = "@_";
330 my $failed = 0;
331
332 for ($i = 0; $i < $kmsg_count; $i++) {
333 $tag = $kmsg_print{$i}->{'TAG'};
334 if (!defined($kmsg_desc{$tag})) {
335 add_kmsg_desc($component,
336 "\"" . $kmsg_print{$i}->{'TEXT'} . "\"",
337 $kmsg_print{$i}->{'SEV'},
338 $kmsg_print{$i}->{'ARGV'},
339 "Please insert description here",
340 "What is the user supposed to do");
341 $kmsg_desc{$tag}->{'CHECK'} = 1;
342 $failed = 1;
343 warn "$component: Missing description for: ".
344 $kmsg_print{$i}->{'TEXT'}."\n";
345 $errors++;
346 next;
347 }
348 if ($kmsg_desc{$tag}->{'SEV'} ne "" &&
349 $kmsg_desc{$tag}->{'SEV'} ne $kmsg_print{$i}->{'SEV'}) {
350 warn "Message severity mismatch for \"$kmsg_print{$i}->{'TEXT'}\"\n";
351 warn " --- $kmsg_desc{$tag}->{'SEV'}\n";
352 warn " +++ $kmsg_print{$i}->{'SEV'}\n";
353 }
354 }
355 return $failed;
356}
357
358sub print_templates()
359{
360 print "Templates for missing messages:\n";
361 foreach $tag ( sort { $kmsg_desc{$a} <=> $kmsg_desc{$b} } keys %kmsg_desc ) {
362 if (!defined($kmsg_desc{$tag}->{'CHECK'})) {
363 next;
364 }
365 print "/*?\n";
366 print " * Text: \"$kmsg_desc{$tag}->{'TEXT'}\"\n";
367 print " * Severity: $kmsg_desc{$tag}->{'SEV'}\n";
368 $argv = $kmsg_desc{$tag}->{'ARGV'};
369 if ($argv ne "") {
370 print " * Parameter:\n";
371 @parms = split(/\s*,\s*/,$kmsg_desc{$tag}->{'ARGV'});
372 $count = 0;
373 foreach $parm (@parms) {
374 $count += 1;
375 if (!($parm eq "")) {
376 print " * \@$count: $parm\n";
377 }
378 }
379 }
380 print " * Description:\n";
381 print " * $kmsg_desc{$tag}->{'DESC'}\n";
382 print " * User action:\n";
383 print " * $kmsg_desc{$tag}->{'USER'}\n";
384 print " */\n\n";
385 }
386}
387
388sub write_man_pages()
389{
390 my ($i, $file);
391
392 for ($i = 0; $i < $kmsg_count; $i++) {
393 $tag = $kmsg_print{$i}->{'TAG'};
394 if (!defined($kmsg_desc{$tag}) ||
395 defined($kmsg_desc{$tag}->{'CHECK'}) ||
396 $kmsg_desc{$tag}->{'DESC'} eq "") {
397 next;
398 }
399 $file = $objtree . "man/" . $tag . ".9";
400 if (!open(WR, ">$file")) {
401 warn "Error: Cannot open file $file\n";
402 $errors++;
403 return;
404 }
405 print WR ".TH \"$tag\" 9 \"Linux Messages\" LINUX\n";
406 print WR ".SH Message\n";
407 print WR $tag . ": " . $kmsg_desc{$tag}->{'TEXT'} . "\n";
408 print WR ".SH Severity\n";
409 print WR "$kmsg_desc{$tag}->{'SEV'}\n";
410 $argv = $kmsg_desc{$tag}->{'ARGV'};
411 if ($argv ne "") {
412 print WR ".SH Parameters\n";
413 @parms = split(/\s*\n\s*/,$kmsg_desc{$tag}->{'ARGV'});
414 foreach $parm (@parms) {
415 $parm =~ s/^\s*(.*)\s*$/$1/;
416 if (!($parm eq "")) {
417 print WR "$parm\n\n";
418 }
419 }
420 }
421 print WR ".SH Description";
422 print WR "$kmsg_desc{$tag}->{'DESC'}\n";
423 $user = $kmsg_desc{$tag}->{'USER'};
424 if ($user ne "") {
425 print WR ".SH User action";
426 print WR "$user\n";
427 }
428 }
429}
430
431if (defined($ENV{'srctree'})) {
432 $srctree = "$ENV{'srctree'}" . "/";
433} else {
434 $srctree = getcwd;
435}
436
437if (defined($ENV{'objtree'})) {
438 $objtree = "$ENV{'objtree'}" . "/";
439} else {
440 $objtree = getcwd;
441}
442
443if (defined($ENV{'SRCARCH'})) {
444 $srcarch = "$ENV{'SRCARCH'}" . "/";
445} else {
446 print "kmsg-doc called without a valid \$SRCARCH\n";
447 exit 1;
448}
449
450$option = shift;
451
452$cc = shift;
453$gcc_options = "-E -D __KMSG_CHECKER ";
454foreach $tmp (@ARGV) {
455 $tmp =~ s/\(/\\\(/;
456 $tmp =~ s/\)/\\\)/;
457 $gcc_options .= " $tmp";
458 $filename = $tmp;
459}
460
461$component = process_source_file("", $filename);
462if ($component ne "") {
463 process_source_file($component, $srctree . "Documentation/kmsg/" .
464 $srcarch . $component);
465 process_source_file($component, $srctree . "Documentation/kmsg/" .
466 $component);
467}
468
469process_cpp_file($cc, $gcc_options, $filename, $component);
470if ($option eq "check") {
471 if (check_messages($component)) {
472 print_templates();
473 }
474} elsif ($option eq "print") {
475 write_man_pages();
476}
477
478exit($errors);