1 /*
2  * Copyright (C) by Argonne National Laboratory
3  *     See COPYRIGHT in top-level directory
4  */
5 
6 /*
7  * This is a *very* simple tool for basic coverage analysis.
8  * This is intended as a stop-gap until gcov works with the C++ files
9  * used in the MPICH binding of C++ (as of 2/23/2004, gcov aborts when
10  * processing the coverage files produced by g++ for the MPICH C++
11  * binding).
12  */
13 
14 #include <iostream.h>
15 #include <fstream.h>
16 #include <string.h>
17 
18 #include "mpicovsimple.h"
19 
20 /* For remove */
21 #include <stdio.h>
22 
23 // Find the matching entry or insert and initialize a new one
findOrInsert(const char name[],int argcount)24 covinfo * MPIX_Coverage::findOrInsert( const char name[], int argcount )
25 {
26     covinfo *p = head, *newp;
27     int cmp;
28     static covinfo *lastp = 0;    // We use lastp to start where we
29                                   // left off if we can; this speeds
30                                   // sorted inserts
31 
32     // See if we can skip ahead...
33     if (lastp) {
34 	cmp = strcmp( lastp->name, name );
35 	if (cmp < 0 || (cmp == 0 && lastp->argcount <= argcount )) p = lastp;
36     }
37 
38     while (p) {
39 	cmp = strcmp( p->name, name );
40 	if (cmp == 0) {
41 	    cmp = p->argcount - argcount;
42 	    if (cmp == 0) {
43 		// If still 0, we've found our match.
44 		lastp = p;
45 		return p;
46 	    }
47 	}
48 
49 	if (cmp > 0) {
50 	    // If we got here, backup and exit to perform the insert
51 	    p = p->bLink;
52 	    break;
53 	}
54 	// If we're at the end of the list, this p is the last element,
55 	// so we exit now
56 	if (!p->fLink) break;
57 
58 	p = p->fLink;
59     }
60 
61     // If we got here, we need to insert after p
62     newp             = new covinfo;
63     newp->name       = new char [strlen(name)+1];
64     strcpy( newp->name, name );
65     newp->argcount   = argcount;
66     newp->count      = 0;
67     newp->sourceFile = 0;
68     newp->firstLine  = -1;
69     newp->lastLine   = -1;
70     newp->bLink      = p;
71     if (p) {
72 	// insert after p
73 	newp->fLink = p->fLink;
74 	p->fLink    = newp;
75 	if (newp->fLink) {
76 	    newp->fLink->bLink = newp;
77 	}
78     }
79     else {
80 	// insert at the head of the list
81 	newp->fLink = head;
82 	if (head) {
83 	    head->bLink = newp;
84 	}
85 	head        = newp;
86     }
87 
88     lastp = newp;
89     return newp;
90 }
91 
92 // Add an item to the coverage list, including the first line
Add(const char name[],int argcnt,const char file[],int line)93 void MPIX_Coverage::Add( const char name[], int argcnt,
94 		    const char file[], int line )
95 {
96     covinfo *p = findOrInsert( name, argcnt );
97 
98     p->count ++;
99     if (!p->sourceFile) {
100 	p->sourceFile = new char [strlen(file)+1];
101 	strcpy( p->sourceFile, file );
102 	p->firstLine = line;
103     }
104 }
105 
106 // Add the last line value to an item in the coverage list
AddEnd(const char name[],int argcnt,const char file[],int line)107 void MPIX_Coverage::AddEnd( const char name[], int argcnt,
108 		       const char file[], int line )
109 {
110     covinfo *p = findOrInsert( name, argcnt );
111 
112     if (p->lastLine < 0)
113 	p->lastLine = line;
114 }
115 
116 //
117 // Merge the coverage data with the data in the file
118 // The directory for the coverage file is defined by the
119 // cpp value COVERAGE_DIR
FileMerge(const char filename[])120 int MPIX_Coverage::FileMerge( const char filename[] )
121 {
122     ifstream infile;
123     ofstream outfile;
124     covinfo *p;
125     covinfo fp;
126     char *infilename, *tmpfilename;
127 
128     // Try to open the input file.
129     {
130 #ifdef COVERAGE_DIR
131 	infilename = new char [strlen(filename) + strlen(COVERAGE_DIR) + 2 ];
132 	strcpy( infilename, COVERAGE_DIR );
133 	strcat( infilename, "/" );
134 	strcat( infilename, filename );
135 #else
136 	infilename = (char *)filename;
137 #endif
138 	infile.open( infilename );
139 
140 	// Create a place in which to read the data
141 	fp.name       = new char [1024];
142 	fp.sourceFile = new char [1024];
143 
144 	// Add the contents of the file to the internal list
145 	// This is easy but not the most memory efficient.
146 	// If this becomes a problem, we can merge the two
147 	// into a single output
148 	while (infile) {
149 	    fp.count = -1;  // Set a sentinal on eof in infile
150 	    infile >> fp.name >> fp.argcount >> fp.count >> fp.sourceFile >>
151 		fp.firstLine >> fp.lastLine;
152 	    if (fp.count == -1) break;
153 	    p = findOrInsert( fp.name, fp.argcount );
154 	    if (!p->sourceFile) {
155 		p->sourceFile = strdup( fp.sourceFile );
156 		p->firstLine  = fp.firstLine;
157 		p->lastLine   = fp.lastLine;
158 	    }
159 	    p->count += fp.count;
160 	}
161 	infile.close();
162 	// Recover new storage
163 	delete fp.name;
164 	delete fp.sourceFile;
165     }
166 
167     // Try to open the output file
168 #ifdef COVERAGE_DIR
169     tmpfilename = new char [strlen(".covtmp") + strlen(COVERAGE_DIR) + 2 ];
170     strcpy( tmpfilename, COVERAGE_DIR );
171     strcat( tmpfilename, "/" );
172     strcat( tmpfilename, ".covtmp" );
173 #else
174     tmpfilename = ".covtmp";
175 #endif
176     outfile.open( tmpfilename );
177 
178     p = head;
179     while (p) {
180 	outfile << p->name << '\t' << p->argcount << '\t' <<
181 	    p->count << '\t' << p->sourceFile << '\t' <<
182 	    p->firstLine << '\t' << p->lastLine << '\n';
183 	p = p->fLink;
184     }
185 
186     outfile.close();
187 
188     // Now, remove the old file and move the new file over it
189     remove( infilename );
190     rename( tmpfilename, infilename );
191 
192     return 0;
193 }
194 
195 #if 0
196 // This is partial and incomplete code for an alternative version of
197 // FileMerge that avoids reading the entire file into memory
198 // This was a early version that was lost in a terrible keyboarding
199 // accident just after it was debugged :)
200 // This predates the current form of the coverage file and contains
201 // a number of bugs, but it would be a reasonable starting point
202 // if it is desired to avoid reading the entire file into memory.
203 int MPIX_Ooverage::FileMerge( const char filename[] )
204 {
205     covinfo *p, *fp=0;
206     ifstream infile;
207     ofstream outfile;
208     char *tmpfile;
209 
210     infile.open( filename, ios::in );
211 
212     tmpfile = new char [ strlen(filename) + 5 ];
213     strcpy( tmpfile, filename );
214     strcat( tmpfile, ".tmp" );
215     outfile.open( tmpfile );
216     if (!outfile) {
217 	cerr << "Unable to open " << tmpfile << "\n";
218     }
219     p = head;
220     if (infile) {
221 	fp = new covinfo;
222 	fp->name = new char [1024];
223 	infile >> fp->name >> fp->argcount >> fp->count;
224     }
225     while (p) {
226 	int cmp = 0;
227 	if (fp) {
228 	    cmp = strcmp( fp->name, p->name );
229 	    if (cmp == 0) {
230 		cmp = fp->argcount - p->argcount;
231 	    }
232 	    if (cmp < 0) {
233 		// output the file entry
234 		outfile << fp->name << '\t' << fp->argcount << '\t' <<
235 		    fp->count << '\n';
236 		infile >> fp->name >> fp->argcount >> fp->count;
237 		if (infile.eof()) break;
238 		continue;
239 	    }
240 	    else if (cmp == 0) {
241 		// Increment the p entry
242 		p->count += fp->count;
243 		infile >> fp->name >> fp->argcount >> fp->count;
244 		if (infile.eof()) break;
245 	    }
246 	    // else keep this entry
247 	}
248 	outfile << p->name << '\t' << p->argcount << '\t' << p->count << '\n';
249 	p = p->fLink;
250     }
251     // Read the rest of the file, if any
252     while (infile && !infile.eof()) {
253 	infile >> fp->name >> fp->argcount fp->count;
254 	outfile << fp->name << '\t' << fp->argcount << '\t' <<
255 	    fp->count << '\n';
256     }
257     // Output the rest of the list
258     while (p) {
259 	outfile << p->name << '\t' << p->argcount << '\t' << p->count << '\n';
260 	p = p->fLink;
261     }
262     // Close the input file if we opened it...
263     if (fp) {
264 	infile.close();
265 	... remove code as above
266     }
267 
268 }
269 #endif
270 
271 MPIX_Coverage MPIR_Cov;
272 
273 //#define TEST_PROGRAM
274 #ifdef TEST_PROGRAM
main(int argc,char ** argv)275 int main( int argc, char **argv )
276 {
277     MPIR_Cov.Add( "foo", 2, __FILE__, __LINE__ );
278     MPIR_Cov.Add( "foo", 1, __FILE__, __LINE__ );
279     MPIR_Cov.Add( "bar", 2, __FILE__, __LINE__ );
280 
281     MPIR_Cov.FileMerge( "covtest.dat" );
282     return 0;
283 }
284 #endif
285