Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / scripts / kmsg-doc
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
16 use Cwd;
17 use bigint;
18
19 my $errors = 0;
20 my $warnings = 0;
21 my $srctree = "";
22 my $objtree = "";
23 my $kmsg_count = 0;
24
25 sub 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
51 sub 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
84 sub 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
125 sub 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
152 sub 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
177 sub 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
301 sub 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*_FMT_(.*)_ARGS_\s*"(.*)"\s*_END_\s*\)/o) {
310             if ($component ne "") {
311                 add_kmsg_print($component, $1, $2, $3);
312             } else {
313                 warn "Error(${file}:$.): kmsg without component\n";
314                 $errors++;
315             }
316         } elsif (/.*__KMSG_DEV\(\s*(\S*)\s*_FMT_(.*)_ARGS_\s*(.*)?_END_\s*\)/o) {
317             if ($component ne "") {
318                 add_kmsg_print($component, $1, "\"%s: \"" . $2, $3);
319             } else {
320                 warn "Error(${file}:$.): kmsg without component\n";
321                 $errors++;
322             }
323         }
324     }
325 }
326
327 sub 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
358 sub 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
388 sub 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
431 if (defined($ENV{'srctree'})) {
432     $srctree = "$ENV{'srctree'}" . "/";
433 } else {
434     $srctree = getcwd;
435 }
436
437 if (defined($ENV{'objtree'})) {
438     $objtree = "$ENV{'objtree'}" . "/";
439 } else {
440     $objtree = getcwd;
441 }
442
443 if (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 ";
454 foreach $tmp (@ARGV) {
455     $tmp =~ s/\(/\\\(/;
456     $tmp =~ s/\)/\\\)/;
457     $gcc_options .= " $tmp";
458     $filename = $tmp;
459 }
460
461 $component = process_source_file("", $filename);
462 if ($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
469 process_cpp_file($cc, $gcc_options, $filename, $component);
470 if ($option eq "check") {
471     if (check_messages($component)) {
472         print_templates();
473     }
474 } elsif ($option eq "print") {
475     write_man_pages();
476 }
477
478 exit($errors);