1 | /*! @file GcovData.cc |
---|
2 | * @brief GcovData Implementation |
---|
3 | * |
---|
4 | * This file contains the implementation of the functions supporting |
---|
5 | * reading *.gcno and writing *.gcda files for gcov support |
---|
6 | */ |
---|
7 | |
---|
8 | #include <stdio.h> |
---|
9 | #include <string.h> |
---|
10 | #include <stdlib.h> |
---|
11 | //#include <sys/stat.h> |
---|
12 | |
---|
13 | //#include "app_common.h" |
---|
14 | #include "GcovData.h" |
---|
15 | //#include "ExecutableInfo.h" |
---|
16 | //#include "CoverageMap.h" |
---|
17 | //#include "qemu-traces.h" |
---|
18 | |
---|
19 | #include "rtems-utils.h" |
---|
20 | |
---|
21 | namespace Gcov { |
---|
22 | |
---|
23 | GcovData::GcovData( Coverage::DesiredSymbols& symbolsToAnalyze ): |
---|
24 | numberOfFunctions( 0 ), |
---|
25 | gcnoPreamble(), |
---|
26 | symbolsToAnalyze_m( symbolsToAnalyze ) |
---|
27 | { |
---|
28 | } |
---|
29 | |
---|
30 | GcovData::~GcovData() |
---|
31 | { |
---|
32 | } |
---|
33 | |
---|
34 | bool GcovData::readGcnoFile( const std::string& fileName ) |
---|
35 | { |
---|
36 | int status; |
---|
37 | std::ifstream gcovFile; |
---|
38 | std::string tempString; |
---|
39 | std::string tempString2; |
---|
40 | std::string tempString3; |
---|
41 | size_t index; |
---|
42 | |
---|
43 | if ( fileName.length() >= FILE_NAME_LENGTH ) { |
---|
44 | std::cerr << "ERROR: File name is too long to be correctly stored: " |
---|
45 | << fileName.length() << std::endl; |
---|
46 | return false; |
---|
47 | } |
---|
48 | |
---|
49 | gcnoFileName = fileName; |
---|
50 | gcdaFileName = fileName; |
---|
51 | textFileName = fileName; |
---|
52 | cFileName = fileName; |
---|
53 | tempString = gcdaFileName; |
---|
54 | tempString2 = textFileName; |
---|
55 | tempString3 = cFileName; |
---|
56 | |
---|
57 | index = tempString.find( ".gcno" ); |
---|
58 | if ( index == std::string::npos ) { |
---|
59 | std::cerr << "ERROR: incorrect name of *.gcno file" << std::endl; |
---|
60 | return false; |
---|
61 | } else { |
---|
62 | // construct gcda file name |
---|
63 | tempString = tempString.replace( index, strlen( ".gcno" ), ".gcda" ); |
---|
64 | |
---|
65 | // construct report file name |
---|
66 | tempString2 = tempString2.replace( index, strlen( ".gcno" ), ".txt" ); |
---|
67 | |
---|
68 | // construct source file name |
---|
69 | tempString3 = tempString3.replace( index, strlen( ".gcno" ), ".c" ); |
---|
70 | } |
---|
71 | |
---|
72 | // Debug message |
---|
73 | // std::cerr << "Reading file: " << gcnoFileName << std::endl; |
---|
74 | |
---|
75 | // Open the notes file. |
---|
76 | gcovFile.open( gcnoFileName ); |
---|
77 | if ( !gcovFile ) { |
---|
78 | std::cerr << "Unable to open " << gcnoFileName << std::endl; |
---|
79 | return false; |
---|
80 | } |
---|
81 | |
---|
82 | // Read and validate the gcnoPreamble (magic, version, timestamp) from the file |
---|
83 | status = readFilePreamble( &gcnoPreamble, gcovFile, GCNO_MAGIC ); |
---|
84 | if ( status <= 0 ) { |
---|
85 | std::cerr << "Unable to read " << gcnoFileName << std::endl; |
---|
86 | return false; |
---|
87 | } |
---|
88 | |
---|
89 | //Read all remaining frames from file |
---|
90 | while( readFrame( gcovFile ) ) {} |
---|
91 | |
---|
92 | return true; |
---|
93 | } |
---|
94 | |
---|
95 | |
---|
96 | bool GcovData::writeGcdaFile() |
---|
97 | { |
---|
98 | gcov_preamble preamble; |
---|
99 | gcov_frame_header header; |
---|
100 | std::ofstream gcdaFile; |
---|
101 | functions_iterator_t currentFunction; |
---|
102 | arcs_iterator_t currentArc; |
---|
103 | uint32_t buffer; |
---|
104 | uint32_t countersFound; |
---|
105 | uint32_t countersFoundSum; |
---|
106 | uint64_t countersSum; |
---|
107 | uint64_t countersMax; |
---|
108 | uint64_t llBuffer[4096]; // TODO: Use common buffer |
---|
109 | gcov_statistics objectStats; |
---|
110 | gcov_statistics programStats; |
---|
111 | std::ofstream::pos_type bytes_before; |
---|
112 | |
---|
113 | // Debug message |
---|
114 | //std::cerr << "Writing file: " << gcdaFileName << std::endl; |
---|
115 | |
---|
116 | // Lets clear counters sumators |
---|
117 | countersSum = 0; |
---|
118 | countersMax = 0; |
---|
119 | countersFoundSum = 0; |
---|
120 | |
---|
121 | // Open the data file. |
---|
122 | gcdaFile.open( gcdaFileName ); |
---|
123 | if ( !gcdaFile ) { |
---|
124 | std::cerr << "Unable to create " << gcdaFileName << std::endl; |
---|
125 | return false; |
---|
126 | } |
---|
127 | |
---|
128 | //Form preamble |
---|
129 | preamble.magic = GCDA_MAGIC; |
---|
130 | preamble.version = gcnoPreamble.version; |
---|
131 | preamble.timestamp = gcnoPreamble.timestamp; |
---|
132 | |
---|
133 | //Write preamble |
---|
134 | gcdaFile.write( (char *) &preamble , sizeof( preamble ) ); |
---|
135 | if ( gcdaFile.fail() ) { |
---|
136 | std::cerr << "Error while writing gcda preamble to a file " |
---|
137 | << gcdaFileName << std::endl; |
---|
138 | } |
---|
139 | |
---|
140 | //Write function info and counter counts |
---|
141 | for ( |
---|
142 | currentFunction = functions.begin(); |
---|
143 | currentFunction != functions.end(); |
---|
144 | currentFunction++ |
---|
145 | ) { |
---|
146 | //Write function announcement frame header (length always equals 2) |
---|
147 | header.tag = GCOV_TAG_FUNCTION; |
---|
148 | header.length = 2; |
---|
149 | gcdaFile.write( (char *) &header, sizeof( header ) ); |
---|
150 | if ( gcdaFile.fail() ) { |
---|
151 | std::cerr << "Error while writing function announcement to a file " |
---|
152 | << gcdaFileName << std::endl; |
---|
153 | } |
---|
154 | |
---|
155 | //Write function id |
---|
156 | buffer = (*currentFunction).getId(); |
---|
157 | gcdaFile.write( (char *) &buffer, sizeof( buffer ) ); |
---|
158 | if ( gcdaFile.fail() ) { |
---|
159 | std::cerr << "Error while writing function id to a file " |
---|
160 | << gcdaFileName << std::endl; |
---|
161 | } |
---|
162 | |
---|
163 | //Write function checksum |
---|
164 | buffer = (*currentFunction).getChecksum(); |
---|
165 | gcdaFile.write( (char *) &buffer, sizeof( buffer ) ); |
---|
166 | if ( gcdaFile.fail() ) { |
---|
167 | std::cerr << "Error while writing function checksum to a file " |
---|
168 | << gcdaFileName << std::endl; |
---|
169 | } |
---|
170 | |
---|
171 | // Determine how many counters there are |
---|
172 | // and store their counts in buffer |
---|
173 | countersFound = 0; |
---|
174 | (*currentFunction).getCounters( |
---|
175 | llBuffer, |
---|
176 | countersFound, |
---|
177 | countersSum, |
---|
178 | countersMax |
---|
179 | ); |
---|
180 | countersFoundSum += countersFound; |
---|
181 | |
---|
182 | //Write info about counters |
---|
183 | header.tag = GCOV_TAG_COUNTER; |
---|
184 | header.length = countersFound * 2; |
---|
185 | gcdaFile.write( (char *) &header, sizeof( header ) ); |
---|
186 | if ( gcdaFile.fail() ) { |
---|
187 | std::cerr << "Error while writing counter header to a file " |
---|
188 | << gcdaFileName << std::endl; |
---|
189 | } |
---|
190 | |
---|
191 | bytes_before = gcdaFile.tellp(); |
---|
192 | |
---|
193 | gcdaFile.write( (char *) llBuffer, sizeof( uint64_t ) * countersFound ); |
---|
194 | if ( gcdaFile.tellp() - bytes_before != countersFound ) { |
---|
195 | std::cerr << "Error while writing counter data to a file " |
---|
196 | << gcdaFileName << std::endl; |
---|
197 | } |
---|
198 | } |
---|
199 | |
---|
200 | // Prepare frame with object file statistics |
---|
201 | header.tag = GCOV_TAG_OBJECT_SUMMARY; |
---|
202 | header.length = 9; |
---|
203 | objectStats.checksum = 0; // TODO: have no idea hov to calculates it :) |
---|
204 | objectStats.counters = countersFoundSum; |
---|
205 | objectStats.runs = 1; // We are lying for now, we have no means of figuring this out |
---|
206 | objectStats.sum = countersSum; // Sum of all counters |
---|
207 | objectStats.max = countersMax; // max value for counter on last run, we have no clue |
---|
208 | objectStats.sumMax = countersMax; // we have no clue |
---|
209 | |
---|
210 | // Write data |
---|
211 | gcdaFile.write( (char *) &header, sizeof( header ) ); |
---|
212 | if ( gcdaFile.fail() ) { |
---|
213 | std::cerr << "Error while writing stats header to a file " |
---|
214 | << gcdaFileName << std::endl; |
---|
215 | } |
---|
216 | |
---|
217 | gcdaFile.write( (char *) &objectStats, sizeof( objectStats ) ); |
---|
218 | if ( gcdaFile.fail() ) { |
---|
219 | std::cerr << "Error while writing object stats to a file " |
---|
220 | << gcdaFileName << std::endl; |
---|
221 | } |
---|
222 | |
---|
223 | |
---|
224 | // Prepare frame with program statistics |
---|
225 | header.tag = GCOV_TAG_PROGRAM_SUMMARY; |
---|
226 | header.length = 9; |
---|
227 | programStats.checksum = 0; // TODO: have no idea hov to calculate it :) |
---|
228 | programStats.counters = countersFoundSum; |
---|
229 | programStats.runs = 1; // We are lying for now, we have no clue |
---|
230 | programStats.sum = countersSum; // Sum of all counters |
---|
231 | programStats.max = countersMax; // max value for counter on last run, we have no clue |
---|
232 | programStats.sumMax = countersMax; // we have no clue |
---|
233 | |
---|
234 | // Write data |
---|
235 | gcdaFile.write( (char *) &header, sizeof( header ) ); |
---|
236 | if ( gcdaFile.fail() ) { |
---|
237 | std::cerr << "Error while writing stats header to a file " |
---|
238 | << gcdaFileName << std::endl; |
---|
239 | } |
---|
240 | |
---|
241 | gcdaFile.write( (char *) &programStats, sizeof( programStats ) ); |
---|
242 | if ( gcdaFile.fail() ) { |
---|
243 | std::cerr << "Error while writing program stats to a file " |
---|
244 | << gcdaFileName << std::endl; |
---|
245 | } |
---|
246 | |
---|
247 | return true; |
---|
248 | } |
---|
249 | |
---|
250 | bool GcovData::readFrame( std::ifstream& gcovFile ) |
---|
251 | { |
---|
252 | gcov_frame_header header; |
---|
253 | char buffer[512]; |
---|
254 | char intBuffer[16384]; |
---|
255 | uint32_t tempBlockId; |
---|
256 | blocks_iterator_t tempBlockIterator; |
---|
257 | int status; |
---|
258 | |
---|
259 | status = readFrameHeader( &header, gcovFile ); |
---|
260 | if ( status <= 0 ) { |
---|
261 | // Not printing error message because this |
---|
262 | // happenns at the end of each file |
---|
263 | return false; |
---|
264 | } |
---|
265 | |
---|
266 | switch ( header.tag ) { |
---|
267 | |
---|
268 | case GCOV_TAG_FUNCTION: |
---|
269 | |
---|
270 | { |
---|
271 | numberOfFunctions++; |
---|
272 | GcovFunctionData newFunction; |
---|
273 | |
---|
274 | if ( !readFunctionFrame(header, gcovFile, &newFunction) ) { |
---|
275 | std::cerr << "Error while reading FUNCTION from gcov file..." |
---|
276 | << std::endl; |
---|
277 | return false; |
---|
278 | } |
---|
279 | |
---|
280 | functions.push_back( newFunction ); |
---|
281 | } |
---|
282 | |
---|
283 | break; |
---|
284 | |
---|
285 | case GCOV_TAG_BLOCKS: |
---|
286 | |
---|
287 | gcovFile.read( intBuffer, header.length ); |
---|
288 | if ( gcovFile.gcount() != (int) header.length ) { |
---|
289 | std::cerr << "Error while reading BLOCKS from gcov file..." |
---|
290 | << std::endl |
---|
291 | << "Header length is " << header.length |
---|
292 | << " instead of " << gcovFile.gcount() |
---|
293 | << std::endl; |
---|
294 | return false; |
---|
295 | } |
---|
296 | |
---|
297 | for( uint32_t i = 0; i < header.length; i++ ) { |
---|
298 | functions.back().addBlock( i, intBuffer[i], "" ); |
---|
299 | } |
---|
300 | |
---|
301 | break; |
---|
302 | |
---|
303 | case GCOV_TAG_ARCS: |
---|
304 | |
---|
305 | gcovFile.read( intBuffer, header.length ); |
---|
306 | if ( gcovFile.gcount() != (int) header.length ) { |
---|
307 | return false; |
---|
308 | } |
---|
309 | |
---|
310 | for ( int i = 1; i < (int) header.length; i += 2 ) { |
---|
311 | functions.back().addArc(intBuffer[0], intBuffer[i], intBuffer[i+1]); |
---|
312 | } |
---|
313 | |
---|
314 | break; |
---|
315 | |
---|
316 | case GCOV_TAG_LINES: |
---|
317 | |
---|
318 | gcovFile.read( intBuffer, 2 ); |
---|
319 | if ( gcovFile.gcount() != 2 || intBuffer[1] != 0 ) { |
---|
320 | std::cerr << "Error while reading block id for LINES from gcov " |
---|
321 | << "file..." << std::endl; |
---|
322 | return false; |
---|
323 | } |
---|
324 | tempBlockId = intBuffer[0]; |
---|
325 | header.length -= 2; |
---|
326 | |
---|
327 | // Find the right block |
---|
328 | tempBlockIterator =functions.back().findBlockById( tempBlockId ); |
---|
329 | |
---|
330 | header.length -= readString( buffer, gcovFile ); |
---|
331 | functions.back().setBlockFileName( tempBlockIterator, buffer ); |
---|
332 | |
---|
333 | gcovFile.read( intBuffer, header.length ); |
---|
334 | if ( gcovFile.gcount() != (int) header.length ) { |
---|
335 | std::cerr << "Error while reading LINES from gcov file..." |
---|
336 | << std::endl; |
---|
337 | return false; |
---|
338 | } else { |
---|
339 | for ( int i = 0; i < (int) (header.length - 2); i++ ) { |
---|
340 | functions.back().addBlockLine( tempBlockIterator, intBuffer[i] ); |
---|
341 | } |
---|
342 | } |
---|
343 | |
---|
344 | break; |
---|
345 | |
---|
346 | default: |
---|
347 | |
---|
348 | std::cerr << std::endl << std::endl |
---|
349 | << "ERROR - encountered unknown *.gcno tag : 0x" |
---|
350 | << std::hex << header.tag << std::dec << std::endl; |
---|
351 | break; |
---|
352 | } |
---|
353 | |
---|
354 | return true; |
---|
355 | } |
---|
356 | |
---|
357 | int GcovData::readString( char* buffer, std::ifstream& gcovFile ) |
---|
358 | { |
---|
359 | int length; |
---|
360 | |
---|
361 | gcovFile.read( (char *) &length, sizeof( int ) ); |
---|
362 | if ( gcovFile.gcount() != sizeof( int ) ) { |
---|
363 | std::cerr << "ERROR: Unable to read string length from gcov file" |
---|
364 | << std::endl; |
---|
365 | return -1; |
---|
366 | } |
---|
367 | |
---|
368 | gcovFile.read( buffer, length * 4 ); |
---|
369 | if ( gcovFile.gcount() != length * 4 ) { |
---|
370 | std::cerr << "ERROR: Unable to read string from gcov file" |
---|
371 | << std::endl; |
---|
372 | return -1; |
---|
373 | } |
---|
374 | |
---|
375 | buffer[length * 4] = '\0'; |
---|
376 | |
---|
377 | return length +1; |
---|
378 | } |
---|
379 | |
---|
380 | int GcovData::readFrameHeader( |
---|
381 | gcov_frame_header* header, |
---|
382 | std::ifstream& gcovFile |
---|
383 | ) |
---|
384 | { |
---|
385 | int length; |
---|
386 | |
---|
387 | length = sizeof( gcov_frame_header ); |
---|
388 | gcovFile.read( (char *) header, length ); |
---|
389 | if ( gcovFile.gcount() != length ) { |
---|
390 | std::cerr << "ERROR: Unable to read frame header from gcov file" |
---|
391 | << std::endl; |
---|
392 | return -1; |
---|
393 | } |
---|
394 | |
---|
395 | return length / 4; |
---|
396 | } |
---|
397 | |
---|
398 | int GcovData::readFilePreamble( |
---|
399 | gcov_preamble* preamble, |
---|
400 | std::ifstream& gcovFile, |
---|
401 | uint32_t desiredMagic |
---|
402 | ) |
---|
403 | { |
---|
404 | rtems::utils::ostream_guard old_state( std::cerr ); |
---|
405 | |
---|
406 | // Read the gcov preamble and make sure it is the right length and has the |
---|
407 | // magic number |
---|
408 | gcovFile.read( (char *) preamble, sizeof( gcov_preamble ) ); |
---|
409 | if ( gcovFile.gcount() != sizeof( gcov_preamble ) ) { |
---|
410 | std::cerr << "Error while reading file preamble" << std::endl; |
---|
411 | return -1; |
---|
412 | } |
---|
413 | |
---|
414 | if ( preamble->magic != GCNO_MAGIC ) { |
---|
415 | std::cerr << "File is not a valid *.gcno output (magic: 0x" |
---|
416 | << std::hex << std::setw( 4 ) << preamble->magic |
---|
417 | << ")" << std::endl; |
---|
418 | return -1; |
---|
419 | } |
---|
420 | |
---|
421 | return sizeof( gcov_preamble ) / 4; |
---|
422 | } |
---|
423 | |
---|
424 | bool GcovData::readFunctionFrame( |
---|
425 | gcov_frame_header header, |
---|
426 | std::ifstream& gcovFile, |
---|
427 | GcovFunctionData* function |
---|
428 | ) |
---|
429 | { |
---|
430 | char buffer[512]; |
---|
431 | char intBuffer[16384]; |
---|
432 | |
---|
433 | gcovFile.read( (char *) intBuffer, 8 ); |
---|
434 | if ( gcovFile.gcount() != 8 ) { |
---|
435 | std::cerr << "ERROR: Unable to read Function ID & checksum" << std::endl; |
---|
436 | return false; |
---|
437 | } |
---|
438 | |
---|
439 | header.length -= 2; |
---|
440 | function->setId( intBuffer[0] ); |
---|
441 | function->setChecksum( intBuffer[1] ); |
---|
442 | |
---|
443 | header.length -= readString( buffer, gcovFile ); |
---|
444 | function->setFunctionName( buffer, symbolsToAnalyze_m ); |
---|
445 | header.length -= readString( buffer, gcovFile ); |
---|
446 | function->setFileName( buffer ); |
---|
447 | gcovFile.read( (char*) intBuffer, 4 * header.length ); |
---|
448 | if (gcovFile.gcount() != 4 * header.length ) { |
---|
449 | std::cerr << "ERROR: Unable to read Function starting line number" |
---|
450 | << std::endl; |
---|
451 | return false; |
---|
452 | } |
---|
453 | |
---|
454 | function->setFirstLineNumber( intBuffer[0] ); |
---|
455 | |
---|
456 | return true; |
---|
457 | } |
---|
458 | |
---|
459 | bool GcovData::writeReportFile() |
---|
460 | { |
---|
461 | functions_iterator_t currentFunction; |
---|
462 | uint32_t i = 1; //iterator |
---|
463 | std::ofstream textFile; |
---|
464 | |
---|
465 | // Debug message |
---|
466 | // std::cerr << "Writing file: " << textFileName << std::endl; |
---|
467 | |
---|
468 | // Open the data file. |
---|
469 | textFile.open( textFileName ); |
---|
470 | if ( !textFile.is_open() ) { |
---|
471 | std::cerr << "Unable to create " << textFileName << std::endl; |
---|
472 | return false; |
---|
473 | } |
---|
474 | |
---|
475 | printGcnoFileInfo( textFile ); |
---|
476 | |
---|
477 | for ( |
---|
478 | currentFunction = functions.begin(); |
---|
479 | currentFunction != functions.end(); |
---|
480 | currentFunction++ |
---|
481 | ) { |
---|
482 | (*currentFunction).printFunctionInfo( textFile, i ); |
---|
483 | (*currentFunction).printCoverageInfo( textFile, i ); |
---|
484 | i++; |
---|
485 | } |
---|
486 | |
---|
487 | return true; |
---|
488 | } |
---|
489 | |
---|
490 | void GcovData::printGcnoFileInfo( std::ofstream& textFile ) |
---|
491 | { |
---|
492 | textFile << std::endl << "FILE:\t\t\t" << gcnoFileName |
---|
493 | << std::endl << std::hex |
---|
494 | << "magic:\t\t\t" << gcnoPreamble.magic << std::endl |
---|
495 | << "version:\t\t" << gcnoPreamble.version << std::endl |
---|
496 | << "timestamp:\t\t" << gcnoPreamble.timestamp << std::endl |
---|
497 | << std::dec |
---|
498 | << "functions found: \t" |
---|
499 | << std::endl << std::endl << gcnoPreamble.timestamp |
---|
500 | << std::endl << std::endl; |
---|
501 | } |
---|
502 | |
---|
503 | void GcovData::writeGcovFile() |
---|
504 | { |
---|
505 | //std::cerr << "Attempting to run gcov for: " << cFileName << std::endl; |
---|
506 | std::ostringstream command; |
---|
507 | |
---|
508 | command << "( cd " << rld::path::dirname( cFileName ) |
---|
509 | << " && gcov " << rld::path::basename( cFileName ) |
---|
510 | << " &>> gcov.log)"; |
---|
511 | //std::cerr << "> " << command << std::endl; |
---|
512 | system( command.str().c_str() ); |
---|
513 | } |
---|
514 | |
---|
515 | bool GcovData::processCounters() |
---|
516 | { |
---|
517 | functions_iterator_t currentFunction; |
---|
518 | bool status = true; |
---|
519 | |
---|
520 | for ( |
---|
521 | currentFunction = functions.begin(); |
---|
522 | currentFunction != functions.end(); |
---|
523 | currentFunction++ |
---|
524 | ) { |
---|
525 | if ( !(*currentFunction).processFunctionCounters() ) { |
---|
526 | status = false; |
---|
527 | } |
---|
528 | } |
---|
529 | |
---|
530 | return status; |
---|
531 | } |
---|
532 | } |
---|