1eval '(exit $?0)' && 2 eval 'exec perl -S $0 ${1+"$@"}' && 3 eval 'exec perl -S $0 $argv:q' 4 if 0; 5 6#!/usr/bin/perl -W 7# 8# Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved. 9# Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved. 10# Copyright (c) 1996-2003 Intel Corporation. All rights reserved. 11# 12# This software is available to you under a choice of one of two 13# licenses. You may choose to be licensed under the terms of the GNU 14# General Public License (GPL) Version 2, available from the file 15# COPYING in the main directory of this source tree, or the 16# OpenIB.org BSD license below: 17# 18# Redistribution and use in source and binary forms, with or 19# without modification, are permitted provided that the following 20# conditions are met: 21# 22# - Redistributions of source code must retain the above 23# copyright notice, this list of conditions and the following 24# disclaimer. 25# 26# - Redistributions in binary form must reproduce the above 27# copyright notice, this list of conditions and the following 28# disclaimer in the documentation and/or other materials 29# provided with the distribution. 30# 31# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 34# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 35# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 36# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 37# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 38# SOFTWARE. 39# 40######################################################################### 41# 42# Abstract: 43# Perl script for simple source code error checking and fixing 44# 45# Environment: 46# Linux User Mode 47# 48# Author: 49# Eitan Zahavi, Mellanox Technologies LTD Yokneam Israel. 50# 51# $Revision: 1.4 $ 52# 53# 54# 55# DESCRIPTION: 56# 57# This script performs some simple conformance checks on the 58# OpenSM source code. It does NOT attempt to act like a full 59# blown 'C' language parser, so it can be fooled. Something 60# is better than nothing. 61# 62# The script starts by running the 'osm_indent' script on teh given files. 63# 64# We use an extra file for tracking error codes used by each file. 65# The name is osm_errors_codes. 66# 67# The following checks are performed: 68# 1) Verify that the function name provided in a log statement 69# matches the name of the current function. 70# 71# 2) Verify that log statements are in the form that this script 72# can readily parse. Improvements to the regular expressions 73# might make this unnecessary. 74# 75# 3) Verify that lower two digits of the error codes used in log 76# statements are unique within that file. 77# 78# 4) Verify that upper two digits of the error codes used in log 79# statements are not used by any other module. 80# 81# 5) Verify the lines do not have extra spaces. 82# 83# USAGE: 84# 85# In the OpenSM source directory, type: 86# osm_check_n_fix -f *.c 87# 88######################################################################### 89 90# Do necessary upfront initialization 91$verbose = 0; 92$in_c_comment = 0; 93$fix_mode = 0; 94$confirm_mode = 0; 95$re_assign_err_prefix = 0; 96 97if( !scalar(@ARGV) ) 98{ 99 print "ERROR: You must specify the files on which to operate, such as '*.c'\n"; 100 osm_check_usage(); 101 exit; 102} 103 104# loop through all the command line options 105do 106{ 107 $doing_params = 0; 108 109 # First, look for command line options. 110 if( $ARGV[0] =~ /-[v|V]/ ) 111 { 112 $verbose += 1; 113 shift; 114 print "Verbose mode on, level = $verbose.\n"; 115 $doing_params = 1; 116 } 117 118 if( $ARGV[0] =~ /(-f|--fix)/ ) 119 { 120 $fix_mode += 1; 121 shift; 122 print "Fix mode on.\n"; 123 $doing_params = 1; 124 } 125 126 if( $ARGV[0] =~ /(-c|--confirm)/ ) 127 { 128 $confirm_mode += 1; 129 shift; 130 print "Confirm mode on.\n"; 131 $doing_params = 1; 132 } 133 134 if( $ARGV[0] =~ /(-r|--re-assign-mod-err-prefix)/ ) 135 { 136 $re_assign_err_prefix += 1; 137 shift; 138 print "Allow Re-Assignment of Module Err Prefixes.\n"; 139 $doing_params = 1; 140 } 141 142 if( !scalar(@ARGV)) 143 { 144 print "ERROR: You must specify the files on which to operate, such as '*.c'\n"; 145 osm_check_usage(); 146 exit; 147 } 148} while( $doing_params == 1 ); 149 150# parse the osm_error_codes file and define: 151# module_by_prefix 152# module_err_prefixes 153# module_last_err_used 154if (open(ERRS, "<osm_error_codes")) { 155 @ERR_DEFS = <ERRS>; 156 close(ERRS); 157 foreach $errDef (@ERR_DEFS) { 158 # the format should be <file name> <err prefix> <last err> 159 if ($errDef =~ m/^(\S+)\s+(\S+)\s+([0-9]+)$/) { 160 ($file_name,$mod_prefix,$last_err) = ($1,$2,$3); 161 if (defined($module_by_prefix{$mod_prefix})) { 162 print "ERROR: Double module prefix:$mod_prefix on:$module_by_prefix($mod_prefix) and $file_name\n"; 163 exit 3; 164 } 165 $module_by_prefix{$mod_prefix} = $file_name; 166 $module_err_prefixes{$file_name} = $mod_prefix; 167 $module_last_err_used{$file_name} = $last_err; 168 } else { 169 print "ERROR: Fail to parse sm_error_codes: $errDef\n"; 170 exit 3; 171 } 172 } 173} 174 175# do a file by file read into memory so we can tweek it: 176foreach $file_name (@ARGV) { 177 print "- $file_name ----------------------------------------------------\n"; 178 # first step is to run indent 179 $res=`osm_indent $file_name`; 180 181 open(INFILE, "<$file_name") || die("Fail to open $file_name"); 182 @LINES = <INFILE>; 183 close(INFILE); 184 $any_fix = 0; 185 $needed_fixing = 0; 186 $need_indentation = 0; 187 188 LINE: for ($line_num = 0; $line_num <scalar(@LINES); $line_num++) { 189 $line = $LINES[$line_num]; 190 $_ = $line; 191 192 # Skip C single line C style comments 193 # This line must come before the multi-line C comment check! 194 if( /\/\*.*\*\// ) 195 { 196 $in_c_comment = 0; 197 next LINE; 198 } 199 200 # skip multi-line C style comments 201 if( /\/\*/ ) 202 { 203 $in_c_comment = 1; 204 next LINE; 205 } 206 207 # end skipping of multi-line C style comments 208 if( /\*\// ) 209 { 210 $in_c_comment = 0; 211 next LINE; 212 } 213 214 # We're still in a C comment, so ignore input 215 if( $in_c_comment == 1 ) 216 { 217 next LINE; 218 } 219 220 221 # Error on C++ style comment lines 222 if( /\/\// ) 223 { 224 print "C++ style comment on $file_name $line_num\n"; 225 $needed_fixing++; 226 if ($fix_mode) { 227 $line =~ s=\/\/(.*)$=/* \1 */=; 228 if (confirm_change($line, $LINES[$line_num])) { 229 $LINES[$line_num] = $line; 230 $any_fix++; 231 } 232 $any_fix++; 233 } 234 } 235 236 # check for lines with trailing spaces: 237 if (/[ \t]+$/) { 238 $needed_fixing++; 239 if ($fix_mode) { 240 $line =~ s/\s+$/\n/; 241 if (confirm_change($line, $LINES[$line_num])) { 242 $LINES[$line_num] = $line; 243 $any_fix++; 244 } 245 $any_fix++; 246 } 247 } 248 249 # check for bad PRIx64 usage 250 # It's a common mistake to forget the % before the PRIx64 251 if (/[^%0-9][0-9]*\"\s*PRIx64/ ) { 252 $needed_fixing++; 253 print "No % sign before PRx64!!: $file_name $line_num\n"; 254 if ($fix_mode) { 255 $line =~ s/([0-9]*)\"\s*PRIx64/%$1\" PRIx64/; 256 if (confirm_change($line, $LINES[$line_num])) { 257 $LINES[$line_num] = $line; 258 $any_fix++; 259 } 260 } 261 } 262 263 # This simple script doesn't handle checking PRIx64 usage 264 # when PRIx64 starts the line. Just give a warning. 265 if( /^\s*PRIx64/ ) 266 { 267 $needed_fixing++; 268 print "Warning: PRIx64 at start of line. $file_name $line_num\n"; 269# if ($fix_mode) { 270# print "Fatal: can not auto fix\n"; 271# exit 1; 272# } 273 } 274 275 # Attempt to locate function names. 276 # Function names must start on the beginning of the line. 277 if( /^(\w+)\s*\(/ ) 278 { 279 $current_func = $1; 280 if( $verbose == 1 ) 281 { 282 print "Processing $file_name: $current_func\n"; 283 } 284 } 285 286 # Attempt to find OSM_LOG_ENTER entries. 287 # When found, verify that the function name provided matches 288 # the actual function. 289 if( /OSM_LOG_ENTER\s*\(\s*([\-\.\>\w]+)\s*,\s*(\w+)\s*\)/ ) { 290 $log_func = $2; 291 if( $current_func ne $log_func ) { 292 printf "MISMATCH!! $file_name $line_num: $current_func != $log_func\n"; 293 $needed_fixing++; 294 if ($fix_mode) { 295 $line =~ 296 s/OSM_LOG_ENTER\s*\(\s*([\-\.\>\w]+)\s*,\s*(\w+)\s*\)/OSM_LOG_ENTER( $1, $current_func )/; 297 if (confirm_change($line, $LINES[$line_num])) { 298 $LINES[$line_num] = $line; 299 $any_fix++; 300 } 301 } 302 } 303 } 304 305 # Check for non-conforming log statements. 306 # Log statements must not start the log string on the same line 307 # as the osm_log function itself. 308 # Watch out for the #include "osm_log.h" statement as a false positive. 309 if (/osm_log\s*\(.*OSM_.*\"/ ) { 310 if (/Format Waved/) { 311 print "Skipping log format waiver at $file_name $line_num\n"; 312 } else { 313 print "NON-CONFORMING LOG STATEMENT!! $file_name $line_num\n"; 314 $needed_fixing++; 315 if ($fix_mode) { 316 print "Fatal: can not auto fix\n"; 317 exit 1; 318 } 319 } 320 } 321 322 # Attempt to find osm_log entries. 323 if( /^\s*\"(\w+):/ ) 324 { 325 $log_func = $1; 326 if( $current_func ne $log_func ) 327 { 328 print "MISMATCHED LOG FUNCTION!! $file_name $line_num: $current_func != $log_func\n"; 329 $needed_fixing++; 330 if ($fix_mode) { 331 $line =~ 332 s/^(\s*)\"(\w+):/$1\"$current_func:/; 333 if (confirm_change($line, $LINES[$line_num])) { 334 $LINES[$line_num] = $line; 335 $any_fix++; 336 } 337 } 338 } 339 } 340 341 # Error logging must look like 'ERR 1234:' 342 # The upper two digits are error range assigned to that module. 343 # The lower two digits are the error code itself. 344 # Error codes are in hexadecimal. 345 if( /ERR(\s+)([0-9a-fA-F]{2})([0-9a-fA-F]{2})(..)/ ) 346 { 347 # track any error for this exp: 348 $exp_err = 0; 349 350 # the parsed prefix and err code: 351 ($found_prefix,$found_code) = ($2,$3); 352 353 # Check if we already established the error prefix for this module 354 $err_prefix = $module_err_prefixes{$file_name}; 355 356 # err prefix is not available for this file 357 if ( ! $err_prefix ) { 358 # make sure no other file uses this prefix: 359 if ($module_by_prefix{$found_prefix}) { 360 # some other file uses that prefix: 361 362 # two modes: either use a new one or abort 363 if ($re_assign_err_prefix) { 364 # scan the available module prefixes for an empty one: 365 $found = 0; 366 for ($new_prefix_idx = 1; $found == 0; $new_prefix_idx++) { 367 $prefix = sprintf("%02X", $new_prefix_idx); 368 if (!defined($module_by_prefix{$prefix})) { 369 $module_err_prefixes{$file_name} = $prefix; 370 $module_by_prefix{$prefix} = $file_name; 371 $found = 1; 372 } 373 $exp_err = 1; 374 } 375 } else { 376 print "Fatal: File $module_by_prefix{$2} already uses same prefix:$2 used by: $file_name (line=$line_num)\n"; 377 exit 1; 378 } 379 } else { 380 # the prefix found is unused: 381 382 # Create a new prefix for this module. 383 $module_err_prefixes{$file_name} = $found_prefix; 384 $module_by_prefix{$found_prefix} = $file_name; 385 $err_prefix = $found_prefix; 386 } 387 } else { 388 # we already have a prefix for this file 389 390 if( $err_prefix ne $found_prefix ) 391 { 392 $needed_fixing++; 393 print "BAD ERR RANGE IN LOG ENTRY!! $file_name $line_num: $current_func\n"; 394 print "\tExpected $err_prefix but found $found_prefix\n"; 395 $exp_err = 1; 396 } 397 } 398 399 # now check for code duplicates 400 $err_base = $module_err_bases{$found_code}; 401 if( $err_base ) { 402 $needed_fixing++; 403 print "DUPLICATE ERR NUMBER IN LOG ENTRY!! $file_name $line_num: $current_func: $3\n"; 404 print "\tPrevious use on line $err_base.\n"; 405 406 # use the last error code for this module: 407 $module_last_err_used{$file_name}++; 408 $err_code = sprintf("%02X", $module_last_err_used{$file_name}); 409 print "\tUsing new err code:0x$err_code ($module_last_err_used{$file_name})\n"; 410 $module_err_bases{$err_code} = $line_num; 411 $exp_err = 1; 412 } else { 413 # Add this error code to the list used by this module 414 # The data stored in the line number on which it is used. 415 $module_err_bases{$found_code} = $line_num; 416 # track the last code used 417 $err_code_num = eval("0x$found_code"); 418 if ($module_last_err_used{$file_name} < $err_code_num) { 419 $module_last_err_used{$file_name} = $err_code_num; 420 } 421 $err_code = $found_code; 422 423 if( $verbose > 1 ) { 424 print "Adding new error: $err_prefix$found_code in $file_name.\n"; 425 } 426 } 427 428 if( $4 ne ": " ) { 429 $needed_fixing++; 430 print "MALFORMED LOG STATEMENT!! NEEDS ': ' $file_name $line_num\n"; 431 $exp_err = 1; 432 } 433 434 if( $1 ne " " ) 435 { 436 $needed_fixing++; 437 print "USE ONLY 1 SPACE AFTER ERR!! $file_name $line_num\n"; 438 $exp_err = 1; 439 } 440 441 if ($exp_err && $fix_mode) { 442 $line =~ 443 s/ERR(\s+)([0-9a-fA-F]{2})([0-9a-fA-F]{2})([^\"]*\")/ERR ${err_prefix}$err_code: \" /; 444 if (confirm_change($line, $LINES[$line_num])) { 445 $LINES[$line_num] = $line; 446 $any_fix++; 447 } 448 } 449 } 450 451 # verify expected use of sizeof() with pointers 452 if( /sizeof\s*\(\s*[h|p]_[^-]+\)/ ) 453 { 454 print "SUSPICIOUS USE OF SIZEOF(), DO YOU NEED AN '*' $file_name $line_num\n"; 455 $needed_fixing++; 456 if ($fix_mode) { 457 $line =~ 458 s/sizeof\s*\(\s*([h|p])_/sizeof \(*$1_/; 459 if (confirm_change($line, $LINES[$line_num])) { 460 $LINES[$line_num] = $line; 461 $any_fix++; 462 } 463 } 464 } 465 } 466 467 # reset the base error value, since each module can 468 # repeat this range. 469 %module_err_bases = (); 470 471 # if any fix write out the fixed file: 472 if ($any_fix) { 473 open(OF,">$file_name.fix"); 474 print OF @LINES; 475 close(OF); 476 } elsif ($needed_fixing) { 477 print "Found $needed_fixing Errors on file: $file_name\n"; 478 } 479} 480 481# write out the error codes. 482# module_by_prefix 483# module_err_prefixes 484# module_last_err_used 485open(ERRS,">osm_error_codes"); 486foreach $fn (sort(keys(%module_err_prefixes))) { 487 print ERRS "$fn $module_err_prefixes{$fn} $module_last_err_used{$fn}\n"; 488} 489close(ERRS); 490 491sub osm_check_usage 492{ 493 print "Usage:\n"; 494 print "osm_check.pl [-v|V] [-f|--fix] [-c|--confirm] [-r|--re-assign-mod-err-prefix] <file list>\n"; 495 print "[-v|V] - enable verbose mode.\n"; 496 print "[-f|--fix] - enable auto fix mode.\n"; 497 print "[-c|--confirm] - enable manual confirmation mode.\n"; 498 print "[-r|--re-assign-mod-err-prefix] - enables re-assign error prefixes if the file does not have one.\n"; 499} 500 501sub confirm_change { 502 local ($line, $orig_line) = @_; 503 if ($confirm_mode) { 504 print "In Line:".($line_num + 1)."\n"; 505 print "From: ${orig_line}To: ${line}Ok [y] ?"; 506 $| = 1; 507 $ans = <STDIN>; 508 chomp $ans; 509 510 if ($ans && $ans ne "y") { 511 return 0; 512 } 513 } else { 514 print "From: ${orig_line}To: ${line}"; 515 } 516 return 1; 517} 518