2019-08-14 06:34:26 +02:00
# OpenSTA, Static Timing Analyzer
2019-08-10 03:44:31 +02:00
# Copyright (c) 2019, Parallax Software, Inc.
#
2019-08-14 06:34:26 +02:00
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
2019-08-10 03:44:31 +02:00
proc regression_main { } {
setup
parse_args
run_tests
show_summary
exit [ found_errors ]
}
proc setup { } {
global result_dir diff_file failure_file errors
global use_valgrind valgrind_shared_lib_failure
set use_valgrind 0
if { ! ( [ file exists $result_dir ] && [ file isdirectory $result_dir ] ) } {
file mkdir $result_dir
}
file delete $diff_file
file delete $failure_file
set errors( error ) 0
set errors( memory ) 0
set errors( leak ) 0
set errors( race ) 0
set errors( slow ) 0
set errors( fast ) 0
set errors( big ) 0
set errors( small ) 0
set errors( fail ) 0
set errors( no_cmd ) 0
set errors( no_ok ) 0
set valgrind_shared_lib_failure 0
}
proc parse_args { } {
global argv app_options tests test_groups cmd_paths
global use_valgrind
global result_dir tests
while { $argv != { } } {
set arg [ lindex $argv 0 ]
if { $arg == " h e l p " || $arg == " - h e l p " } {
puts { Usage : regression [ -help ] [ -threads threads] [ -valgrind ] tests...}
puts " - t h r e a d s m a x | i n t e g e r - n u m b e r o f t h r e a d s t o u s e "
puts " - v a l g r i n d - r u n v a l g r i n d ( l i n u x m e m o r y c h e c k e r ) "
puts " W i l d c a r d i n g f o r t e s t n a m e s i s s u p p o r t e d ( e n c l o s e i n \" ' s ) "
puts " T e s t s a r e : a l l , f a s t , m e d , s l o w , o r a t e s t g r o u p o r t e s t n a m e "
puts " "
puts " I f ' l i m i t c o r e d u m p s i z e u n l i m i t e d ' c o r e f i l e s a r e s a v e d i n $ r e s u l t _ d i r / t e s t . c o r e "
exit
} elseif { $arg == " - t h r e a d s " } {
set threads [ lindex $argv 1 ]
if { ! ( [ string is integer $threads ] || $threads == " m a x " ) } {
puts " E r r o r : - t h r e a d s a r g $ t h r e a d s i s n o t a n i n t e g e r o r m a x . "
exit 0
}
lappend app_options " - t h r e a d s "
lappend app_options $threads
set argv [ lrange $argv 2 end]
} elseif { $arg == " - v a l g r i n d " } {
set use_valgrind 1
set argv [ lrange $argv 1 end]
} elseif { [ llength $argv ] == 0 } {
# Default is to run dist tests.
set tests [ group_tests dist]
} else {
break
}
}
set tests [ expand_tests $argv ]
}
proc expand_tests { argv } {
global test_groups
set tests { }
foreach arg $argv {
if { [ info exists test_groups( $arg ) ] } {
set tests [ concat $tests $test_groups ( $arg ) ]
} elseif { [ string first " * " $arg ] != -1 \
|| [ string first " ? " $arg ] != -1 } {
# Find wildcard matches.
foreach test [ group_tests " a l l " ] {
if [ string match $arg $test ] {
lappend tests $test
}
}
} elseif { [ lsearch [ group_tests " a l l " ] $arg ] != -1 } {
lappend tests $arg
} else {
puts " E r r o r : t e s t $ a r g n o t f o u n d . "
}
}
return $tests
}
proc run_tests { } {
global tests errors app_path
foreach test $tests {
run_test $test
}
# Macos debug info generated by valgrind.
file delete - force " $ a p p _ p a t h . d S Y M "
}
proc run_test { test } {
global result_dir diff_file errors diff_options
set cmd_file [ test_cmd_file $test ]
if [ file exists $cmd_file ] {
set ok_file [ test_ok_file $test ]
set log_file [ test_log_file $test ]
foreach file [ glob - nocomplain [ file join $result_dir $test. * ] ] {
file delete - force $file
}
puts - nonewline $test
flush stdout
set test_errors [ run_test_app $test $cmd_file $log_file ]
if { [ lindex $test_errors 0 ] == " E R R O R " } {
puts " * E R R O R * [ l r a n g e $ t e s t _ e r r o r s 1 e n d ] "
append_failure $test
incr errors( error )
# For some reason seg faults aren't echoed in the log - add them.
if [ file exists $log_file ] {
set log_ch [ open $log_file " a " ]
puts $log_ch " $ t e s t _ e r r o r s "
close $log_ch
}
# Report partial log diff anyway.
if [ file exists $ok_file ] {
catch [ concat exec diff $diff_options $ok_file $log_file \
>> $diff_file ]
}
} else {
set error_msg " "
if { [ lsearch $test_errors " M E M O R Y " ] != -1 } {
append error_msg " * M E M O R Y * "
append_failure $test
incr errors( memory )
}
if { [ lsearch $test_errors " L E A K " ] != -1 } {
append error_msg " * L E A K * "
append_failure $test
incr errors( leak )
}
if { [ lsearch $test_errors " R A C E " ] != -1 } {
append error_msg " * R A C E * "
append_failure $test
incr errors( race )
}
if { [ lsearch $test_errors " S L O W " ] != -1 } {
append error_msg " * S L O W * "
incr errors( slow )
}
if { [ lsearch $test_errors " F A S T " ] != -1 } {
append error_msg " * F A S T * "
incr errors( fast )
}
if { [ lsearch $test_errors " B I G " ] != -1 } {
append error_msg " * B I G * "
incr errors( big )
}
if { [ lsearch $test_errors " S M A L L " ] != -1 } {
append error_msg " * S M A L L * "
incr errors( small )
}
if [ file exists $ok_file ] {
# Filter dos '/r's from log file.
set tmp_file [ file join $result_dir $test.tmp ]
exec tr - d " \r " < $log_file > $tmp_file
file rename - force $tmp_file $log_file
if [ catch [ concat exec diff $diff_options $ok_file $log_file \
>> $diff_file ] ] {
puts " * F A I L * $ e r r o r _ m s g "
append_failure $test
incr errors( fail )
} else {
puts " p a s s $ e r r o r _ m s g "
}
} else {
puts " * N O O K F I L E * $ e r r o r _ m s g "
append_failure $test
incr errors( no_ok )
}
}
} else {
puts " $ t e s t * N O C M D F I L E * "
incr errors( no_cmd )
}
}
proc append_failure { test } {
global failure_file
set fail_ch [ open $failure_file " a " ]
puts $fail_ch $test
close $fail_ch
}
# Return error.
proc run_test_app { test cmd_file log_file } {
global app_path errorCode use_valgrind
if { $use_valgrind } {
return [ run_test_valgrind $test $cmd_file $log_file ]
} else {
return [ run_test_plain $test $cmd_file $log_file ]
}
}
proc run_test_plain { test cmd_file log_file } {
global app_path app_options result_dir errorCode
if { ! [ file exists $app_path ] } {
return " E R R O R $ a p p _ p a t h n o t f o u n d . "
} elseif { ! [ file executable $app_path ] } {
return " E R R O R $ a p p _ p a t h i s n o t e x e c u t a b l e . "
} else {
set save_dir [ pwd ]
cd [ file dirname $cmd_file ]
if { [ catch [ concat exec $app_path $app_options \
[ file tail $cmd_file ] > & $log_file ] ] } {
cd $save_dir
set signal [ lindex $errorCode 2 ]
set error [ lindex $errorCode 3 ]
# Errors strings are not consistent across platforms but signal
# names are.
if { $signal == " S I G S E G V " } {
# Save corefiles to regression results directory.
set pid [ lindex $errorCode 1 ]
set sys_corefile [ test_sys_core_file $test $pid ]
if { [ file exists $sys_corefile ] } {
file copy $sys_corefile [ test_core_file $test ]
}
}
cleanse_logfile $test $log_file
return " E R R O R $ e r r o r "
}
cd $save_dir
cleanse_logfile $test $log_file
return " "
}
}
proc run_test_valgrind { test cmd_file log_file } {
global app_path app_options valgrind_options result_dir errorCode
set vg_cmd_file [ test_valgrind_cmd_file $test ]
set vg_stream [ open $vg_cmd_file " w " ]
puts $vg_stream " c d [ f i l e d i r n a m e $ c m d _ f i l e ] "
puts $vg_stream " s o u r c e [ f i l e t a i l $ c m d _ f i l e ] "
puts $vg_stream " s t a : : d e l e t e _ a l l _ m e m o r y "
close $vg_stream
set cmd [ concat exec valgrind $valgrind_options \
$app_path $app_options $vg_cmd_file > & $log_file ]
if { [ catch $cmd ] } {
set error [ lindex $errorCode 3 ]
cleanse_valgrind_logfile $test $log_file
cleanse_logfile $test $log_file
return " E R R O R $ e r r o r "
} else {
cleanse_logfile $test $log_file
return [ cleanse_valgrind_logfile $test $log_file ]
}
}
# Error messages can be found in "valgrind/memcheck/mc_errcontext.c".
#
# "Conditional jump or move depends on uninitialised value(s)"
# "%s contains unaddressable byte(s)"
# "%s contains uninitialised or unaddressable byte(s)"
# "Use of uninitialised value of size %d"
# "Invalid read of size %d"
# "Syscall param %s contains uninitialised or unaddressable byte(s)"
# "Unaddressable byte(s) found during client check request"
# "Uninitialised or unaddressable byte(s) found during client check request"
# "Invalid free() / delete / delete[]"
# "Mismatched free() / delete / delete []"
set valgrind_mem_regexp " ( d e p e n d s o n u n i n i t i a l i s e d v a l u e ) | ( c o n t a i n s u n a d d r e s s a b l e ) | ( c o n t a i n s u n i n i t i a l i s e d ) | ( U s e o f u n i n i t i a l i s e d v a l u e ) | ( I n v a l i d r e a d ) | ( U n a d d r e s s a b l e b y t e ) | ( U n i n i t i a l i s e d o r u n a d d r e s s a b l e ) | ( I n v a l i d f r e e ) | ( M i s m a t c h e d f r e e ) "
# "%d bytes in %d blocks are definitely lost in loss record %d of %d"
# "%d bytes in %d blocks are possibly lost in loss record %d of %d"
# set valgrind_leak_regexp "blocks are (possibly|definitely) lost"
set valgrind_leak_regexp " b l o c k s a r e d e f i n i t e l y l o s t "
# Valgrind fails on executables using shared libraries.
set valgrind_shared_lib_failure_regexp " N o m a l l o c ' d b l o c k s - - n o l e a k s a r e p o s s i b l e "
# Scan the log file to separate valgrind notifications and check for
# valgrind errors.
proc cleanse_valgrind_logfile { test log_file } {
global valgrind_mem_regexp valgrind_leak_regexp
global valgrind_shared_lib_failure_regexp
global valgrind_shared_lib_failure
set tmp_file [ test_tmp_file $test ]
set valgrind_log_file [ test_valgrind_file $test ]
file copy - force $log_file $tmp_file
set tmp [ open $tmp_file " r " ]
set log [ open $log_file " w " ]
set valgrind [ open $valgrind_log_file " w " ]
set leaks 0
set mem_errors 0
gets $tmp line
while { ! [ eof $tmp ] } {
if { [ regexp " ^ = = " $line ] } {
puts $valgrind $line
if { [ regexp $valgrind_leak_regexp $line ] } {
set leaks 1
}
if { [ regexp $valgrind_mem_regexp $line ] } {
set mem_errors 1
}
if { [ regexp $valgrind_shared_lib_failure_regexp $line ] } {
set valgrind_shared_lib_failure 1
}
} elseif { [ regexp { ^ -- [ 0-9 ] + } $line ] } {
# Valgrind notification line.
} else {
puts $log $line
}
gets $tmp line
}
close $log
close $tmp
close $valgrind
file delete $tmp_file
set errors { }
if { $mem_errors } {
lappend errors " M E M O R Y "
}
if { $leaks } {
lappend errors " L E A K "
}
return $errors
}
# ###############################################################
proc show_summary { } {
global errors tests diff_file result_dir valgrind_shared_lib_failure
global app_path app
puts " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "
set test_count [ llength $tests ]
if { [ found_errors ] } {
if { $errors ( error ) != 0 } {
puts " E r r o r e d $ e r r o r s ( e r r o r ) / $ t e s t _ c o u n t "
}
if { $errors ( fail ) != 0 } {
puts " F a i l e d $ e r r o r s ( f a i l ) / $ t e s t _ c o u n t "
}
if { $errors ( leak ) != 0 } {
puts " M e m o r y l e a k s i n $ e r r o r s ( l e a k ) / $ t e s t _ c o u n t "
}
if { $errors ( memory ) != 0 } {
puts " M e m o r y c o r r u p t i o n i n $ e r r o r s ( m e m o r y ) / $ t e s t _ c o u n t "
}
if { $errors ( race ) != 0 } {
puts " R a c e e r r o r s i n $ e r r o r s ( r a c e ) / $ t e s t _ c o u n t "
}
if { $errors ( no_ok ) != 0 } {
puts " N o o k f i l e f o r $ e r r o r s ( n o _ o k ) / $ t e s t _ c o u n t "
}
if { $errors ( no_cmd ) != 0 } {
puts " N o c m d t c l f i l e f o r $ e r r o r s ( n o _ c m d ) / $ t e s t _ c o u n t "
}
if { $errors ( fail ) != 0 } {
puts " S e e $ d i f f _ f i l e f o r d i f f e r e n c e s "
}
} else {
puts " P a s s e d $ t e s t _ c o u n t "
}
if { $valgrind_shared_lib_failure } {
puts " W A R N I N G : v a l g r i n d f a i l e d b e c a u s e t h e e x e c u t a b l e i s n o t s t a t i c a l l y l i n k e d . "
}
puts " S e e $ r e s u l t _ d i r f o r l o g f i l e s "
}
proc found_errors { } {
global errors
return [ expr $errors ( error ) != 0 || $errors ( fail ) != 0 \
|| $errors ( no_cmd ) != 0 || $errors ( no_ok ) != 0 \
|| $errors ( memory ) != 0 || $errors ( leak ) != 0 \
|| $errors ( race ) != 0 ]
}
# ###############################################################
proc save_ok_main { } {
global argv
if { $argv == " h e l p " || $argv == " - h e l p " } {
puts { Usage : save_ok [ failures ] test1 [ test2 ] ...}
} elseif { $argv == " f a i l u r e s " } {
global failure_file
if [ file exists $failure_file ] {
set fail_ch [ open $failure_file " r " ]
while { ! [ eof $fail_ch ] } {
set test [ gets $fail_ch ]
if { $test != " " } {
save_ok $test
}
}
close $fail_ch
}
} else {
foreach test $argv {
save_ok $test
}
}
}
proc save_ok { test } {
if { [ lsearch [ group_tests " a l l " ] $test ] == -1 } {
puts " E r r o r : t e s t $ t e s t n o t f o u n d . "
} else {
set ok_file [ test_ok_file $test ]
set log_file [ test_log_file $test ]
if { ! [ file exists $log_file ] } {
puts " E r r o r : l o g f i l e $ l o g _ f i l e n o t f o u n d . "
} else {
file copy - force $log_file $ok_file
}
}
}
# ###############################################################
proc test_cmd_dir { test } {
global cmd_dirs
if { [ info exists cmd_dirs( $test ) ] } {
return $cmd_dirs ( $test )
} else {
return " "
}
}
proc test_cmd_file { test } {
return [ file join [ test_cmd_dir $test ] " $ t e s t . t c l " ]
}
proc test_ok_file { test } {
global test_dir
return [ file join $test_dir " $ t e s t . o k " ]
}
proc test_log_file { test } {
global result_dir
return [ file join $result_dir " $ t e s t . l o g " ]
}
proc test_tmp_file { test } {
global result_dir
return [ file join $result_dir $test.tmp ]
}
proc test_valgrind_cmd_file { test } {
global result_dir
return [ file join $result_dir $test.vg_cmd ]
}
proc test_valgrind_file { test } {
global result_dir
return [ file join $result_dir $test.valgrind ]
}
proc test_core_file { test } {
global result_dir
return [ file join $result_dir $test.core ]
}
proc test_sys_core_file { test pid } {
global cmd_dirs
# macos
# return [file join "/cores" "core.$pid"]
# Suse
return [ file join [ test_cmd_dir $test ] " c o r e " ]
}
# ###############################################################
# Local Variables:
# mode:tcl
# End: