// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Error handling // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2014 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 #include #include #include #include "V3Error.h" #ifndef _V3ERROR_NO_GLOBAL_ # include "V3Ast.h" # include "V3Global.h" # include "V3Stats.h" # include "V3Config.h" #endif //====================================================================== // Statics int V3Error::s_errCount = 0; int V3Error::s_warnCount = 0; int V3Error::s_debugDefault = 0; int V3Error::s_tellManual = 0; ostringstream V3Error::s_errorStr; // Error string being formed V3ErrorCode V3Error::s_errorCode = V3ErrorCode::EC_FATAL; bool V3Error::s_errorSuppressed = false; bool V3Error::s_describedEachWarn[V3ErrorCode::_ENUM_MAX]; bool V3Error::s_describedWarnings = false; bool V3Error::s_pretendError[V3ErrorCode::_ENUM_MAX]; V3Error::MessagesSet V3Error::s_messages; V3Error::ErrorExitCb V3Error::s_errorExitCb = NULL; struct v3errorIniter { v3errorIniter() { V3Error::init(); } }; v3errorIniter v3errorInit; //###################################################################### // ErrorCode class functions V3ErrorCode::V3ErrorCode(const char* msgp) { // Return error encoding for given string, or ERROR, which is a bad code for (int codei=V3ErrorCode::EC_MIN; codei 64 bit number char out[size]; char* op = out+size-1; *--op = '\0'; // We build backwards int num = no; do { *--op = 'a'+num%26; num /= 26; } while (num); return op; } //! Convert filenames to a filenameno //! This lets us assign a nice small identifier for debug messages, but more //! importantly lets us use a 4 byte int instead of 8 byte pointer in every //! FileLine. //! We associate a language with each source file, so we also set the default //! for this. int FileLineSingleton::nameToNumber(const string& filename) { FileNameNumMap::const_iterator it = m_namemap.find(filename); if (VL_LIKELY(it != m_namemap.end())) return it->second; int num = m_names.size(); m_names.push_back(filename); m_languages.push_back(V3LangCode::mostRecent()); m_namemap.insert(make_pair(filename,num)); return num; } //! Support XML output //! Experimental. Updated to also put out the language. void FileLineSingleton::fileNameNumMapDumpXml(ostream& os) { os<<"\n"; for (FileNameNumMap::const_iterator it = m_namemap.begin(); it != m_namemap.end(); ++it) { os<<"second) <<"\" filename=\""<first <<"\" language=\""<second).ascii()<<"\"/>\n"; } os<<"\n"; } //###################################################################### // FileLine class functions FileLine::FileLine(FileLine::EmptySecret) { // Sort of a singleton m_lineno=0; m_filenameno=singleton().nameToNumber("AstRoot"); m_warnOn=0; for (int codei=V3ErrorCode::EC_MIN; codeilineno(atoi(ln)); } while (*textp && (isspace(*textp) || *textp=='"')) textp++; // Grab filename const char *fn = textp; while (*textp && !(isspace(*textp) || *textp=='"')) textp++; if (textp != fn) { string strfn = fn; strfn = strfn.substr(0, textp-fn); this->filename(strfn); } // Grab level while (*textp && (isspace(*textp) || *textp=='"')) textp++; if (isdigit(*textp)) enterExitRef = atoi(textp); else enterExitRef = 0; //printf ("PPLINE %d '%s'\n", s_lineno, s_filename.c_str()); } FileLine* FileLine::copyOrSameFileLine() { // When a fileline is "used" to produce a node, calls this function. // Return this, or a copy of this // There are often more than one token per line, thus we use the // same pointer as long as we're on the same line, file & warn state. #ifndef _V3ERROR_NO_GLOBAL_ V3Config::applyIgnores(this); // Toggle warnings based on global config file #endif static FileLine* lastNewp = NULL; if (lastNewp && *lastNewp == *this) { // Compares lineno, filename, etc return lastNewp; } FileLine* newp = new FileLine(this); lastNewp = newp; return newp; } void FileLine::updateLanguage () { language(v3Global.opt.fileLanguage(filename())); } const string FileLine::filebasename() const { string name = filename(); string::size_type pos; if ((pos = name.rfind("/")) != string::npos) { name.erase(0,pos+1); } return name; } const string FileLine::filebasenameNoExt() const { string name = filebasename(); string::size_type pos; if ((pos = name.find(".")) != string::npos) { name = name.substr(0,pos); } return name; } const string FileLine::profileFuncname() const { // Return string that is OK as a function name - for profiling string name = filebasenameNoExt(); string::size_type pos; while ((pos = name.find_first_not_of("abcdefghijlkmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789_")) != string::npos) { name.replace(pos, 1, "_"); } name += "__l"+cvtToStr(lineno()); return name; } string FileLine::ascii() const { return filename()+":"+cvtToStr(lineno()); } ostream& operator<<(ostream& os, FileLine* fileline) { os <ascii()<<": "<warnIsOff(code)) { this->warnOff(code, true); } } } void FileLine::v3errorEnd(ostringstream& str) { if (this && m_lineno) { ostringstream nsstr; nsstr< FileLineCheckSet; FileLineCheckSet fileLineLeakChecks; void* FileLine::operator new(size_t size) { FileLine* objp = static_cast(::operator new(size)); fileLineLeakChecks.insert(objp); return objp; } void FileLine::operator delete(void* objp, size_t size) { if (!objp) return; FileLine* flp = static_cast(objp); FileLineCheckSet::iterator it = fileLineLeakChecks.find(flp); if (it != fileLineLeakChecks.end()) { fileLineLeakChecks.erase(it); } else { flp->v3fatalSrc("Deleting FileLine object that was never tracked\n"); } ::operator delete(objp); } #endif void FileLine::deleteAllRemaining() { #ifdef VL_LEAK_CHECKS // FileLines are allocated, but never nicely freed, as it's much faster // that way. Unfortunately this makes our leak checking a big mess, so // only when leak checking we'll track them all and cleanup. while (1) { FileLineCheckSet::iterator it=fileLineLeakChecks.begin(); if (it==fileLineLeakChecks.end()) break; delete *it; // Operator delete will remove the iterated object from the list. // Eventually the list will be empty and terminate the loop. } fileLineLeakChecks.clear(); singleton().clear(); #endif } //###################################################################### // V3Error class functions void V3Error::init() { for (int i=0; i20) numsp = 20; out<<(spaces + numsp); return out.str(); } void V3Error::incErrors() { s_errCount++; if (errorCount() == v3Global.opt.errorLimit()) { // Not >= as would otherwise recurse v3fatal ("Exiting due to too many errors encountered; --error-limit="<=V3ErrorCode::EC_MIN) { V3Stats::addStatSum(string("Warnings, Suppressed ")+s_errorCode.ascii(), 1); s_errorSuppressed = true; } } string V3Error::warnMore() { return msgPrefix(); } void V3Error::v3errorEnd (ostringstream& sstr) { #if defined(__COVERITY__) || defined(__cppcheck__) if (s_errorCode==V3ErrorCode::EC_FATAL) __coverity_panic__(x); #endif // Skip suppressed messages if (s_errorSuppressed // On debug, show only non default-off warning to prevent pages of warnings && (!debug() || s_errorCode.defaultsOff())) return; string msg = msgPrefix()+sstr.str(); if (msg[msg.length()-1] != '\n') msg += '\n'; // Suppress duplicates if (s_messages.find(msg) != s_messages.end()) return; s_messages.insert(msg); // Output cerr<=V3ErrorCode::EC_FIRST_WARN && !s_describedWarnings) { cerr<dumpTreeFile(v3Global.debugFilename("final.tree",990)); if (s_errorExitCb) s_errorExitCb(); V3Stats::statsFinalAll(v3Global.rootp()); V3Stats::statsReport(); } #endif } vlAbort(); } else if (isError(s_errorCode, s_errorSuppressed)) { // We don't dump tree on any error because a Visitor may be in middle of // a tree cleanup and cause a false broken problem. if (s_errorExitCb) s_errorExitCb(); } } }