// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: verilator_coverage: top implementation // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2016 by Wilson Snyder. This program is free software; you can // redistribute it and/or modify it under the terms of either the GNU // Lesser General Public License Version 3 or the Perl Artistic License // Version 2.0. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* #include "V3Error.h" #include "V3Os.h" #include "VlcOptions.h" #include "VlcTop.h" #include <sys/stat.h> #include <fstream> #include <algorithm> //###################################################################### void VlcTop::readCoverage(const string& filename, bool nonfatal) { UINFO(2,"readCoverage "<<filename<<endl); ifstream is (filename.c_str()); if (!is) { if (!nonfatal) v3fatal("Can't read "<<filename); return; } // Testrun and computrons argument unsupported as yet VlcTest* testp = tests().newTest(filename, 0, 0); while (!is.eof()) { string line; getline(is, line); //UINFO(9," got "<<line<<endl); if (line[0] == 'C') { string::size_type secspace=3; for (; secspace<line.length(); secspace++) { if (line[secspace]=='\'' && line[secspace+1]==' ') break; } string point = line.substr(3,secspace-3); vluint64_t hits = atoll(line.c_str()+secspace+1); //UINFO(9," point '"<<point<<"'"<<" "<<hits<<endl); vluint64_t pointnum = points().findAddPoint(point, hits); if (pointnum) {} // Prevent unused if (opt.rank()) { // Only if ranking - uses a lot of memory if (hits >= VlcBuckets::sufficient()) { points().pointNumber(pointnum).testsCoveringInc(); testp->buckets().addData(pointnum, hits); } } } } } void VlcTop::writeCoverage(const string& filename) { UINFO(2,"writeCoverage "<<filename<<endl); ofstream os (filename.c_str()); if (!os) { v3fatal("Can't write "<<filename); return; } os << "# SystemC::Coverage-3" << endl; for (VlcPoints::ByName::iterator it=m_points.begin(); it!=m_points.end(); ++it) { const VlcPoint& point = m_points.pointNumber(it->second); os <<"C '"<<point.name()<<"' " << point.count()<<endl; } } //******************************************************************** struct CmpComputrons { inline bool operator () (const VlcTest* lhsp, const VlcTest* rhsp) const { if (lhsp->computrons() != rhsp->computrons()) { return lhsp->computrons() < rhsp->computrons(); } return lhsp->bucketsCovered() > rhsp->bucketsCovered(); } }; void VlcTop::rank() { UINFO(2,"rank...\n"); vluint64_t nextrank=1; // Sort by computrons, so fast tests get selected first vector<VlcTest*> bytime; for (VlcTests::ByName::iterator it=m_tests.begin(); it!=m_tests.end(); ++it) { VlcTest* testp = *it; if (testp->bucketsCovered()) { // else no points, so can't help us bytime.push_back(*it); } } sort(bytime.begin(), bytime.end(), CmpComputrons()); // Sort the vector VlcBuckets remaining; for (VlcPoints::ByName::iterator it=m_points.begin(); it!=m_points.end(); ++it) { VlcPoint* pointp = &points().pointNumber(it->second); // If any tests hit this point, then we'll need to cover it. if (pointp->testsCovering()) { remaining.addData(pointp->pointNum(), 1); } } // Additional Greedy algorithm // O(n^2) Ouch. Probably the thing to do is randomize the order of data // then hierarchically solve a small subset of tests, and take resulting // solution and move up to larger subset of tests. (Aka quick sort.) while (1) { if (debug()) { UINFO(9,"Left on iter"<<nextrank<<": "); remaining.dump(); } VlcTest* bestTestp = NULL; vluint64_t bestRemain = 0; for (vector<VlcTest*>::iterator it=bytime.begin(); it!=bytime.end(); ++it) { VlcTest* testp = *it; if (!testp->rank()) { vluint64_t remain = testp->buckets().dataPopCount(remaining); if (remain > bestRemain) { bestTestp = testp; bestRemain = remain; } } } if (VlcTest* testp = bestTestp) { testp->rank(nextrank++); testp->rankPoints(bestRemain); remaining.orData(bestTestp->buckets()); } else { break; // No test covering more stuff found } } } //###################################################################### void VlcTop::annotateCalc() { // Calculate per-line information into filedata structure for (VlcPoints::ByName::iterator it=m_points.begin(); it!=m_points.end(); ++it) { const VlcPoint& point = m_points.pointNumber(it->second); string filename = point.filename(); int lineno = point.lineno(); if (filename!="" && lineno!=0) { int column = point.column(); VlcSource& source = sources().findNewSource(filename); string threshStr = point.thresh(); unsigned thresh = (threshStr!="") ? atoi(threshStr.c_str()) : opt.annotateMin(); bool ok = (point.count() >= thresh); UINFO(9, "AnnoCalc count "<<filename<<" "<<lineno<<" "<<point.count()<<endl); source.incCount(lineno, column, point.count(), ok); } } } void VlcTop::annotateCalcNeeded() { // Compute which files are needed. A file isn't needed if it has appropriate // coverage in all categories int totCases = 0; int totOk = 0; for (VlcSources::NameMap::iterator sit=m_sources.begin(); sit!=m_sources.end(); ++sit) { VlcSource& source = sit->second; //UINFO(1,"Source "<<source.name()<<endl); if (opt.annotateAll()) source.needed(true); VlcSource::LinenoMap& lines = source.lines(); for (VlcSource::LinenoMap::iterator lit=lines.begin(); lit!=lines.end(); ++lit) { VlcSource::ColumnMap& cmap = lit->second; for (VlcSource::ColumnMap::iterator cit=cmap.begin(); cit!=cmap.end(); ++cit) { VlcSourceCount& col = cit->second; //UINFO(0,"Source "<<source.name()<<" lineno="<<col.lineno()<<" col="<<col.column()<<endl); ++totCases; if (col.ok()) { ++totOk; } else { source.needed(true); } } } } float pct = totCases ? (100*totOk / totCases) : 0; cout<<"Total coverage ("<<totOk<<"/"<<totCases<<") " <<fixed<<setw(3)<<setprecision(2)<<pct<<"%"<<endl; if (totOk != totCases) cout<<"See lines with '%00' in "<<opt.annotateOut()<<endl; } void VlcTop::annotateOutputFiles(const string& dirname) { // Create if uncreated, ignore errors V3Os::createDir(dirname); for (VlcSources::NameMap::iterator sit=m_sources.begin(); sit!=m_sources.end(); ++sit) { VlcSource& source = sit->second; if (!source.needed()) continue; string filename = source.name(); string outfilename = dirname+"/"+V3Os::filenameNonDir(filename); UINFO(1,"annotateOutputFile "<<filename<<" -> "<<outfilename<<endl); ifstream is (filename.c_str()); if (!is) { v3error("Can't read "<<filename); return; } ofstream os (outfilename.c_str()); if (!os) { v3fatal("Can't write "<<outfilename); return; } os << "\t// verilator_coverage annotation"<<endl; int lineno = 0; while (!is.eof()) { lineno++; string line; getline(is, line); bool first = true; VlcSource::LinenoMap& lines = source.lines(); VlcSource::LinenoMap::iterator lit=lines.find(lineno); if (lit != lines.end()) { VlcSource::ColumnMap& cmap = lit->second; for (VlcSource::ColumnMap::iterator cit=cmap.begin(); cit!=cmap.end(); ++cit) { VlcSourceCount& col = cit->second; //UINFO(0,"Source "<<source.name()<<" lineno="<<col.lineno()<<" col="<<col.column()<<endl); os<<(col.ok()?" ":"%") <<setfill('0')<<setw(6)<<col.count() <<"\t"<<line<<endl; if (first) { first = false; // Multiple columns on same line; print line just once string indent = ""; for (string::const_iterator pos=line.begin(); pos!=line.end() && isspace(*pos); ++pos) { indent += *pos; } line = indent + "verilator_coverage: (next point on previous line)\n"; } } } if (first) { os<<"\t"<<line<<endl; } } } } void VlcTop::annotate(const string& dirname) { // Calculate per-line information into filedata structure annotateCalc(); annotateCalcNeeded(); annotateOutputFiles(dirname); }