工具与思路-记如何统计编译警告

手里有把锤子,看什么都像钉子。

都想去敲。

最近遇到的问题是,要统计各个组件编程过程中有多少警告。这些警告统计出来便于安排资源去清理,目标是一个干净的编译环境。问题是编译Log比较大,有好几M,肯定不能靠数。怎么办?

首先分析GCC的编译警告是有规律的,它给出了警告所在文件,所在的行数,又哪个警告选项产生的。例如下面的警告就是代码违背了-Wunused-but-set-variable规则,变量未使用。还有一个总要的规律,是同一个文件的警告肯定是连续的。

<Line No.>: <File Path>:4023:9: warning: variable 'isError' set but not used [-Wunused-but-set-variable]

另外,我们增加一个关闭所有警告(-w)的编译Log,结合原有警告是连续的这个规律,这两个Log文件的Delta就是相邻位置对应文件的警告数目。很容易通过一个Perl脚本来实现。

#!/user/bin/perl

use strict;
use warnings;

# file 1 should contine more lines
my $file1 = 'baseline.txt';
my $file2 = 'fix.txt';

open(FILE1, "<$file1") || die "Could not read file, program halting.";
my @lines1 = <FILE1>;
close(FILE1);

open(FILE2, "<$file2") || die "Could not read file, program halting.";
my @lines2 = <FILE2>;
close(FILE2);

my $len_lines1 = $#lines1;
my $len_lines2 = $#lines2;

print "len1 = $len_lines1, len2 = $len_lines2\r\n";

if($len_lines1 < $len_lines1)
{
	die "file order is incorrect.";
}

my $i = 0;
my $j = 0;

my $plusi_count = 0;
my $warn_file = "";

while($j < $len_lines2)
{
	#print "" . $lines1[$i] . " VS\r\n" . $lines2[$j];
	
	if($lines1[$i] eq $lines2[$j])
	{
		if($plusi_count > 0)
		{
			print $plusi_count . "\t" . $warn_file;
		}
		
		$plusi_count = 0;
		$warn_file = "";
		++$i;
		++$j;
	}
	else
	{
		#print $lines2[$j];
		$warn_file = $lines2[$j - 1];
		
		++$plusi_count;
		++$i;
	}
}

细节未完善。

这个办法有个不足就是我们需要一个对比的无警告的文件做参考。从实现上讲,我们可以改进:筛选出有警告的行,然后把文件路径提取出来,然后相同文件的统计一个数目,这个数目就是每个文件的警告总数。

如果我们增加需求,需要按组件统计数目又该怎么办?我们继续改进,根据前面统计得出的文件结果,根据不同组件使用不同的路径,再把相同路径的统计一个数目,这个数目就是每个组件的警告总数。

随着需求的增加,Perl越来越显得无力。因为Perl的实现,需要增加很多复杂的逻辑。

换个角度考虑,把整个统计的过程看成是一个工作链,一个节点处理完了交给下一个节点处理。其实这就是Streaming或者Chain的思想。Spark对这样的处理要求支持的非常好。我们用Spark实现它:

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import scala.Tuple2;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HLWarningCount {
    private static final Pattern FILE_PATTERN = Pattern.compile("(.*?[c|cpp|h]):(\\d+):(\\d+): warning:");

    public static void main(String[] args) throws Exception {
        SparkConf sparkConf = new SparkConf().setAppName("WarnCount");
        JavaSparkContext ctx = new JavaSparkContext(sparkConf);
        JavaRDD<String> lines = ctx.textFile("log.txt", 1);

        JavaRDD<String> warningLines = lines.filter(new Function<String, Boolean>() {
            @Override
            public Boolean call(String s) throws Exception {
                if(s.contains("warning:")){
                    return true;
                }else{
                    return false;
                }
            }
        }).filter(new Function<String, Boolean>() {
            @Override
            public Boolean call(String s) throws Exception {
                /* TODO: update to filter real code warning */
                if(s.contains(".mk")){
                    return false;
                }else{
                    return true;
                }
            }
        });

        JavaPairRDD<String, Integer> mappedLines = warningLines.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) {
                Matcher m = FILE_PATTERN.matcher(s);
                if(m.find()){
                    return new Tuple2<String, Integer>(m.group(1), 1);
                }else{
                    System.out.println("ERROR - " + s);
                    return null;
                }
            }
        });

        JavaPairRDD<String, Integer> countedLines = mappedLines.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer i1, Integer i2) {
                return i1 + i2;
            }
        });

        List<Tuple2<String, Integer>> output = countedLines.toArray();
        for (Tuple2<?,?> tuple : output) {
            if(tuple != null){
                System.out.println(tuple._1() + ": " + tuple._2());
            }
        }

        ctx.stop();
    }
}

Spark的链式处理办法,很容易扩展,我们很容易做些过滤(filter)合并(reduce)操作。

当然我们只是使用Spark提供的编程范式和易用的API,并不是它的大数据能力。使用Scala一样可以做到,因为Spark很多变换操作其实是Scala 的Iterator接口。

你可能感兴趣的:(工具与思路-记如何统计编译警告)