2006-08-26 11:35:28 +00:00
|
|
|
|
// $Id$
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Error handling
|
|
|
|
|
//
|
|
|
|
|
// Code available from: http://www.veripool.com/verilator
|
|
|
|
|
//
|
|
|
|
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2008-01-15 14:29:08 +00:00
|
|
|
|
// Copyright 2003-2008 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
// General Public License or the Perl Artistic License.
|
|
|
|
|
//
|
|
|
|
|
// 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 <stdio.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <string.h>
|
2007-11-02 11:23:03 +00:00
|
|
|
|
#include <set>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include "V3Error.h"
|
|
|
|
|
#ifndef _V3ERROR_NO_GLOBAL_
|
|
|
|
|
# include "V3Ast.h"
|
|
|
|
|
# include "V3Global.h"
|
|
|
|
|
# include "V3Stats.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//======================================================================
|
|
|
|
|
// Statics
|
|
|
|
|
|
|
|
|
|
FileLine FileLine::s_defaultFileLine = FileLine(EmptySecret());
|
|
|
|
|
|
2007-06-12 19:39:10 +00:00
|
|
|
|
int V3Error::s_errCount = 0;
|
|
|
|
|
int V3Error::s_warnCount = 0;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
int V3Error::s_debugDefault = 0;
|
|
|
|
|
ostringstream V3Error::s_errorStr; // Error string being formed
|
|
|
|
|
V3ErrorCode V3Error::s_errorCode = V3ErrorCode::FATAL;
|
|
|
|
|
bool V3Error::s_describedEachWarn[V3ErrorCode::MAX];
|
|
|
|
|
bool V3Error::s_describedWarnings = false;
|
|
|
|
|
bool V3Error::s_pretendError[V3ErrorCode::MAX];
|
|
|
|
|
|
|
|
|
|
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::FIRST_WARN; codei<V3ErrorCode::MAX; codei++) {
|
|
|
|
|
V3ErrorCode code = (V3ErrorCode)codei;
|
|
|
|
|
if (0==strcasecmp(msgp,code.ascii())) {
|
|
|
|
|
m_e = code; return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_e = V3ErrorCode::ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// FileLine class functions
|
|
|
|
|
|
|
|
|
|
void FileLine::lineDirective(const char* textp) {
|
|
|
|
|
// Handle `line directive
|
|
|
|
|
// Skip `line
|
|
|
|
|
while (*textp && isspace(*textp)) textp++;
|
|
|
|
|
while (*textp && !isspace(*textp)) textp++;
|
|
|
|
|
while (*textp && (isspace(*textp) || *textp=='"')) textp++;
|
|
|
|
|
|
|
|
|
|
// Grab linenumber
|
|
|
|
|
const char *ln = textp;
|
|
|
|
|
while (*textp && !isspace(*textp)) textp++;
|
|
|
|
|
if (isdigit(*ln)) {
|
|
|
|
|
this->lineno(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);
|
|
|
|
|
}
|
|
|
|
|
//printf ("PPLINE %d '%s'\n", s_lineno, s_filename.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileLine::warnOff(const string& msg, bool flag) {
|
|
|
|
|
V3ErrorCode code (msg.c_str());
|
2006-10-18 13:45:50 +00:00
|
|
|
|
if (code < V3ErrorCode::FIRST_WARN) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
warnOff(code, flag);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-31 21:17:23 +00:00
|
|
|
|
void FileLine::warnLintOff(bool flag) {
|
|
|
|
|
for (int codei=V3ErrorCode::FIRST_WARN; codei<V3ErrorCode::MAX; codei++) {
|
|
|
|
|
V3ErrorCode code = (V3ErrorCode)codei;
|
|
|
|
|
if (code.lintError()) warnOff(code, flag);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
FileLine* FileLine::copyOrSameFileLine() {
|
|
|
|
|
// Return this, or a copy of this
|
2007-11-30 22:12:53 +00:00
|
|
|
|
// There are often more than one token per line, thus we use the
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// same pointer as long as we're on the same line.
|
|
|
|
|
static FileLine* lastNewp = NULL;
|
|
|
|
|
if (lastNewp && *lastNewp == *this) {
|
|
|
|
|
return lastNewp;
|
|
|
|
|
}
|
|
|
|
|
FileLine* newp = new FileLine(this);
|
|
|
|
|
lastNewp = newp;
|
|
|
|
|
return newp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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::profileFuncname() const {
|
|
|
|
|
// Return string that is OK as a function name - for profiling
|
|
|
|
|
string name = filebasename();
|
|
|
|
|
string::size_type pos;
|
|
|
|
|
if ((pos = name.find(".")) != string::npos) {
|
|
|
|
|
name = name.substr(0,pos);
|
|
|
|
|
}
|
|
|
|
|
while ((pos = name.find_first_not_of("abcdefghijlkmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ0123456789_"))
|
|
|
|
|
!= string::npos) {
|
|
|
|
|
name.replace(pos, 1, "_");
|
|
|
|
|
}
|
|
|
|
|
name += "__"+cvtToStr(lineno());
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string FileLine::ascii() const {
|
|
|
|
|
return filename()+":"+cvtToStr(lineno());
|
|
|
|
|
}
|
|
|
|
|
ostream& operator<<(ostream& os, FileLine* fileline) {
|
|
|
|
|
os <<fileline->ascii()<<": "<<hex;
|
|
|
|
|
return(os);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool FileLine::warnIsOff(V3ErrorCode code) {
|
|
|
|
|
if (m_warnOff.test(code)) return true;
|
|
|
|
|
// UNOPTFLAT implies UNOPT
|
|
|
|
|
if (code==V3ErrorCode::UNOPT && m_warnOff.test(V3ErrorCode::UNOPTFLAT)) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileLine::v3errorEnd(ostringstream& str) {
|
|
|
|
|
if (this && m_lineno) {
|
|
|
|
|
ostringstream nsstr;
|
|
|
|
|
nsstr<<this<<str.str();
|
|
|
|
|
if (warnIsOff(V3Error::errorCode())) V3Error::suppressThisWarning();
|
|
|
|
|
V3Error::v3errorEnd(nsstr);
|
|
|
|
|
} else {
|
|
|
|
|
V3Error::v3errorEnd(str);
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-11-02 11:23:03 +00:00
|
|
|
|
|
|
|
|
|
#ifdef VL_LEAK_CHECKS
|
|
|
|
|
typedef set<FileLine*> FileLineCheckSet;
|
|
|
|
|
FileLineCheckSet fileLineLeakChecks;
|
|
|
|
|
|
|
|
|
|
void* FileLine::operator new(size_t size) {
|
|
|
|
|
FileLine* objp = static_cast<FileLine*>(::operator new(size));
|
|
|
|
|
fileLineLeakChecks.insert(objp);
|
|
|
|
|
return objp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileLine::operator delete(void* objp, size_t size) {
|
|
|
|
|
if (!objp) return;
|
|
|
|
|
FileLine* flp = static_cast<FileLine*>(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();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//######################################################################
|
|
|
|
|
// V3Error class functions
|
|
|
|
|
|
|
|
|
|
void V3Error::init() {
|
|
|
|
|
for (int i=0; i<V3ErrorCode::MAX; i++) {
|
|
|
|
|
s_describedEachWarn[i] = false;
|
2006-10-11 15:41:42 +00:00
|
|
|
|
s_pretendError[i] = V3ErrorCode(i).pretendError();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (string(V3ErrorCode(V3ErrorCode::MAX).ascii()) != " MAX") {
|
|
|
|
|
v3fatalSrc("Enum table in V3ErrorCode::ascii() is munged");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string V3Error::lineStr (const char* filename, int lineno) {
|
|
|
|
|
ostringstream out;
|
|
|
|
|
const char* fnslashp = strrchr (filename, '/');
|
|
|
|
|
if (fnslashp) filename = fnslashp+1;
|
|
|
|
|
out<<filename<<":"<<dec<<lineno<<":";
|
|
|
|
|
const char* spaces = " ";
|
|
|
|
|
int numsp = out.str().length(); if (numsp>20) numsp = 20;
|
|
|
|
|
out<<(spaces + numsp);
|
|
|
|
|
return out.str();
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-12 19:39:10 +00:00
|
|
|
|
void V3Error::incWarnings() {
|
|
|
|
|
s_warnCount++;
|
2007-08-23 13:21:58 +00:00
|
|
|
|
// We don't exit on a lot of warnings.
|
2007-06-12 19:39:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void V3Error::incErrors() {
|
2007-06-12 19:39:10 +00:00
|
|
|
|
s_errCount++;
|
2007-08-23 13:21:58 +00:00
|
|
|
|
if (errorCount() == MAX_ERRORS) { // Not >= as would otherwise recurse
|
2006-08-26 11:35:28 +00:00
|
|
|
|
v3fatal ("Exiting due to too many errors encountered\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Error::abortIfErrors() {
|
|
|
|
|
if (errorCount()) {
|
2007-06-12 19:39:10 +00:00
|
|
|
|
v3fatal ("Exiting due to "<<dec<<errorOrWarnCount()<<" warning(s)\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-12 19:39:10 +00:00
|
|
|
|
void V3Error::abortIfWarnings() {
|
|
|
|
|
if (errorOrWarnCount()) {
|
|
|
|
|
v3fatal ("Exiting due to "<<dec<<errorOrWarnCount()<<" warning(s)\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool V3Error::isError(V3ErrorCode code) {
|
|
|
|
|
if (code==V3ErrorCode::SUPPRESS) return false;
|
|
|
|
|
else if (code==V3ErrorCode::FATAL) return true;
|
|
|
|
|
else if (code==V3ErrorCode::ERROR) return true;
|
|
|
|
|
else if (code<V3ErrorCode::FIRST_WARN
|
|
|
|
|
|| s_pretendError[code]) return true;
|
|
|
|
|
else return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
string V3Error::msgPrefix(V3ErrorCode code) {
|
|
|
|
|
if (code==V3ErrorCode::SUPPRESS) return "-arning-suppressed: ";
|
|
|
|
|
else if (code==V3ErrorCode::FATAL) return "%Error: ";
|
2006-10-18 13:45:50 +00:00
|
|
|
|
else if (code==V3ErrorCode::ERROR) return "%Error: ";
|
2007-06-12 19:39:10 +00:00
|
|
|
|
else if (isError(code)) return "%Error-"+(string)code.ascii()+": ";
|
2006-08-26 11:35:28 +00:00
|
|
|
|
else return "%Warning-"+(string)code.ascii()+": ";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//======================================================================
|
|
|
|
|
// Global Functions
|
|
|
|
|
|
|
|
|
|
string V3Error::v3sform (const char* format, ...) {
|
|
|
|
|
static char msg[1000] = "";
|
|
|
|
|
|
|
|
|
|
va_list ap;
|
|
|
|
|
va_start(ap,format);
|
|
|
|
|
vsprintf(msg,format,ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
string out = msg;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Error::suppressThisWarning() {
|
|
|
|
|
if (s_errorCode>=V3ErrorCode::FIRST_WARN) {
|
|
|
|
|
V3Stats::addStatSum(string("Warnings, Suppressed ")+s_errorCode.ascii(), 1);
|
|
|
|
|
s_errorCode=V3ErrorCode::SUPPRESS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Error::v3abort () {
|
|
|
|
|
v3fatalSrc("v3abort called\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Error::v3errorEnd (ostringstream& sstr) {
|
2006-10-06 16:08:46 +00:00
|
|
|
|
#ifdef __COVERITY__
|
|
|
|
|
if (s_errorCode==V3ErrorCode::FATAL) __coverity_panic__(x);
|
|
|
|
|
#endif
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (s_errorCode!=V3ErrorCode::SUPPRESS || debug()) {
|
|
|
|
|
cerr<<msgPrefix()<<sstr.str();
|
|
|
|
|
if (sstr.str()[sstr.str().length()-1] != '\n') {
|
|
|
|
|
cerr<<endl;
|
|
|
|
|
}
|
|
|
|
|
if (s_errorCode!=V3ErrorCode::SUPPRESS) {
|
|
|
|
|
if (!s_describedEachWarn[s_errorCode]
|
|
|
|
|
&& !s_pretendError[s_errorCode]) {
|
|
|
|
|
s_describedEachWarn[s_errorCode] = true;
|
|
|
|
|
if (s_errorCode>=V3ErrorCode::FIRST_WARN && !s_describedWarnings) {
|
|
|
|
|
cerr<<msgPrefix()<<"Use \"/* verilator lint_off "<<s_errorCode.ascii()
|
|
|
|
|
<<" */\" and lint_on around source to disable this message."<<endl;
|
|
|
|
|
s_describedWarnings = true;
|
|
|
|
|
}
|
|
|
|
|
if (s_errorCode.dangerous()) {
|
|
|
|
|
cerr<<msgPrefix()<<"*** See the manual before disabling this,"<<endl;
|
|
|
|
|
cerr<<msgPrefix()<<"else you may end up with different sim results."<<endl;
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-06-12 19:39:10 +00:00
|
|
|
|
if (isError(s_errorCode)) incErrors();
|
|
|
|
|
else incWarnings();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (s_errorCode==V3ErrorCode::FATAL) {
|
|
|
|
|
static bool inFatal = false;
|
|
|
|
|
if (!inFatal) {
|
|
|
|
|
inFatal = true;
|
|
|
|
|
#ifndef _V3ERROR_NO_GLOBAL_
|
|
|
|
|
if (debug()) {
|
|
|
|
|
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("final.tree",99));
|
|
|
|
|
V3Stats::statsFinalAll(v3Global.rootp());
|
|
|
|
|
V3Stats::statsReport();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2006-10-04 15:46:13 +00:00
|
|
|
|
|
|
|
|
|
if (V3Error::debugDefault()) {
|
|
|
|
|
cerr<<msgPrefix()<<"Aborting since under --debug"<<endl;
|
|
|
|
|
abort();
|
|
|
|
|
} else {
|
|
|
|
|
exit(10);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|