#!/usr/local/bin/perl 'di'; 'ig00'; ###################################################################### # # evalpsp1 - evaluate psp timestamp file # $debug = 0; sub debug { print "### $.: @_\n" if $debug; } sub is_leap_year { my $year = shift; ! ( $year % 4 ) && $year % 100 || ! ( $year % 400 ); } sub leap_days { my $start_year = shift; my $end_year = shift; my $leap_days = 0; while( $start_year < $end_year ) { $leap_days++ if( is_leap_year( $start_year ) ); $start_year++; } $leap_days; } sub days_until_month { my @month_names = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ); my %days_in_month = ( "Jan", 31, "Feb", 28, "Mar", 31, "Apr", 30, "May", 31, "Jun", 30, "Jul", 31, "Aug", 31, "Sep", 30, "Oct", 31, "Nov", 30, "Dec", 31 ); my $month = shift; my $year = shift; my $until = 0; my $loop = 0; while( $month_names[ $loop ] ne $month && $loop < 12 ) { $until += $days_in_month{ $month_names[ $loop ] }; $until++ if( $loop == 1 && is_leap_year( $year ) ); $loop++; } $until; } sub error_level { my $error_level = shift; if( $error_level + $option_level >= 2 ) { $FILE = $debug ? STDOUT : STDERR; print $FILE "@_\n"; } if( $error_level + $option_level >= 4 ) { $check_only or $data_only or print "####################### erroneous data #######################\n"; $check_only or exit 1; } } sub error_level_file { my $error_level = shift; if( $error_level + $option_level >= 2 ) { $FILE = $debug ? STDOUT : STDERR; print $FILE "$infilename:$.: @_\n$_"; } if( $error_level + $option_level >= 4 ) { $check_only or $data_only or print "####################### erroneous data #######################\n"; $check_only or exit 1; } } sub minutes { my( $seconds ) = @_; my $minutes = ( ( $seconds + 30 ) - ( ( $seconds + 30 ) % 60 ) ) / 60 ; $minutes ? $minutes : $seconds ? 1 : 0; } sub interrupt_split { my( @rest ) = @_; ( $interrupt ) = @rest; } sub interrupt_start { my( $start, @rest ) = @_; # start time, rest of input line if( ! $interrupt ) { $interrupt = $phase_name ? "unnamed" : "unknown"; } $depth++; debug "start of interrupt", $interrupt, "depth", $depth, "phased", $phase_name; $interrupt_rest = "@rest"; $start_time[ $depth ] = $start; $event_flags[ 1 ] = 1; } sub interrupt_end { my( @rest ) = @_; # rest of input line if( ! scalar @rest ) { @rest = split /\s+/, $interrupt_rest; interrupt_split( @rest ); } if( ! $interrupt ) { $interrupt = $phase_name ? "unnamed" : "unknown"; } enter_interrupt( $interrupt ); my $duration = $normal - $start_time[ $depth ]; $duration += 86400 if $duration < 0; # working late, eh? $interrupt_duration{ $interrupt } += $duration; $interrupt_count{ $interrupt }++; $int_duration[ $depth ] += $duration; $event_flags[ 1 ] = 0; debug "end of interrupt", $interrupt, "dur", minutes( $duration ), "cum", minutes( $int_duration[ $depth ] ), "depth", $depth; $depth--; } sub defect_split { my( @rest ) = @_; my $loop; for( $loop = 0; $loop < $order_tags; $loop++ ) { $class = shift @rest if $order[ $loop ] =~ /c/; $inject = shift @rest if $order[ $loop ] =~ /i/; $reason = shift @rest if $order[ $loop ] =~ /r/; $fix_defect = shift @rest if $order[ $loop ] =~ /f/; } $comment = "@rest"; } sub defect_start { my( $start, @rest ) = @_; # start time, rest of input line $defect_count++; $depth++; debug "start of defect", $defect_count, "depth", $depth; $defect_number[ $depth ] = $defect_count; $defect_rest[ $depth ] = "@rest"; $start_time[ $depth ] = $start; $event_flags[ 2 ] = 1; } sub defect_end { my( @rest ) = @_; # rest of input line my $loop; my $s1, $s2, $s3, $t; my $duration = $normal - $start_time[ $depth ]; $duration += 86400 if $duration < 0; # working late, eh? $duration -= $int_duration[ $depth + 1 ] + $defect_duration[ $depth ]; if( ! $header ) { if( ! $check_only && ! $data_only && ! $stats_only ) { print "\n\n\n############ Defects:\n"; $s1 = "#no. "; $s2 = "# dur."; $s3 = "#-------"; for( $loop = 0; $loop < $order_tags; $loop++ ) { if( $order[ $loop ] =~ /c/ ) { $s1 .= "class "; $s2 .= " "; $s3 .= "----"; } if( $order[ $loop ] =~ /i/ ) { $s1 .= " inj."; $s2 .= " "; $s3 .= "---"; ( $s1, $s2 ) = ( $s2, $s1 ); $s1 .= " rem."; $s2 .= " "; $s3 .= "---"; } if( $order[ $loop ] =~ /r/ ) { $s1 .= "reas."; $s2 .= " "; $s3 .= "---"; } if( $order[ $loop ] =~ /f/ ) { $s1 .= " fix "; $s2 .= " "; $s3 .= "----"; } ( $s1, $s2 ) = ( $s2, $s1 ); } $s1 .= " comment"; $s2 .= ""; $s3 .= "--------"; ( $s1, $s2 ) = ( $s2, $s1 ) unless $s1 =~ /^#no/; print "$s1\n$s2\n$s3\n"; } $data_only or print "############ $infilename\n"; $header = 1; } if( ! scalar @rest ) { @rest = split /\s+/, $defect_rest[ $depth ]; defect_split( @rest ); } if( ! $check_only && ! $stats_only ) { printf( "%3d %2d ", $defect_number[ $depth ], minutes( $duration ) ); for( $loop = 0; $loop < $order_tags; $loop++ ) { printf( "%3s ", $class ) if $order[ $loop ] =~ /c/; printf( "%2s %2s ", $inject, $last_phase ) if $order[ $loop ] =~ /i/; printf( "%2s ", $reason ) if $order[ $loop ] =~ /r/; printf( "%3d ", $fix_defect ) if $order[ $loop ] =~ /f/; } print " $comment\n"; } $int_duration[ $depth ] += $int_duration[ $depth + 1 ]; $int_duration[ $depth + 1 ] = 0; debug "end of defect", $defect_number[ $depth ], "depth", $depth; $depth--; $event_flags[ 2 ] = 0 if $depth == 1; $defect_duration[ $depth ] += $duration + $defect_duration[ $depth + 1 ]; $defect_duration[ $depth + 1 ] = 0; if( ! $stats_only ) { $defect_rest[ $depth + 1 ] = 0; $defect_class_count{ $class }++; $defect_reason_count{ $reason }++ if $extract_reasons; $defect_class_duration{ $class } += $duration; $defect_reason_duration{ $reason } += $duration; enter_phase( $inject ); $defect_inject_count{ $inject }++; $defect_inject_duration{ $inject } += $duration; $class_inject_count{ $inject }{ $class }++; $class_inject_duration{ $inject }{ $class } += $duration; $defect_remove_count{ "*" }++; # in case remove phase unknown, just save under "*" $defect_remove_duration{ "*" } += $duration; $class_remove_count{ "*" }{ $class }++; $class_remove_duration{ "*" }{ $class } += $duration; $defect_phase_count{ $inject }{ "*" }++; $defect_phase_duration{ $inject }{ "*" } += $duration; } } sub phase_start { my( $start, $phase, $flags ) = @_; enter_phase( $phase ) if $flags != 2; $depth++; debug "start of phase", $phase, "depth", $depth; $start_time[ $depth ] = $start; $last_phase = $phase; $event_flags[ 3 ] = $flags ; } sub phase_end { my( $phase ) = @_; enter_phase( $phase ); my $duration = $normal - $start_time[ $depth ]; $duration += 86400 if $duration < 0; # working late, eh? $phase_duration{ $phase } += $duration - $int_duration[ $depth + 1 ]; $phase_intervals{ $phase }++; $phase_first{ $phase } = $sum_time if $phase_first{ $phase } < 0; $sum_time += $duration - $int_duration[ $depth + 1 ]; $phase_last{ $phase } = $sum_time ; debug "end of phase", $phase, "dur", minutes( $duration ), "count", $phase_intervals{ $phase }, "cum", minutes( $phase_duration{ $phase } ), "int", minutes( $int_duration[ $depth + 1 ] ), "depth", $depth; $int_duration[ $depth + 1 ] = 0; $event_flags[ 3 ] = 0; $depth--; if( ! $stats_only ) { if( $defect_remove_count{ "*" } ) { $defect_remove_count{ $phase } += $defect_remove_count{ "*" }; $defect_remove_count{ "*" } = 0; # phase name should be known now, so collect data } if( $defect_remove_duration{ "*" } ) { $defect_remove_duration{ $phase } += $defect_remove_duration{ "*" }; $defect_remove_duration{ "*" } = 0; } my( $item ); foreach $item ( keys %defect_class_count ) { if( $class_remove_count{ "*" }{ $item } ) { $class_remove_count{ $phase }{ $item } += $class_remove_count{ "*" }{ $item }; $class_remove_count{ "*" }{ $item } = 0; } if( $class_remove_duration{ "*" }{ $item } ) { $class_remove_duration{ $phase }{ $item } += $class_remove_duration{ "*" }{ $item }; $class_remove_duration{ "*" }{ $item } = 0; } } foreach $item ( keys %phase_number ) { if( $defect_phase_count{ $item } ) { $defect_phase_count{ $item }{ $phase } += $defect_phase_count{ $item }{ "*" }; $defect_phase_count{ $item }{ "*" } = 0; } if( $defect_phase_duration{ $item } ) { $defect_phase_duration{ $item }{ $phase } += $defect_phase_duration{ $item }{ "*" }; $defect_phase_duration{ $item }{ "*" } = 0; } } } } sub enter_interrupt { my( $interrupt ) = @_; if( ! $interrupt_number{ $interrupt } ) { $interrupt_count++; $interrupt_number{ $interrupt } = $interrupt_count; $interrupt_name{ $interrupt_count } = $interrupt; } } sub enter_phase { my( $phase ) = @_; if( $phase ) { if( $phase_number{ $phase } ) { $phase_count = $phase_number{ $phase }; } else { $phase_count++; $phase_number{ $phase } = $phase_count; } if( ! $phase_first{ $phase } ) { $phase_first{ $phase } = -1; } $phase_name{ $phase_count } = $phase; debug "phase *", $phase, "*", scalar( keys %phase_number ), "*", %number_number, "*"; } } sub emulate_phase { if( ! defined $event_flags[ 3 ] ) { # before any phase error_level_file( 0, "Interpolating begin of unnamed phase" ); phase_start( $normal, "?", 2 ); # emulating phase } elsif( ! $event_flags[ 3 ] ) { # not in phase error_level_file( 0, "Interpolating begin of unnamed phase" ); phase_start( $normal, "?", 2 ); # emulating phase } } sub make_filename { my( $infilename ) = @_; if( -d $infilename ) { $infilename = "$infilename/" unless $infilename =~ /\/$/; $infilename = "${infilename}$default_filename"; } return $infilename; } sub process { my $opened; my( @tags, $tag ); my( $month, $day, $time, $year, $event, @rest ); my( $hour, $minute, $second ); my( $phase_tag ); # my( $phase_tag, $phase_name ); my( $infilename ) = @_; $opened = open INFILE, "<$infilename" or error_level( 1, "Can't open input file $infilename" ); ! $header or ! $opened or $data_only or print "############ $infilename\n"; while( ) { if( /^\Q$comment_tag!\E/ ) { # change default tag chars @tags = split; while( $tag = shift @tags ) { if( $tag =~ /^-(.*)/ ) { eval_options( $1 ); debug "options: ", print_options(); } else { eval_tags( $tag ); debug "tags: ", print_tags(); } } next; } next if /^$/ || /^[\Q$comment_tag\E \t]/; # skip empty lines and comments # here is where the line is being split into its fields. changes are necessary only here! ( $month, $day, $time, $year, $event, @rest ) = split; ( $hour, $minute, $second ) = split /:/, $time; if( length $event == 1 ) { $phase_tag = $event; $phase_name = shift @rest; if( $phase_name =~ /\./ ) { ( $phase_name ) = $phase_name =~ /\.([^.]*)/; } } elsif( $event eq "end" ) { $phase_tag = $phase_end_tag; $phase_name = $last_phase; } else { if( $exchange_positions ) { ( $phase_name, $phase_tag ) = $event =~ /(.*)(.)/; } else { ( $phase_tag, $phase_name ) = $event =~ /(.)(.*)/; } } defect_split( @rest ); # look there, too! interrupt_split( @rest ); # look there, too! # end of line splitting. $last_event = $normal; # $normal = 3600 * $hour + 60 * $minute + $second; # normalized time $normal = ( $year - 1995 ) * 365 + leap_days( 1995, $year ) + days_until_month( $month, $year ); $normal = ( ( ( $normal + $day ) * 24 + $hour ) * 60 + $minute ) * 60 + $second; if( $phase_tag =~ /\Q$phase_start_tag\E/ ) { # start of phase if( ! $phase_name || $phase_name =~ /^\Q$interrupt_tag\E/ ) { # start of interrupt phase emulate_phase(); if( $event_flags[ 1 ] ) { # missing end of last interrupt? error_level_file( 0, "Interpolating end of interrupt" ); interrupt_end( @rest ); } interrupt_start( $normal, @rest ); } elsif( $phase_name =~ /^\Q$defect_tag\E/ ) { # start of defect phase emulate_phase(); if( $event_flags[ 1 ] ) { error_level_file( 0, "Interpolating end of interrupt" ); interrupt_end( @rest ); } defect_start( $normal, @rest ); } else { # start of normal psp phase if( $event_flags[ 1 ] ) { error_level_file( 0, "Interpolating end of interrupt" ); interrupt_end( @rest ); } while( $depth > 1 ) { error_level_file( 0, "Interpolating end of defect" ); defect_end( @rest ); } if( $event_flags[ 3 ] == 1 ) { error_level_file( 0, "Interpolating end of phase $last_phase" ); phase_end( $last_phase ); } elsif( $event_flags[ 3 ] == 2 ) { error_level_file( 1, "End of unnamed phase detected" ); phase_end( "?" ); } phase_start( $normal, $phase_name, 1 ); } } elsif( $phase_tag =~ /\Q$phase_end_tag\E/ ) { # end of phase if( ! $phase_name || $phase_name =~ /^\Q$interrupt_tag\E/ ) { # end of interrupt phase if( $event_flags[ 1 ] ) { # normal end of interrupt interrupt_end( @rest ); } elsif( defined $event_flags[ 3 ] ) { error_level_file( 1, "Interpolating begin for unmatched end of interrupt" ); emulate_phase(); interrupt_start( $normal, @rest ); interrupt_end( @rest ); } else { # before any phase error_level_file( 1, "Unmatched end of interrupt detected" ); } } elsif( $phase_name =~ /^\Q$defect_tag\E/ ) { # end of defect phase if( $event_flags[ 1 ] ) { error_level_file( 0, "Interpolating end of interrupt" ); interrupt_end( @rest ); } if( $depth > 1 ) { # normal end of defect defect_end( @rest ); } elsif( defined $event_flags[ 3 ] ) { error_level_file( 1, "Interpolating begin for unmatched end of defect" ); emulate_phase(); defect_start( $last_event, @rest ); defect_end( @rest ); } else { # before any phase error_level_file( 1, "Unmatched end of defect detected" ); } } else { # end of normal psp phase if( $event_flags[ 1 ] ) { error_level_file( 0, "Interpolating end of interrupt" ); interrupt_end( @rest ); } while( $depth > 1 ) { error_level_file( 0, "Interpolating end of defect" ); defect_end( @rest ); } if( ! defined $event_flags[ 3 ] ) { error_level_file( 1, "Unmatched end of phase $phase_name" ); } elsif( $event_flags[ 3 ] == 1 ) { debug $event_flags[ 3 ], $phase_name, $last_phase; if( $phase_name eq $last_phase ) { phase_end( $last_phase ); } else { error_level_file( 1, "End of phase $phase_name does not match begin of phase $last_phase" ); } } elsif( $event_flags[ 3 ] == 2 ) { error_level_file( 0, "Previously unnamed phase now has name $phase_name" ); phase_end( $phase_name ); } else { error_level_file( 1, "Interpolating begin of phase $phase_name" ); phase_start( $last_event, $phase_name, 2 ); phase_end( $phase_name ); } } } else { error_level_file( 0, "Unknown phase tag $phase_tag" ); } } close INFILE; if( $event_flags[ 1 ] ) { error_level_file( 1, "Unmatched begin of interrupt" ); } if( $event_flags[ 2 ] ) { error_level_file( 1, "Unmatched begin of defect" ); } if( $event_flags[ 3 ] ) { error_level_file( 1, "Unmatched begin of phase $last_phase" ); } } sub round_times { local $loop1; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $phase_duration{ $item1 } = minutes( $phase_duration{ $item1 } ) * 60; } for( $loop1 = 1; $loop1 <= scalar( keys %interrupt_number ); $loop1++ ) { $item1 = $interrupt_name{ $loop1 }; $interrupt_duration{ $item1 } = minutes( $interrupt_duration{ $item1 } ) * 60; } } sub print_out { local( $loop1, $loop2, $item1, $item2, $sum1, $sum2, $sum3, $sum4 ); $precise_flag or round_times(); if( %phase_duration ) { debug scalar( keys %phase_number ), %phase_number; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $sum1 += minutes( $phase_duration{ $item1 } ); ### debug $sum1; } print "\n\n\n"; $data_only or print "############ Duration of phases:\n"; $data_only or print "#phase\tinterv.\tfirst\tlast\ttot.dur. avg.dur. percent\n"; $data_only or print "#--------------------------------------------------------\n"; $sum2 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; if( $phase_intervals{ $item1 } ) { printf( "$item1\t%4d\t", $phase_intervals{ $item1 } ); $sum2 += $phase_intervals{ $item1 }; printf( "%4d\t", minutes( $phase_first{ $item1 } ) ); printf( "%4d\t", minutes( $phase_last{ $item1 } ) ); printf( "%4d\t", minutes( $phase_duration{ $item1 } ) ); printf( "%6.2f\t", $phase_duration{ $item1 } / 60 / $phase_intervals{ $item1 } ); printf( "%6.2f\n", 100.0 * $phase_duration{ $item1 } / 60 / ( $sum1 ) ); } } $data_only or print "#--------------------------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t\t\t", $sum2 ); $data_only or printf( "%4d\t", $sum1 ); if( $sum2 ) { $data_only or printf( "%6.2f\t", 1.0 * $sum1 / $sum2 ); } else { print "-\t"; } $data_only or printf( "%6.2f\n", 100.0 ); } if( %phase_duration ) { $data_only or $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $sum1 += $phase_duration{ $item1 }; } $sum2 = $phase_duration{ $compile_tag } + $phase_duration{ $test_tag }; $sum3 = $phase_duration{ $design_review_tag } + $phase_duration{ $code_review_tag }; if( $sum2 && $sum3 ) { $data_only or print "\n\n\n"; $data_only or print "############ Failure COQ:\t\t\t"; $data_only or printf( "%6.2f\n", 100.0 * $sum2 / $sum1 ); $data_only or print "############ Appraisal COQ:\t\t\t"; $data_only or printf( "%6.2f\n", 100.0 * $sum3 / $sum1 ); $data_only or print "############ Total COQ:\t\t\t\t"; $data_only or printf( "%6.2f\n", 100.0 * ( $sum2 + $sum3 ) / $sum1 ); $data_only or print "############ Appraisal Percentage:\t\t"; $data_only or printf( "%6.2f\n", 100.0 * $sum3 / ( $sum2 + $sum3 ) ); $data_only or print "############ Appraisal to Failure Ratio:\t"; $data_only or printf( "%6.2f\n", 100.0 * $sum3 / $sum2 ); } } if( $interrupt_flag && %interrupt_duration ) { print "\n\n\n"; $data_only or print "############ Interrupts:\n"; $data_only or print "#name\tcount\ttot.dur. avg.dur. percent\n"; $data_only or print "#----------------------------------------\n"; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %interrupt_number ); $loop1++ ) { $item1 = $interrupt_name{ $loop1 }; $sum1 += minutes( $interrupt_duration{ $item1 } ); } $sum2 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %interrupt_number ); $loop1++ ) { $item1 = $interrupt_name{ $loop1 }; print "$item1\t"; printf( "%4d\t", $interrupt_count{ $item1 } ); $sum2 += $interrupt_count{ $item1 }; printf( "%4d\t", minutes( $interrupt_duration{ $item1 } ) ); printf( "%6.2f\t", $interrupt_duration{ $item1 } / 60 / $interrupt_count{ $item1 } ); printf( "%7.2f\n", 100.0 * $interrupt_duration{ $item1 } / 60 / ( $sum1 ) ); } $data_only or print "#----------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t", $sum2 ); $data_only or printf( "%4d\t", $sum1 ); $data_only or printf( "%6.2f\t", 1.0 * $sum1 / $sum2 ); $data_only or printf( "%7.2f\n", 100.0 ); } if( ! $stats_only && %defect_class_count ) { print "\n\n\n"; $data_only or print "############ Defects per class:\n"; $data_only or print "#class\tdefects\tpercent\ttot.dur. avg.dur.\n"; $data_only or print "#----------------------------------------\n"; $sum1 = 0; foreach $item1 ( sort keys %defect_class_count ) { print( "$item1\t" ); printf( "%4d\t", $defect_class_count{ $item1 } ); printf( "%6.2f\t", 100.0 * $defect_class_count{ $item1 } / $defect_count ); printf( "%6.2f\t", $defect_class_duration{ $item1 } / 60 ); printf( "%6.2f\n", $defect_class_duration{ $item1 } / 60 / $defect_class_count{ $item1 } ); $sum1 += $defect_class_duration{ $item1 }; } $data_only or print "#----------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t", $defect_count ); $data_only or printf( "%6.2f\t", 100.0 ); $data_only or printf( "%6.2f\t", $sum1 / 60 ); $data_only or printf( "%6.2f\n", $sum1 / 60 / $defect_count ); } if( ! $stats_only && %defect_reason_count ) { print "\n"; $data_only or print "############ Defects per reason:\n"; $data_only or print "#reason\tdefects\tpercent\ttot.dur. avg.dur.\n"; $data_only or print "#----------------------------------------\n"; $sum1 = 0; foreach $item1 ( sort keys %defect_reason_count ) { print( "$item1\t" ); printf( "%4d\t", $defect_reason_count{ $item1 } ); printf( "%6.2f\t", 100.0 * $defect_reason_count{ $item1 } / $defect_count ); printf( "%6.2f\t", $defect_reason_duration{ $item1 } / 60 ); printf( "%6.2f\n", $defect_reason_duration{ $item1 } / 60 / $defect_reason_count{ $item1 } ); $sum1 += $defect_reason_duration{ $item1 }; } $data_only or print "#----------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t", $defect_count ); $data_only or printf( "%6.2f\t", 100.0 ); $data_only or printf( "%6.2f\t", $sum1 / 60 ); $data_only or printf( "%6.2f\n", $sum1 / 60 / $defect_count ); } if( ! $stats_only && %defect_inject_count ) { print "\n\n\n"; $data_only or print "############ Defects injected in phase:\n"; $data_only or print "#phase\tdefects\tpercent\ttot.dur. avg.dur.\n"; $data_only or print "#----------------------------------------\n"; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print( "$item1\t" ); if( defined $defect_inject_count{ $item1 } ) { printf( "%4d\t", $defect_inject_count{ $item1 } ); printf( "%6.2f\t", 100.0 * $defect_inject_count{ $item1 } / $defect_count ); printf( "%6.2f\t", $defect_inject_duration{ $item1 } / 60 ); $sum1 += $defect_inject_duration{ $item1 }; } else { printf( "%4d\t", 0 ); printf( "%6.2f\t", 0 ); printf( "%6.2f\t", 0 ); } if( $defect_inject_count{ $item1 } ) { printf( "%6.2f\n", $defect_inject_duration{ $item1 } / 60 / $defect_inject_count{ $item1 } ); } else { print " -\n"; } } $data_only or print "#----------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t", $defect_count ); $data_only or printf( "%6.2f\t", 100.0 ); $data_only or printf( "%6.2f\t", $sum1 / 60 ); $data_only or printf( "%6.2f\n", $sum1 / 60 / $defect_count ); } if( ! $stats_only && %defect_remove_count ) { print "\n"; $data_only or print "############ Defects removed in phase:\n"; $data_only or print "#phase\tdefects\tpercent\ttot.dur. avg.dur.\n"; $data_only or print "#----------------------------------------\n"; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print( "$item1\t" ); if( defined $defect_remove_count{ $item1 } ) { printf( "%4d\t", $defect_remove_count{ $item1 } ); printf( "%6.2f\t", 100.0 * $defect_remove_count{ $item1 } / $defect_count ); printf( "%6.2f\t", $defect_remove_duration{ $item1 } / 60 ); $sum1 += $defect_remove_duration{ $item1 }; } else { printf( "%4d\t", 0 ); printf( "%6.2f\t", 0 ); printf( "%6.2f\t", 0 ); } if( $defect_remove_count{ $item1 } ) { printf( "%6.2f\n", $defect_remove_duration{ $item1 } / 60 / $defect_remove_count{ $item1 } ); } else { print " -\n"; } } $data_only or print "#----------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t", $defect_count ); $data_only or printf( "%6.2f\t", 100.0 ); $data_only or printf( "%6.2f\t", $sum1 / 60 ); $data_only or printf( "%6.2f\n", $sum1 / 60 / $defect_count ); } if( ! $stats_only && %defect_remove_count && %defect_inject_count ) { print "\n\n\n"; $data_only or print "############ Defect removal efficiency:\n"; $data_only or print "#phase\tinject\tpres.\tremove\tescape\tyield\tdef./h\tDRL(test)\n"; $data_only or print "#----------------------------------------------------------------\n"; $sum1 = 0; $sum2 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print( "$item1\t" ); # phase printf( "%4d\t", $defect_inject_count{ $item1 } ); # inject $sum2 += $defect_inject_count{ $item1 }; printf( "%4d\t", $sum2 ); # present printf( "%4d\t", $defect_remove_count{ $item1 } ); # remove printf( "%4d\t", $sum2 - $defect_remove_count{ $item1 } ); # escape if( $defect_remove_count{ $item1 } ) { # yield printf( "%6.2f\t", 100.0 * $defect_remove_count{ $item1 } / $sum2 ); } else { print " -\t"; } $sum2 -= $defect_remove_count{ $item1 }; if( $defect_remove_duration{ $item1 } ) { # defects/hour printf( "%6.2f\t", 3600.0 * $defect_remove_count{ $item1 } / $phase_duration{ $item1 } ); } else { print " -\t"; } $sum1 += $phase_duration{ $item1 }; if( $phase_duration{ $item1 } && $defect_remove_count{ $test_tag } && $loop1 < $phase_number{ $test_tag } ) { # defect removal leverage printf( "%6.2f\n", ( $defect_remove_count{ $item1 } / $phase_duration{ $item1 } ) / ( $defect_remove_count{ $test_tag } / $phase_duration{ $test_tag } ) ); } else { print " -\n"; } } $sum3 = 0; $sum4 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; if( $loop1 < $phase_number{ $compile_tag } ) { $sum3 += $defect_inject_count{ $item1 }; $sum4 += $defect_remove_count{ $item1 }; } } $data_only or print "#----------------------------------------------------------------\n"; $data_only or print "#total\t"; printf( "%4d\t", $defect_count ); # inject printf( "%4d\t", $sum2 ); # present printf( "%4d\t", $defect_count ); # remove printf( "%4d\t", 0 ); # escape if( $sum3 ) { # overall yield $data_only or printf( "%6.2f\t", 100.0 * $sum4 / $sum3 ); } else { print " -\n"; } $data_only or printf( "%6.2f\n", 3600.0 * $defect_count / $sum1 ); # defects/hour } if( ! $stats_only && %defect_phase_count ) { print( "\n\n\n" ); $data_only or print "############ Number of defects injected in phase and removed in phase:\n"; $data_only or print "#inject\tremove phase\n"; $data_only or print "#phase\t"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $data_only or print "$item1\t"; } $data_only or print "# total\n"; $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "--------------\n"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print "$item1"; for( $loop2 = 1; $loop2 <= scalar( keys %phase_number ); $loop2++ ) { $item2 = $phase_name{ $loop2 }; if( defined $defect_phase_count{ $item1 }{ $item2 } ) { printf( "\t%4d", $defect_phase_count{ $item1 }{ $item2 } ); } else { printf( "\t%4d", 0 ); } } if( defined $defect_inject_count{ $item1 } ) { $data_only or printf( "\t#%4d", $defect_inject_count{ $item1 } ); } else { $data_only or printf( "\t#%4d", 0 ); } print( "\n" ); } $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "--------------\n"; $data_only or print "#total\t"; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; if( defined $defect_remove_count{ $item1 } ) { $data_only or printf( "%4d\t", $defect_remove_count{ $item1 } ); $sum1 += $defect_remove_count{ $item1 }; } else { $data_only or printf( "%4d\t", 0 ); } } $data_only or printf( "#%4d\n", $sum1 ); } if( ! $stats_only && %defect_phase_count ) { print( "\n" ); $data_only or print "############ Percentage of defects injected in phase and removed in phase:\n"; $data_only or print "#inject\tremove phase\n"; $data_only or print "#phase\t"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $data_only or print "$item1\t"; } $data_only or print "# total\n"; $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "---------------\n"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print "$item1"; for( $loop2 = 1; $loop2 <= scalar( keys %phase_number ); $loop2++ ) { $item2 = $phase_name{ $loop2 }; if( defined $defect_phase_count{ $item1 }{ $item2 } ) { printf( "\t%7.2f", 100.0 * $defect_phase_count{ $item1 }{ $item2 } / $defect_count ); } else { printf( "\t%7.2f", 0 ); } } if( defined $defect_inject_count{ $item1 } ) { $data_only or printf( "\t#%7.2f", 100.0 * $defect_inject_count{ $item1 } / $defect_count ); } else { $data_only or printf( "\t#%7.2f", 0 ); } print( "\n" ); } $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "---------------\n"; $data_only or print "#total\t"; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; if( defined $defect_remove_count{ $item1 } ) { $data_only or printf( "%7.2f\t", 100.0 * $defect_remove_count{ $item1 } / $defect_count ); $sum1 += $defect_remove_count{ $item1 }; } else { $data_only or printf( "%7.2f\t", 0 ); } } $data_only or printf( "#%7.2f\n", 100.0 * $sum1 / $defect_count ); } if( ! $stats_only && %defect_phase_count ) { print( "\n" ); $data_only or print "############ Total duration of defects injected in phase and removed in phase:\n"; $data_only or print "#inject\tremove phase\n"; $data_only or print "#phase\t"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $data_only or print "$item1\t"; } $data_only or print "# total\n"; $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "---------------\n"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print "$item1"; for( $loop2 = 1; $loop2 <= scalar( keys %phase_number ); $loop2++ ) { $item2 = $phase_name{ $loop2 }; if( $defect_phase_count{ $item1 }{ $item2 } ) { printf( "\t%6.2f", $defect_phase_duration{ $item1 }{ $item2 } / 60 ); } else { printf( "\t%6.2f", 0 ); } } if( defined $defect_inject_count{ $item1 } ) { $data_only or printf( "\t#%7.2f", $defect_inject_duration{ $item1 } / 60 ); } else { $data_only or printf( "\t#%7.2f", 0 ); } print( "\n" ); } $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "---------------\n"; $data_only or print "#total\t"; $sum1 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; if( $defect_remove_count{ $item1 } ) { $data_only or printf( "%6.2f\t", $defect_remove_duration{ $item1 } / 60 ); $sum1 += $defect_remove_duration{ $item1 }; } else { $data_only or printf( "%6.2f\t", 0 ); } } $data_only or printf( "#%7.2f\n", $sum1 / 60 ); } if( ! $stats_only && %defect_phase_count ) { print( "\n" ); $data_only or print "############ Average duration of defects injected in phase and removed in phase:\n"; $data_only or print "#inject\tremove phase\n"; $data_only or print "#phase\t"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; $data_only or print "$item1\t"; } $data_only or print "# total\n"; $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "---------------\n"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; print "$item1"; for( $loop2 = 1; $loop2 <= scalar( keys %phase_number ); $loop2++ ) { $item2 = $phase_name{ $loop2 }; if( $defect_phase_count{ $item1 }{ $item2 } ) { printf( "\t%6.2f", $defect_phase_duration{ $item1 }{ $item2 } / 60 / $defect_phase_count{ $item1 }{ $item2 } ); } else { printf( "\t%6.2f", 0 ); } } if( defined $defect_inject_count{ $item1 } ) { $data_only or printf( "\t#%7.2f", $defect_inject_duration{ $item1 } / 60 / $defect_inject_count{ $item1 } ); } else { $data_only or printf( "\t#%7.2f", 0 ); } print( "\n" ); } $data_only or print "#"; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $data_only or print "--------"; } $data_only or print "---------------\n"; $data_only or print "#total\t"; $sum1 = 0; $sum2 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item1 = $phase_name{ $loop1 }; if( $defect_remove_count{ $item1 } ) { $data_only or printf( "%6.2f\t", $defect_remove_duration{ $item1 } / 60 / $defect_remove_count{ $item1 } ); $sum1 += $defect_remove_count{ $item1 }; $sum2 += $defect_remove_duration{ $item1 }; } else { $data_only or printf( "%6.2f\t", 0 ); } } $data_only or printf( "#%7.2f\n", $sum2 / 60 / $sum1 ); } if( ! $stats_only && %class_inject_count ) { print "\n\n\n"; $data_only or print "############ Defects per class injected in phase:\n"; $data_only or print "#\tdefects\t\tpercent\t\ttotal duration\tavg. duration\n"; $data_only or print "#class\tdesign\tcode\tdesign\tcode\tdesign\tcode\tdesign\tcode\n"; $data_only or print "#---------------------------------------------------------------------\n"; foreach $item1 ( sort keys %defect_class_count ) { print( "$item1\t" ); printf( "%4d\t", $class_inject_count{ $design_tag }{ $item1 } ); printf( "%4d\t", $class_inject_count{ $code_tag }{ $item1 } ); if( $defect_inject_count{ $design_tag } ) { printf( "%6.2f\t", 100.0 * $class_inject_count{ $design_tag }{ $item1 } / $defect_inject_count{ $design_tag } ); } else { print " -\t"; } if( $defect_inject_count{ $code_tag } ) { printf( "%6.2f\t", 100.0 * $class_inject_count{ $code_tag }{ $item1 } / $defect_inject_count{ $code_tag } ); } else { print " -\t"; } printf( "%6.2f\t", $class_inject_duration{ $design_tag }{ $item1 } / 60 ); printf( "%6.2f\t", $class_inject_duration{ $code_tag }{ $item1 } / 60 ); if( $class_inject_count{ $design_tag }{ $item1 } ) { printf( "%6.2f\t", $class_inject_duration{ $design_tag }{ $item1 } / 60 / $class_inject_count{ $design_tag }{ $item1 } ); } else { print " -\t"; } if( $class_inject_count{ $code_tag }{ $item1 } ) { printf( "%6.2f\n", $class_inject_duration{ $code_tag }{ $item1 } / 60 / $class_inject_count{ $code_tag }{ $item1 } ); } else { print " -\n"; } } $data_only or print "#---------------------------------------------------------------------\n"; $data_only or print "#total\t"; $data_only or printf( "%4d\t", $defect_inject_count{ $design_tag } ); $data_only or printf( "%4d\t", $defect_inject_count{ $code_tag } ); if( $defect_inject_count{ $design_tag } ) { $data_only or printf( "%6.2f\t", 100.0 ); } else { $data_only or print " -\t"; } if( $defect_inject_count{ $code_tag } ) { $data_only or printf( "%6.2f\t", 100.0 ); } else { $data_only or print " -\t"; } $data_only or printf( "%6.2f\t", $defect_inject_duration{ $design_tag } / 60 ); $data_only or printf( "%6.2f\t", $defect_inject_duration{ $code_tag } / 60 ); if( $defect_inject_count{ $design_tag } ) { $data_only or printf( "%6.2f\t", $defect_inject_duration{ $design_tag } / 60 / $defect_inject_count{ $design_tag } ); } else { $data_only or print " -\t"; } if( $defect_inject_count{ $code_tag } ) { $data_only or printf( "%6.2f\n", $defect_inject_duration{ $code_tag } / 60 / $defect_inject_count{ $code_tag } ); } else { $data_only or print " -\n"; } } if( ! $stats_only && %class_remove_count ) { print "\n"; $data_only or print "############ Defects per class removed in phase:\n"; $data_only or print "#\tpresent\t\tremoved\t\tperc. in phase\ttotal duration\tavg. duration\n"; $data_only or print "#class\tcompile\ttest\tcompile\ttest\tcompile\ttest\tcompile\ttest\tcompile\ttest\n"; $data_only or print "#------------------------------------------"; $data_only or print "-------------------------------------------\n"; $sum3 = 0; $sum4 = 0; foreach $item1 ( sort keys %defect_class_count ) { print( "$item1\t" ); $sum1 = 0; $sum2 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item2 = $phase_name{ $loop1 }; if( $loop1 < $phase_number{ $compile_tag } ) { $sum1 += $class_inject_count{ $item2 }{ $item1 } - $class_remove_count{ $item2 }{ $item1 }; } if( $loop1 < $phase_number{ $test_tag } ) { $sum2 += $class_inject_count{ $item2 }{ $item1 } - $class_remove_count{ $item2 }{ $item1 }; } } printf( "%4d\t", $sum1 ); printf( "%4d\t", $sum2 ); $sum3 += $sum1; $sum4 += $sum2; printf( "%4d\t", $class_remove_count{ $compile_tag }{ $item1 } ); printf( "%4d\t", $class_remove_count{ $test_tag }{ $item1 } ); if( $defect_remove_count{ $compile_tag } ) { printf( "%6.2f\t", 100.0 * $class_remove_count{ $compile_tag }{ $item1 } / $defect_remove_count{ $compile_tag } ); } else { print " -\t"; } if( $defect_remove_count{ $test_tag } ) { printf( "%6.2f\t", 100.0 * $class_remove_count{ $test_tag }{ $item1 } / $defect_remove_count{ $test_tag } ); } else { print " -\t"; } printf( "%6.2f\t", $class_remove_duration{ $compile_tag }{ $item1 } / 60 ); printf( "%6.2f\t", $class_remove_duration{ $test_tag }{ $item1 } / 60 ); if( $class_remove_count{ $compile_tag }{ $item1 } ) { printf( "%6.2f\t", $class_remove_duration{ $compile_tag }{ $item1 } / 60 / $class_remove_count{ $compile_tag }{ $item1 } ); } else { print " -\t"; } if( $class_remove_count{ $test_tag }{ $item1 } ) { printf( "%6.2f\n", $class_remove_duration{ $test_tag }{ $item1 } / 60 / $class_remove_count{ $test_tag }{ $item1 } ); } else { print " -\n"; } } $data_only or print "#------------------------------------------"; $data_only or print "-------------------------------------------\n"; $data_only or print "#total\t"; printf( "%4d\t", $sum3 ); printf( "%4d\t", $sum4 ); $data_only or printf( "%4d\t", $defect_remove_count{ $compile_tag } ); $data_only or printf( "%4d\t", $defect_remove_count{ $test_tag } ); if( $defect_remove_count{ $compile_tag } ) { $data_only or printf( "%6.2f\t", 100.0 ); } else { $data_only or print " -\t"; } if( $defect_remove_count{ $test_tag } ) { $data_only or printf( "%6.2f\t", 100.0 ); } else { $data_only or print " -\t"; } $data_only or printf( "%6.2f\t", $defect_remove_duration{ $compile_tag } / 60 ); $data_only or printf( "%6.2f\t", $defect_remove_duration{ $test_tag } / 60 ); if( $defect_remove_count{ $compile_tag } ) { $data_only or printf( "%6.2f\t", $defect_remove_duration{ $compile_tag } / 60 / $defect_remove_count{ $compile_tag } ); } else { $data_only or print " -\t"; } if( $defect_remove_count{ $test_tag } ) { $data_only or printf( "%6.2f\n", $defect_remove_duration{ $test_tag } / 60 / $defect_remove_count{ $test_tag } ); } else { $data_only or print " -\n"; } } if( ! $stats_only && %class_remove_count ) { print "\n"; $data_only or print "############ Defects per class removed in phase:\n"; $data_only or print "#\tpresent\t\tremove\t\tperc. in phase\ttotal duration\tavg. duration\n"; $data_only or print "#class\tdsg.rv.\tcd.rv.\tdsg.rv.\tcd.rv.\tdsg.rv.\tcd.rv.\tdsg.rv.\tcd.rv.\tdsg.rv.\tcd.rv.\n"; $data_only or print "#------------------------------------------"; $data_only or print "-------------------------------------------\n"; $sum3 = 0; $sum4 = 0; foreach $item1 ( sort keys %defect_class_count ) { print( "$item1\t" ); $sum1 = 0; $sum2 = 0; for( $loop1 = 1; $loop1 <= scalar( keys %phase_number ); $loop1++ ) { $item2 = $phase_name{ $loop1 }; if( $loop1 < $phase_number{ $design_review_tag } ) { $sum1 += $class_inject_count{ $item2 }{ $item1 } - $class_remove_count{ $item2 }{ $item1 }; } if( $loop1 < $phase_number{ $code_review_tag } ) { $sum2 += $class_inject_count{ $item2 }{ $item1 } - $class_remove_count{ $item2 }{ $item1 }; } } printf( "%4d\t", $sum1 ); printf( "%4d\t", $sum2 ); $sum3 += $sum1; $sum4 += $sum2; printf( "%4d\t", $class_remove_count{ $design_review_tag }{ $item1 } ); printf( "%4d\t", $class_remove_count{ $code_review_tag }{ $item1 } ); if( $defect_remove_count{ $design_review_tag } ) { printf( "%6.2f\t", 100.0 * $class_remove_count{ $design_review_tag }{ $item1 } / $defect_remove_count{ $design_review_tag } ); } else { print " -\t"; } if( $defect_remove_count{ $code_review_tag } ) { printf( "%6.2f\t", 100.0 * $class_remove_count{ $code_review_tag }{ $item1 } / $defect_remove_count{ $code_review_tag } ); } else { print " -\t"; } printf( "%6.2f\t", $class_remove_duration{ $design_review_tag }{ $item1 } / 60 ); printf( "%6.2f\t", $class_remove_duration{ $code_review_tag }{ $item1 } / 60 ); if( $class_remove_count{ $design_review_tag }{ $item1 } ) { printf( "%6.2f\t", $class_remove_duration{ $design_review_tag }{ $item1 } / 60 / $class_remove_count{ $design_review_tag }{ $item1 } ); } else { print " -\t"; } if( $class_remove_count{ $code_review_tag }{ $item1 } ) { printf( "%6.2f\n", $class_remove_duration{ $code_review_tag }{ $item1 } / 60 / $class_remove_count{ $code_review_tag }{ $item1 } ); } else { print " -\n"; } } $data_only or print "#------------------------------------------"; $data_only or print "-------------------------------------------\n"; $data_only or print "#total\t"; printf( "%4d\t", $sum3 ); printf( "%4d\t", $sum4 ); $data_only or printf( "%4d\t", $defect_remove_count{ $design_review_tag } ); $data_only or printf( "%4d\t", $defect_remove_count{ $code_review_tag } ); if( $defect_remove_count{ $design_review_tag } ) { $data_only or printf( "%6.2f\t", 100.0 ); } else { $data_only or print " -\t"; } if( $defect_remove_count{ $code_review_tag } ) { $data_only or printf( "%6.2f\t", 100.0 ); } else { $data_only or print " -\t"; } $data_only or printf( "%6.2f\t", $defect_remove_duration{ $design_review_tag } / 60 ); $data_only or printf( "%6.2f\t", $defect_remove_duration{ $code_review_tag } / 60 ); if( $defect_remove_count{ $design_review_tag } ) { $data_only or printf( "%6.2f\t", $defect_remove_duration{ $design_review_tag } / 60 / $defect_remove_count{ $design_review_tag } ); } else { $data_only or print " -\t"; } if( $defect_remove_count{ $code_review_tag } ) { $data_only or printf( "%6.2f\n", $defect_remove_duration{ $code_review_tag } / 60 / $defect_remove_count{ $code_review_tag } ); } else { $data_only or print " -\n"; } } } sub clear_data { my $item; $header = 0; $sum_time = 0; foreach $item ( keys %start_time ) { delete $start_time{ $item }; } foreach $item ( keys %event_flags ) { delete $event_flags{ $item }; } $depth = 0; foreach $item ( keys %int_duration ) { delete $int_duration{ $item }; } $interrupt_count = 0; foreach $item ( keys %interrupt_number ) { delete $interrupt_number{ $item }; } foreach $item ( keys %interrupt_name ) { delete $interrupt_name{ $item }; } foreach $item ( keys %interrupt_duration ) { delete $interrupt_duration{ $item }; } foreach $item ( keys %interrupt_count ) { delete $interrupt_count{ $item }; } $defect_count = 0; foreach $item ( keys %defect_number ) { delete $defect_number{ $item }; } foreach $item ( keys %defect_duration ) { delete $defect_duration{ $item }; } foreach $item ( keys %defect_rest ) { delete $defect_rest{ $item }; } $phase_count = 0; foreach $item ( keys %phase_number ) { delete $phase_number{ $item }; } foreach $item ( keys %phase_name ) { delete $phase_name{ $item }; } foreach $item ( keys %phase_duration ) { delete $phase_duration{ $item }; } foreach $item ( keys %phase_intervals ) { delete $phase_intervals{ $item }; } foreach $item ( keys %phase_first ) { delete $phase_first{ $item }; } foreach $item ( keys %phase_last ) { delete $phase_last{ $item }; } foreach $item ( keys %defect_class_count ) { delete $defect_class_count{ $item }; } foreach $item ( keys %defect_reason_count ) { delete $defect_reason_count{ $item }; } foreach $item ( keys %defect_inject_count ) { delete $defect_inject_count{ $item }; } foreach $item ( keys %defect_remove_count ) { delete $defect_remove_count{ $item }; } foreach $item ( keys %defect_phase_count ) { delete $defect_phase_count{ $item }; } foreach $item ( keys %class_inject_count ) { delete $class_inject_count{ $item }; } foreach $item ( keys %class_remove_count ) { delete $class_remove_count{ $item }; } foreach $item ( keys %defect_class_duration ) { delete $defect_class_duration{ $item }; } foreach $item ( keys %defect_reason_duration ) { delete $defect_reason_duration{ $item }; } foreach $item ( keys %defect_inject_duration ) { delete $defect_inject_duration{ $item }; } foreach $item ( keys %defect_remove_duration ) { delete $defect_remove_duration{ $item }; } foreach $item ( keys %defect_phase_duration ) { delete $defect_phase_duration{ $item }; } foreach $item ( keys %class_inject_duration ) { delete $class_inject_duration{ $item }; } foreach $item ( keys %class_remove_duration ) { delete $class_remove_duration{ $item }; } } sub eval_tags { my( $tags ) = @_; my @tags, $defect_tags; my $order, @order; my $item; @tags = split /\s+/, $tags; while( $tag = shift @tags ) { next if $tag =~ /^\Q$comment_tag\E/; $comment_tag = $1 and next if $tag =~ /^c(.+)/; $phase_start_tag = $1 and next if $tag =~ /^s(.+)/; $phase_end_tag = $1 and next if $tag =~ /^e(.+)/; $interrupt_tag = $1 and next if $tag =~ /^i(.+)/; if( $tag =~ /^x(.+)/ ) { $defect_tags = $1; if( $defect_tags =~ /\:(.+)/ ) { $order = $1; $extract_reasons = $order =~ /r/; @order = split //, $order; $order_tags = scalar @order; } $defect_tags = $1 if $defect_tags =~ /(.*)\:/; $defect_tag = $defect_tags if $defect_tags; next; } $planning_tag = $1 and next if $tag =~ /^1(.+)/; $design_tag = $1 and next if $tag =~ /^2(.+)/; $design_review_tag = $1 and next if $tag =~ /^3(.+)/; $code_tag = $1 and next if $tag =~ /^4(.+)/; $code_review_tag = $1 and next if $tag =~ /^5(.+)/; $compile_tag = $1 and next if $tag =~ /^6(.+)/; $test_tag = $1 and next if $tag =~ /^7(.+)/; $postmortem_tag = $1 and next if $tag =~ /^8(.+)/; } foreach $item ( keys %phase_number ) { delete $phase_number{ $item }; } $phase_number{ $planning_tag } = 1; $phase_number{ $design_tag } = 2; $phase_number{ $design_review_tag } = 3; $phase_number{ $code_tag } = 4; $phase_number{ $code_review_tag } = 5; $phase_number{ $compile_tag } = 6; $phase_number{ $test_tag } = 7; $phase_number{ $postmortem_tag } = 8; } sub print_tags { my $result; $result = "c$comment_tag s$phase_start_tag e$phase_end_tag i$interrupt_tag x$defect_tag:" . join( '', @order ) . " 1$planning_tag 2$design_tag 3$design_review_tag 4$code_tag" . " 5$code_review_tag 6$compile_tag 7$test_tag 8$postmortem_tag"; return $result; } sub eval_options { my( $options ) = @_; $old_level = $option_level; while( $options ) { chop $options and next if $options =~ /-$/; chop $options and next if $options =~ /h$/; chop $options and next if $options =~ /\?$/; if( $options =~ /(f)$/ ) { chop $options; next; } if( $options =~ /([0-4])$/ ) { $option_level = $1; chop $options; next; } if( $options =~ /(p)$/ ) { $precise_flag = $1; chop $options; next; } if( $options =~ /(c)$/ ) { $check_only = $1; chop $options; next; } if( $options =~ /(e)$/ ) { $defects_only = $1; chop $options; next; } if( $options =~ /(s)$/ ) { $stats_only = $1; chop $options; next; } if( $options =~ /(d)$/ ) { $data_only = $1; chop $options; next; } if( $options =~ /(g)$/ ) { $debug |= 2; $option_level = 2 if $option_level < 2; chop $options; next; } if( $options =~ /(i)$/ ) { $interrupt_flag = $1; chop $options; next; } if( $options =~ /(r)$/ ) { $exchange_positions = $1; chop $options; next; } if( $options =~ /(m)$/ ) { $merge_flag = $1; chop $options; next; } if( $options =~ /(.)$/ ) { error_level( 2, "Unknown option $1" ); chop $options; } } } sub clear_options { $precise_flag = 0; $merge_flag = 0; $check_only = 0; $defects_only = 0; $stats_only = 0; $option_level = 0; $data_only = 0; $interrupt_flag = 0; $exchange_positions = 0; $option_level = $old_level; $debug &= ~ 2; } sub print_options { my $result; $result .= $option_level; $result .= "p" if $precise_flag; $result .= "c" if $check_only; $result .= "e" if $defects_only; $result .= "s" if $stats_only; $result .= "d" if $data_only; $result .= "g" if $debug & 2; $result .= "i" if $interrupt_flag; $result .= "r" if $exchange_positions; $result .= "m" if $merge_flag; return $result; } # global vars # local( $class, $inject ); # defect class, inject phase # local( $reason, $fix_defect ); # defect reason, fix defect # local( $last_event, $normal ); # timestamp of last event found, timestamp of this event # local $header; # true if defect header printed # local $sum_time; # sum of time spent in psp phases # local %start_time; # time of event start per nesting level # local %event_flags; # flags for unfinished events # local $depth; # defect/interrupt nesting depth # local %int_duration; # cumulative duration of interrupts per nesting level # local $interrupt_count; # number of different interrupts encountered so far # local %interrupt_number; # number of interrupt by name # local %interrupt_name; # name of interrupt by number # local %interrupt_duration; # cumulative duration of named interrupts # local $interrupt_rest; # rest of input line after interrupt tag # local %interrupt_count; # repetitions of named interrupts # local $defect_count; # number of defects encountered so far # local %defect_number; # numbers of nested defects # local %defect_duration; # cumulative duration of defects per nesting level # local %defect_rest; # rest of input line after defect tag per nesting level # local $phase_count; # number of phases encountered so far # local %phase_number; # number of phase by name # local %phase_name; # name of phase by number # local %phase_duration; # cumulative duration of psp phases # local %phase_intervals; # number of intervals per phase # local %phase_first; # first beginning of phase # local %phase_last; # last end of phase # local %defect_class_count; # number of defects per class # local %defect_reason_count; # number of defects per reason # local %defect_inject_count; # number of defects injected in phase # local %defect_remove_count; # number of defects removed in phase # local %defect_phase_count; # number of defects injected in phase and removed in phase # local %class_inject_count; # number of defects per class injected in phase # local %class_remove_count; # number of defects per class removed in phase # local %defect_class_duration; # cumulative duration of defects per class # local %defect_reason_duration; # cumulative duration of defects per class # local %defect_inject_duration; # cumulative duration of defects injected in phase # local %defect_remove_duration; # cumulative duration of defects removed in phase # local %defect_phase_duration; # cumulative duration of defects injected in phase and removed in phase # local %class_inject_duration; # cumulative duration of defects per class injected in phase # local %class_remove_duration; # cumulative duration of defects per class removed in phase $default_filename = "timestamps"; $comment_tag = "/"; $option_level = 1; $default_tags = "sb ee c# ii xdf:ci 1pl 2ds 3dr 4cd 5cr 6cp 7te 8pm"; while( $#ARGV >= 0 and $ARGV[ 0 ] =~ /^-/ ) { $options = shift; if( ( $rest, $tags ) = $options =~ /([^t]*)t(.*)/ ) { $options = $rest; $all_tags .= $tags; } $default_filename = shift if $options =~ /f/; ( $tags ) = $options =~ /^-(.*)/; $all_options .= $tags; } if( $all_options =~ /[?h]/ ) { eval_tags( $default_tags ); print "Usage: evalpsp [options] \n"; print "Default filename for directory arguments: \"timestamps\"\n"; print "Options are:\n"; print "-h or\n"; print "-? print this info page and exit\n"; print "-m merge multiple files into single one\n"; print "-f next argument is default filename for directory arguments\n"; print "-- read filenames from stdin (must be first of multiple options)\n"; print "-p do precise calculations (based on seconds)\n"; print "-c check for erroneous data only, no output\n"; print "-e print defect data only, no statistics\n"; print "-s print statistics only, no defect data\n"; print "-b print data in PPDF (Public PSP Database Format)\n"; print "-d print data only, no comments\n"; print "-g print debugging info (implies -2)\n"; print "-i do interrupt calculation by name\n"; print "-0 print no warnings (results may be incorrect)\n"; print "-1 print warnings on severe errors (default)\n"; print "-2 print all warnings\n"; print "-3 print all warnings, die if severe errors encountered\n"; print "-4 die if any errors encountered\n"; print "-r exchange positions of tag/phase name\n"; print "-t change field tags. Must be last of multiple options.\n"; print " Default value: \"", print_tags(), "\"\n"; exit; } if( $all_options =~ /-/ ) { while( <> ) { @filenames = split /\s+/, $_; while( $dir = shift @filenames ) { eval_tags( $default_tags ); eval_tags( $all_tags ); debug "tags: ", print_tags(); clear_options(); eval_options( $all_options ); debug "options: ", print_options(); $infilename = make_filename( $dir ); process( $infilename ); $precise_flag or round_times(); if( ! $merge_flag ) { $check_only or $defects_only or print_out(); clear_data(); } else { } } } ( $merge_flag && ( $check_only || $defects_only ) ) or print_out(); } elsif( $#ARGV >= 0 ) { while( $#ARGV >= 0 ) { $dir = shift; eval_tags( $default_tags ); eval_tags( $all_tags ); debug "tags: ", print_tags(); clear_options(); eval_options( $all_options ); debug "options: ", print_options(); $infilename = make_filename( $dir ); process( $infilename ); $precise_flag or round_times(); if( ! $merge_flag ) { $check_only or $defects_only or print_out(); clear_data(); } else { } } ( $merge_flag && ( $check_only || $defects_only ) ) or print_out(); } else { print "File: "; $dir = ; chop $dir; eval_tags( $default_tags ); eval_tags( $all_tags ); debug "tags: ", print_tags(); eval_options( $all_options ); debug "options: ", print print_options(); $infilename = make_filename( $dir ); process( $infilename ); $precise_flag or round_times(); $check_only or $defects_only or print_out(); } ###################################################################### .00; # finish .ig 'di \" finish diversion--previous line must be blank .nr nl 0-1 \" fake up transition to first page again .nr % 0 \" start at page 1 '; __END__ ############# From here on it's a standard manual page #### .de XX .ds XX \\$4\ (v\\$3) .. .XX $Id: evalpsp,v 1.2 1996/04/02 10:26:10 gramberg Exp $ .TH EVALPSP 1 \*(XX .SH NAME evalpsp \- evaluate PSP time and defect logs .SH SYNOPSIS .nr ww \w'\fBevalpsp\fP\ ' .in +\n(wwu .ta \n(wwu .ti -\n(wwu \fBevalpsp\fP \c [-[0-4]] [-[ces]] [-d] [-g] [-i] [-r] [-t\fItag-definition-string\fP] [-m] [-f filename] [-- | \fIfiles-or-directories\fP] .SH DESCRIPTION .I evalpsp reads PSP time log files and outputs defect and interrupt listings and some time statistics. When supplied with a directory, the filename in that directory is supposed to be "timestamps". .PP The lines in the log files may be comments (the comment tag has to be the first characters on a line, with '#' being the default comment character), or timestamp entries followed by tags describing the event that caused the timestamp to be entered. Currently, the format of the timestamps has to match that of the timestamps in the example below. .PP PSP phases (such as compile phase or test phase) are recognized by a phase delimiter specifying whether the phase starts ('b') or ends ('e') with this timestamp, and a tag specifying the name of the phase. Phases may not overlap; missing start or end entries are interpolated whenever unambiguously possible. The default phase names are: .IP pl for planning, .IP ds for detailed design, .IP dr for detailed design review, .IP cd for code, .IP cr for code review, .IP cp for compile, .IP te for test, .IP pm for postmortem. .PP The other PSP phases not mentioned here are not treated specially by \fIevalpsp\fP, so there are no default names for them. Especially, if you do cyclic development and divide design and design review into high-level and detailed-level, respectively, you should consider entering your data into one log file for each cycle and one for overall development. You can then use the -m option to combine them. .PP Defect entries correspond to the beginning or end of a defect removal. They occur within PSP phases, and may be nested. There is a special defect phase name ('df' being the default) which distinguishes defect removals from enclosing PSP phases. Defect entries may be augmented with tags describing the type of defect occurred. For \fIevalpsp\fP to produce useful output, this should be at least the defect class and the name of the phase in which the defect was injected. .PP Interrupts of your PSP can be documented, too. You mark beginning and end of an interrupt with a designated interrupt tag (the default is 'i'), and may decorate the entry with interrupt class name and a description of the interrupt. If you are in a hurry, or don't care about the interrupt's reason, you may indicate an interrupt by just saying "b" for "begin of interrupt" and "e" for "end of interrupt". But, be careful not to add any comments, because lines like this will be treated specially, as described under \fITimmie\fP below. .PP The following is an excerpt from an example PSP log file. .nf Jan 25 11:29:10 1996 bds Jan 25 11:35:10 1996 bi Jan 25 11:36:10 1996 ei inst - Telefon fuer Christian Jan 25 12:00:31 1996 eds Jan 25 12:00:40 1996 bcd Jan 25 12:43:11 1996 ecd Jan 25 13:06:00 1995 bcp Jan 25 13:06:00 1995 bdf Jan 25 13:07:00 1995 edf 30 cd - "#include " vergessen Jan 25 13:07:00 1995 bdf Jan 25 13:08:00 1995 edf 20 cd - Schlieszende Klammer vergessen Jan 25 13:08:55 1995 ecp .fi There are three phases, the first one containing an interrupt of class "inst", whatever that means. Two defects have been found during compile; the first of class "30", the second of class "20". Both defects have been injected during coding, as indicated by the 'cd' tag. .SH OPTIONS .IP "\-h, \-?" Display about one screenfull of options, then exit. .IP "\-0, \-1, \-2, \-3, \-4" This sets the "error sensitivity level" of \fIevalpsp\fP. Errors are missing or unrecognized tags, for example. .br -0 Print no warnings or errors at all. The results may be incorrect! .br -1 Print warnings on severe errors only. This is the default. .br -2 Print all warnings, but stay here. .br -3 Print all warnings, die if severe errors encountered. Use this option to safe-check your data. .br -4 Die if any errors encountered. .IP \-c Check for erroneous data only, don't output anything. Useful only in combination with -[2-4]. .IP \-e Print defect data only, no statistics. .IP \-s Print statistics only, no defect data. .IP \-b Print data in PPDF (Public PSP Database Format), so it can be piped into \fIdbmerge\fP. .IP \-d Omit comments like filenames, headers, and table borders. Use this option if you want to do further automatic processing. .IP \-g Print debugging informations during run. This was meant for the author only, but may be helpful for you if you want to send me any bug reports. Implies -2. .IP \-i Do interrupt calculation by name. This takes the first word after any interrupt tag to be the name of an interrupt class. Upon end of input, the total interrupt duration per class is printed. .IP \-r Reverse the order of phase delimiters and phase names. By default, the phase delimiter comes first. If you prefer to append the delimiter to the phase name, feel free to do so, but add -r to your option list. .IP "\-t \fIstring\fP" Change the default settings for phase delimiter and name tags. The \fIstring\fP must consist of whitespace-delimited words. The first character of each word specifies which delimiter or name to change, the rest of the word is the new value. Except for the phase start and end delimiters the tags may consist of more than one character. The following characters are recognized as first characters: .br c - the comment character. .br s - the phase start delimiter. .br e - the phase end delimiter. .br i - the interrupt phase name. .br x - the defect phase name. .br 2 - the detailed design phase name. .br 3 - the detailed design review phase name. .br 4 - the code phase name. .br 5 - the code review phase name. .br 6 - the compile phase name. .br 7 - the test phase name. .br If you append a colon (':') to the 'x' and defect phase name word, the characters thereafter are treated as a list describing the order of the marks you append on a defect entry line. These may any be of the characters c for "defect (c)lass", .br i for "phase (i)njected", .br r for "defect (r)eason", and .br f for "(f)ix defect." .br The default settings, as specified in this syntax, would be .nf "c# s( e) ii xdf:ci 2ds 3dr 4cd 5cr 6cp 7te". .fi This option must be the last in a multi-option string. If you want to change more than one delimiter or name with a single -t option, you must enclose the \fIstring\fP in quotes in order to escape the shell! And it is your responsibility to check for unambiguity. .br There is another convenient way to adapt the tags to your preferences. \fIevalpsp\fP treats a comment line, with an exclamation mark ('!') following immediately after the comment delimiter, as an extension to the command line options. You may specify any of the options in normal dash ('-') format, plus your preferred phase tags but without the leading "-t" and without enclosing quotation marks. As an example, this is what the tag redifinition line looks like in my log files: .nf #! -r c# ss ee ii xe:cir 2dsg 3drw 4cde 5crw 6cmp 7tst .fi As you can see, I am one of those who first say what they do and then what happened to it, by specifying "-r". I also prefer 's' for start of phase and 'e' for end of phase tags. And I chose to use some more descriptive phase names. After all, this is a personal SP! .IP \-m Merge multiple files into a single big one. Using this option you can produce overall statistics spanning more than one program log. .IP "\-f \fIfilename\fP" If you supply \fIevalpsp\fP with a directory name, it will look in that directory for a file named "timestamps" and evaluate that file. You can use this option to change the default filename to whatever you take as your standard log file name. .IP \-- Read a list of whitespace delimited input filenames from STDIN and open these files for input. This may be helpful if you want to keep a list of files in order to evaluate them all at once with the -m option. This option must be the first in a multi-option string. .SH TIMMIE If you use Timmie to produce the time log, you can use Timmie projects as PSP phases, and log all PSP phases for one (or more) project(s) in one Timmie log window. Timmie will automatically insert timestamps into the log when you switch between projects/phases. (See the Timmie documentation for this.) If the project names contain a dot ("."), the part of the name after the last dot is taken as the PSP phase name; if there is no dot, the whole project name is used as the PSP phase name. The former offers the possibility to decorate the PSP phase names with the names of the task you're working on. E. g.: .nf GUI.pl GUI.ds database.pl database.ds database.cd database.cp database.te project.pl project.pm .fi .SH EXAMPLES (to be provided) .SH RELATED COMMANDS There is an \fIemacs\fP lisp file named \fIpsp.el\fP which configures \fIemacs\fP to enter timestamps into a special log buffer upon pressing a specified key. These timestamps may then be decorated with phase tags and later on evaluated by \fIevalpsp\fP. .SH AUTHOR Oliver Gramberg .br Softwarelabor Karlsruhe .SH "SEE ALSO" perl(1), emacs(1) .SH BUGS Because \fIevalpsp\fP interpolates ends of interrupt or defect removal phases, at the time being you cannot decide to stop working on a defect, end the enclosing phase, and restart it on Monday next week. This for shure is not very convenient, but can be worked around by introducing a (possibly named?) interrupt instead. .PP Missing data will often result in a "division by zero" error when calculating averages. .PP There is no support for addressing fix defects. This would have to include something like automatic defect numbering by the \fIemacs\fP extension. .ex