Update mojo sdk to rev 1dc8a9a5db73d3718d99917fadf31f5fb2ebad4f
[chromium-blink-merge.git] / third_party / lcov / bin / mcov
blobe6d344665f15674f6576ff19a5520ef2ac8bea66
1 #!/usr/bin/perl -w
2 #
3 # mcov: script to convert gcov data to lcov format on Mac.
5 # Based on lcov (http://ltp.sourceforge.net/coverage/lcov.php)
6 # Written by ajeya at google dot com.
8 # usage:
9 # mcov --directory <base directory> --output <output file> --verbose <level>
12 use strict;
14 use Cwd;
15 use File::Basename;
16 use File::Find;
17 use File::Spec::Functions;
18 use Getopt::Long;
20 # function prototypes
21 sub process_dafile(@);
22 sub canonical_path(@);
23 sub split_filename(@);
24 sub read_gcov_header(@);
25 sub read_gcov_file(@);
27 # scalars with default values
28 my $directory = Cwd::abs_path(Cwd::getcwd);
29 my $data_file_extension = ".gcda";
30 my $output_filename = "output.lcov";
31 my $gcov_tool = "/usr/bin/gcov";
32 my $verbosity = 0;
34 # TODO(ajeya): GetOptions doesn't handle case where the script is called with
35 # no arguments. This needs to be fixed.
36 my $result = GetOptions("directory|d=s" => \$directory,
37 "output|o=s" => \$output_filename,
38 "verbose" => \$verbosity);
39 if (!$result) {
40 print "Usage: $0 --directory <base directory> --output <output file>";
41 print " [--verbose <level>]\n";
42 exit(1);
45 # convert the directory path to absolute path.
46 $directory = Cwd::abs_path($directory);
48 # convert the output file path to absolute path.
49 $output_filename = Cwd::abs_path($output_filename);
51 # Output expected args for buildbot debugging assistance.
52 my $cwd = getcwd();
53 print "mcov: after abs_pathing\n";
54 print "mcov: getcwd() = $cwd\n";
55 print "mcov: directory for data files is $directory\n";
56 print "mcov: output filename is $output_filename\n";
58 # Sanity check; die if path is wrong.
59 # We don't check for output_filename because... we create it.
60 if (! -d $directory) {
61 print "mcov: Bad args passed; exiting with error.\n";
62 exit(1);
65 # open file for output
66 open(INFO_HANDLE, ">$output_filename");
68 my @file_list; # scalar to hold the list of all gcda files.
69 if (-d $directory) {
70 printf("Scanning $directory for $data_file_extension files ...\n");
71 find(sub {
72 my $file = $_;
73 if ($file =~ m/\Q$data_file_extension\E$/i) {
74 push(@file_list, Cwd::abs_path($file));
75 }},
76 $directory);
77 printf("Found %d data files in %s\n", $#file_list + 1, $directory);
80 # Process all files in list
81 foreach my $file (@file_list) {
82 process_dafile($file);
84 close(INFO_HANDLE);
86 # Remove the misc gcov files that are created.
87 my @gcov_list = glob("*.gcov");
88 foreach my $gcov_file (@gcov_list) {
89 unlink($gcov_file);
92 exit(0);
94 # end of script
96 # process_dafile:
97 # argument(s): a file path with gcda extension
98 # returns: void
99 # This method calls gcov to generate the coverage data and write the output in
100 # lcov format to the output file.
101 sub process_dafile(@) {
102 my ($filename) = @_;
103 print("Processing $filename ...\n");
105 my $da_filename; # Name of data file to process
106 my $base_name; # data filename without ".da/.gcda" extension
107 my $gcov_error; # Error code of gcov tool
108 my $object_dir; # Directory containing all object files
109 my $gcov_file; # Name of a .gcov file
110 my @gcov_data; # Contents of a .gcov file
111 my @gcov_list; # List of generated .gcov files
112 my $base_dir; # Base directory for current da file
113 local *OLD_STDOUT; # Handle to store STDOUT temporarily
115 # Get directory and basename of data file
116 ($base_dir, $base_name) = split_filename(canonical_path($filename));
118 # Check for writable $base_dir (gcov will try to write files there)
119 if (!-w $base_dir) {
120 print("ERROR: cannot write to directory $base_dir\n");
121 return;
124 # Construct name of graph file
125 $da_filename = File::Spec::Functions::catfile($base_dir,
126 join(".", $base_name, "gcno"));
128 # Ignore empty graph file (e.g. source file with no statement)
129 if (-z $da_filename) {
130 warn("WARNING: empty $da_filename (skipped)\n");
131 return;
134 # Set $object_dir to real location of object files. This may differ
135 # from $base_dir if the graph file is just a link to the "real" object
136 # file location.
137 $object_dir = dirname($da_filename);
139 # Save the current STDOUT to OLD_STDOUT and set STDOUT to /dev/null to mute
140 # standard output.
141 if (!$verbosity) {
142 open(OLD_STDOUT, ">>&STDOUT");
143 open(STDOUT, ">/dev/null");
146 # run gcov utility with the supplied gcno file and object directory.
147 $gcov_error = system($gcov_tool, $da_filename, "-o", $object_dir);
149 # Restore STDOUT if we changed it before.
150 if (!$verbosity) {
151 open(STDOUT, ">>&OLD_STDOUT");
154 if ($gcov_error) {
155 warn("WARNING: GCOV failed for $da_filename!\n");
156 return;
159 # Collect data from resulting .gcov files and create .info file
160 @gcov_list = glob("*.gcov");
161 # Check for files
162 if (!scalar(@gcov_list)) {
163 warn("WARNING: gcov did not create any files for $da_filename!\n");
166 foreach $gcov_file (@gcov_list) {
167 my $source_filename = read_gcov_header($gcov_file);
169 if (!defined($source_filename)) {
170 next;
173 $source_filename = canonical_path($source_filename);
175 # Read in contents of gcov file
176 @gcov_data = read_gcov_file($gcov_file);
178 # Skip empty files
179 if (!scalar(@gcov_data)) {
180 warn("WARNING: skipping empty file $gcov_file\n");
181 unlink($gcov_file);
182 next;
185 print(INFO_HANDLE "SF:", Cwd::abs_path($source_filename), "\n");
187 # Write coverage information for each instrumented line
188 # Note: @gcov_content contains a list of (flag, count, source)
189 # tuple for each source code line
190 while (@gcov_data) {
191 # Check for instrumented line
192 if ($gcov_data[0]) {
193 print(INFO_HANDLE "DA:", $gcov_data[3], ",", $gcov_data[1], "\n");
195 # Remove already processed data from array
196 splice(@gcov_data,0,4);
198 print(INFO_HANDLE "end_of_record\n");
200 # Remove .gcov file after processing
201 unlink($gcov_file);
202 } #end for_each
205 # canonical_path:
206 # argument(s): any file path
207 # returns: the file path as a string
209 # clean up the file path being passed.
210 sub canonical_path(@) {
211 my ($filename) = @_;
212 return (File::Spec::Functions::canonpath($filename));
215 # split_filename:
216 # argument(s): any file path
217 # returns: an array with the path components
219 # splits the file path into path and filename (with no extension).
220 sub split_filename(@){
221 my ($filename) = @_;
222 my ($base, $path, $ext) = File::Basename::fileparse($filename, '\.[^\.]*');
223 return ($path, $base);
226 # read_gcov_header:
227 # argument(s): path to gcov file
228 # returns: an array the contens of the gcov header.
230 # reads the gcov file and returns the parsed contents of a gcov header as an
231 # array.
232 sub read_gcov_header(@) {
233 my ($filename) = @_;
234 my $source;
235 local *INPUT;
237 if (!open(INPUT, $filename)) {
238 warn("WARNING: cannot read $filename!\n");
239 return (undef,undef);
242 my @lines = <INPUT>;
243 foreach my $line (@lines) {
244 chomp($line);
245 # check for lines with source string.
246 if ($line =~ /^\s+-:\s+0:Source:(.*)$/) {
247 # Source: header entry
248 $source = $1;
249 } else {
250 last;
253 close(INPUT);
254 return $source;
257 # read_gcov_file:
258 # argument(s): path to gcov file
259 # returns: an array with the contents of the gcov file.
261 # reads the gcov file and returns the parsed contents of a gcov file
262 # as an array.
263 sub read_gcov_file(@) {
264 my ($filename) = @_;
265 my @result = ();
266 my $number;
267 local *INPUT;
269 if (!open(INPUT, $filename)) {
270 warn("WARNING: cannot read $filename!\n");
271 return @result;
274 # Parse gcov output and populate the array
275 my @lines = <INPUT>;
276 foreach my $line (@lines) {
277 chomp($line);
278 if ($line =~ /^\s*([^:]+):\s*(\d+?):(.*)$/) {
279 # <exec count>:<line number>:<source code>
281 if ($1 eq "-") {
282 # Uninstrumented line
283 push(@result, 0);
284 push(@result, 0);
285 push(@result, $3);
286 push(@result, $2);
287 } elsif ($2 eq "0") {
288 #ignore comments and other header info
289 } else {
290 # Source code execution data
291 $number = $1;
292 # Check for zero count
293 if ($number eq "#####") {
294 $number = 0;
296 push(@result, 1);
297 push(@result, $number);
298 push(@result, $3);
299 push(@result, $2);
303 close(INPUT);
304 return @result;