generate-coverage-data 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/perl -w
  2. # Copyright (C) 2005, 2006, 2013 Apple Computer, Inc. All rights reserved.
  3. # Copyright (C) 2007 Holger Hans Peter Freyther. All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. #
  9. # 1. Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. # 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  15. # its contributors may be used to endorse or promote products derived
  16. # from this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  19. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  22. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. # Script to build, run and visualize coverage information
  29. use strict;
  30. use File::Basename;
  31. use File::Spec;
  32. use FindBin;
  33. use Getopt::Long qw(:config pass_through);
  34. use JSON;
  35. use lib $FindBin::Bin;
  36. use List::Util qw(sum);
  37. use List::Util qw(max);
  38. use POSIX;
  39. use webkitdirs;
  40. use XML::Simple;
  41. sub parseGcovrOutput($);
  42. sub getFileHitsAndBranches($);
  43. sub addLineCounts($$$$$$);
  44. sub createResultName();
  45. my $resultName = createResultName();
  46. # Move to the source directory
  47. chdirWebKit();
  48. # Delete old gcov files
  49. print "Cleaning up\n";
  50. system("if [ -d WebKitBuild ]; then find WebKitBuild -name '*.gcda' -delete; fi;") == 0 or die "Cannot delete old gcda files (code coverage";
  51. # Compile WebKit and run the tests
  52. print "Building and testing\n";
  53. system("Tools/Scripts/build-webkit", "--coverage", @ARGV) == 0 or die "Cannot compile webkit with code coverage";
  54. system("Tools/Scripts/run-webkit-tests");
  55. system("Tools/Scripts/run-webkit-tests -2");
  56. system("Tools/Scripts/run-javascriptcore-tests");
  57. system("Tools/Scripts/run-api-tests");
  58. # Generate the coverage data and report
  59. print "Collecting coverage data\n";
  60. system("mkdir WebKitBuild/Coverage") if ! -d "WebKitBuild/Coverage";
  61. system("python Tools/Scripts/webkitpy/tool/gcovr --xml --output=WebKitBuild/Coverage/" . $resultName . ".xml") == 0 or die "Cannot run gcovr";
  62. # Collect useful data from xml to json format
  63. open my $jsonFile, ">", "WebKitBuild/Coverage/$resultName.json" or die "Cannot open $resultName.json";
  64. print $jsonFile encode_json(parseGcovrOutput("WebKitBuild/Coverage/$resultName.xml"));
  65. close $jsonFile;
  66. print "Done\n";
  67. sub parseGcovrOutput($)
  68. {
  69. my ($xmlData) = @_;
  70. my $sourceDir = sourceDir();
  71. my @files;
  72. # The xml output of gcovr uses a Java-like package/class names for directories and files
  73. my $packages = new XML::Simple->XMLin($xmlData)->{"packages"}->{"package"};
  74. foreach my $packageName (keys %{$packages}) {
  75. my $classes = $packages->{$packageName}->{"classes"}->{"class"};
  76. # Perl's XML::Simple causes files to be here in the parsed xml data structure
  77. # if there's only one child, even though they're a layer deeper in the xml tree
  78. if ($classes->{"filename"} && $classes->{"lines"}) {
  79. if ($classes->{"filename"} =~ /$sourceDir/) {
  80. push(@files, getFileHitsAndBranches($classes));
  81. }
  82. }
  83. else {
  84. foreach my $key (keys %{$classes}) {
  85. my $class = $classes->{$key};
  86. if ($class->{"filename"} =~ /$sourceDir/) {
  87. push(@files,getFileHitsAndBranches($class));
  88. }
  89. }
  90. }
  91. }
  92. return \@files;
  93. }
  94. sub getFileHitsAndBranches($)
  95. {
  96. my ($class) = @_;
  97. my @hits;
  98. my @hitLines;
  99. my @branchesPossible;
  100. my @branchesTaken;
  101. my @branchLines;
  102. my $lines = $class->{"lines"}->{"line"};
  103. if (ref($lines) eq "ARRAY") {
  104. foreach my $line (@$lines) {
  105. addLineCounts($line, \@hits, \@hitLines, \@branchesPossible, \@branchesTaken, \@branchLines);
  106. }
  107. } else {
  108. addLineCounts($lines, \@hits, \@hitLines, \@branchesPossible, \@branchesTaken, \@branchLines);
  109. }
  110. my $file = {};
  111. $file->{"hits"} = \@hits;
  112. $file->{"hitLines"} = \@hitLines;
  113. $file->{"branchesPossible"} = \@branchesPossible;
  114. $file->{"branchesTaken"} = \@branchesTaken;
  115. $file->{"branchLines"} = \@branchLines;
  116. $file->{"filename"} = substr($class->{"filename"}, length(sourceDir()));
  117. $file->{"coverage"} = abs($class->{"line-rate"});
  118. if (@branchLines) {
  119. $file->{"branchCoverage"} = abs($class->{"branch-rate"});
  120. } else {
  121. $file->{"branchCoverage"} = 1;
  122. }
  123. $file->{"totalHeat"} = sum(@hits);
  124. $file->{"maxHeat"} = max(@hits);
  125. return $file;
  126. }
  127. sub addLineCounts($$$$$$)
  128. {
  129. my ($line, $hits, $hitLines, $branchesPossible, $branchesTaken, $branchLines) = @_;
  130. push(@$hits, int($line->{"hits"}));
  131. push(@$hitLines, int($line->{"number"}));
  132. if($line->{"branch"} eq "true") {
  133. # Extract the numerator and denominator of the condition-coverage attribute, which looks like "75% (3/4)"
  134. $line->{"condition-coverage"} =~ /\((.*)\/(.*)\)/;
  135. push(@$branchesTaken, int($1));
  136. push(@$branchesPossible, int($2));
  137. push(@$branchLines, int($line->{"number"}));
  138. }
  139. }
  140. sub createResultName()
  141. {
  142. my $svnVersion = determineCurrentSVNRevision();
  143. my @timeData = localtime(time);
  144. return $svnVersion . "-" . join('_', @timeData);
  145. }