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