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