diff --git a/include/verilated.cpp b/include/verilated.cpp index 34f715952..d842d16ce 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -688,10 +688,11 @@ std::string _vl_vsformat_time(char* tmp, double ld, bool left, size_t width) { QData fraction = static_cast(scaled) % fracDiv; int digits = 0; if (!fracDigits) { - digits = sprintf(tmp, "%" VL_PRI64 "u%s", whole, suffix.c_str()); + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u%s", whole, + suffix.c_str()); } else { - digits = sprintf(tmp, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", whole, fracDigits, fraction, - suffix.c_str()); + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u.%0*" VL_PRI64 "u%s", + whole, fracDigits, fraction, suffix.c_str()); } int needmore = width - digits; std::string padding; @@ -791,7 +792,7 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA output += _vl_vsformat_time(t_tmp, d, left, width); } else { std::string fmts(pctp, pos - pctp + 1); - sprintf(t_tmp, fmts.c_str(), d); + VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d); output += t_tmp; } break; @@ -836,8 +837,9 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA int digits = 0; std::string append; if (lbits <= VL_QUADSIZE) { - digits = sprintf(t_tmp, "%" VL_PRI64 "d", - static_cast(VL_EXTENDS_QQ(lbits, lbits, ld))); + digits = VL_SNPRINTF( + t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "d", + static_cast(VL_EXTENDS_QQ(lbits, lbits, ld))); append = t_tmp; } else { if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) { @@ -865,7 +867,8 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA int digits = 0; std::string append; if (lbits <= VL_QUADSIZE) { - digits = sprintf(t_tmp, "%" VL_PRI64 "u", ld); + digits + = VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" VL_PRI64 "u", ld); append = t_tmp; } else { append = VL_DECIMAL_NW(lbits, lwp); @@ -1671,9 +1674,11 @@ const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE { const std::string& match = VerilatedImp::argPlusMatch(prefixp); static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; if (match.empty()) return nullptr; - t_outstr[0] = '\0'; - strncat(t_outstr, match.c_str() + strlen(prefixp) + 1, // +1 to skip the "+" - VL_VALUE_STRING_MAX_WIDTH - 1); + char* dp = t_outstr; + for (const char* sp = match.c_str() + strlen(prefixp) + 1; // +1 to skip the "+" + *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);) + *dp++ = *sp++; + *dp++ = '\0'; return t_outstr; } @@ -2346,14 +2351,16 @@ const char* Verilated::catName(const char* n1, const char* n2, const char* delim static VL_THREAD_LOCAL char* t_strp = nullptr; static VL_THREAD_LOCAL size_t t_len = 0; size_t newlen = strlen(n1) + strlen(n2) + strlen(delimiter) + 1; - if (!t_strp || newlen > t_len) { + if (VL_UNLIKELY(!t_strp || newlen > t_len)) { if (t_strp) delete[] t_strp; t_strp = new char[newlen]; t_len = newlen; } - strcpy(t_strp, n1); - if (*n1) strcat(t_strp, delimiter); - strcat(t_strp, n2); + char* dp = t_strp; + for (const char* sp = n1; *sp;) *dp++ = *sp++; + for (const char* sp = delimiter; *sp;) *dp++ = *sp++; + for (const char* sp = n2; *sp;) *dp++ = *sp++; + *dp++ = '\0'; return t_strp; } @@ -2396,7 +2403,7 @@ void Verilated::runFlushCallbacks() VL_MT_SAFE { #ifdef VL_THREADED static std::atomic s_recursing; #else - int s_recursing = 0; + static int s_recursing = 0; #endif if (!s_recursing++) { const VerilatedLockGuard lock(VlCbStatic.s_flushMutex); @@ -2423,7 +2430,7 @@ void Verilated::runExitCallbacks() VL_MT_SAFE { #ifdef VL_THREADED static std::atomic s_recursing; #else - int s_recursing = 0; + static int s_recursing = 0; #endif if (!s_recursing++) { const VerilatedLockGuard lock(VlCbStatic.s_exitMutex); @@ -2446,8 +2453,10 @@ const char* Verilated::commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE { const std::string& match = VerilatedImp::argPlusMatch(prefixp); static VL_THREAD_LOCAL char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; if (match.empty()) return ""; - t_outstr[0] = '\0'; - strncat(t_outstr, match.c_str(), VL_VALUE_STRING_MAX_WIDTH - 1); + char* dp = t_outstr; + for (const char* sp = match.c_str(); *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);) + *dp++ = *sp++; + *dp++ = '\0'; return t_outstr; } @@ -2712,11 +2721,15 @@ void VerilatedScope::configure(VerilatedSyms* symsp, const char* prefixp, const m_symsp = symsp; m_type = type; m_timeunit = timeunit; - char* namep = new char[strlen(prefixp) + strlen(suffixp) + 2]; - strcpy(namep, prefixp); - if (*prefixp && *suffixp) strcat(namep, "."); - strcat(namep, suffixp); - m_namep = namep; + { + char* namep = new char[strlen(prefixp) + strlen(suffixp) + 2]; + char* dp = namep; + for (const char* sp = prefixp; *sp;) *dp++ = *sp++; + if (*prefixp && *suffixp) *dp++ = '.'; + for (const char* sp = suffixp; *sp;) *dp++ = *sp++; + *dp++ = '\0'; + m_namep = namep; + } m_identifierp = identifier; VerilatedImp::scopeInsert(this); } diff --git a/include/verilated_cov.cpp b/include/verilated_cov.cpp index 8a7c9bdaf..377d5abbe 100644 --- a/include/verilated_cov.cpp +++ b/include/verilated_cov.cpp @@ -134,7 +134,7 @@ private: for (const char* pos = text.c_str(); *pos; ++pos) { if (!isprint(*pos) || *pos == '%' || *pos == '"') { char hex[10]; - sprintf(hex, "%%%02X", pos[0]); + VL_SNPRINTF(hex, 10, "%%%02X", pos[0]); rtn += hex; } else { rtn += *pos; diff --git a/include/verilated_trace_imp.cpp b/include/verilated_trace_imp.cpp index fe4580794..aa4a6c233 100644 --- a/include/verilated_trace_imp.cpp +++ b/include/verilated_trace_imp.cpp @@ -70,7 +70,7 @@ static std::string doubleToTimescale(double value) { else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; } // clang-format on char valuestr[100]; - sprintf(valuestr, "%0.0f%s", value, suffixp); + VL_SNPRINTF(valuestr, 100, "%0.0f%s", value, suffixp); return valuestr; // Gets converted to string, so no ref to stack } diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index c59468167..c379b678a 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -269,7 +269,7 @@ void VerilatedVcd::printStr(const char* str) { void VerilatedVcd::printQuad(vluint64_t n) { char buf[100]; - sprintf(buf, "%" VL_PRI64 "u", n); + VL_SNPRINTF(buf, 100, "%" VL_PRI64 "u", n); printStr(buf); } @@ -347,9 +347,16 @@ void VerilatedVcd::printIndent(int level_change) { void VerilatedVcd::dumpHeader() { printStr("$version Generated by VerilatedVcd $end\n"); - time_t time_str = time(nullptr); printStr("$date "); - printStr(ctime(&time_str)); + { + time_t tick = time(nullptr); + tm ticktm; + VL_LOCALTIME_R(&tick, &ticktm); + constexpr int bufsize = 50; + char buf[bufsize]; + strftime(buf, bufsize, "%c", &ticktm); + printStr(buf); + } printStr(" $end\n"); printStr("$timescale "); @@ -487,11 +494,12 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, decl += wirep; // usually "wire" } - char buf[1000]; - sprintf(buf, " %2d ", bits); + constexpr size_t bufsize = 1000; + char buf[bufsize]; + VL_SNPRINTF(buf, bufsize, " %2d ", bits); decl += buf; if (m_evcd) { - sprintf(buf, "<%u", code); + VL_SNPRINTF(buf, bufsize, "<%u", code); decl += buf; } else { // Add string code to decl @@ -505,7 +513,8 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, // 1 bit values don't have a ' ' separator between value and string code const bool isBit = bits == 1; entryp[0] = ' '; // Separator - std::strcpy(entryp + !isBit, buf); // Code (overwrite separator if isBit) + // Use memcpy as we checked size above, and strcpy is flagged unsafe + std::memcpy(entryp + !isBit, buf, strlen(buf)); // Code (overwrite separator if isBit) entryp[length + !isBit] = '\n'; // Replace '\0' with line termination '\n' // Set length of suffix (used to increment write pointer) entryp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = !isBit + length + 1; @@ -513,12 +522,12 @@ void VerilatedVcd::declare(vluint32_t code, const char* name, const char* wirep, decl += " "; decl += basename; if (array) { - sprintf(buf, "(%d)", arraynum); + VL_SNPRINTF(buf, bufsize, "(%d)", arraynum); decl += buf; hiername += buf; } if (bussed) { - sprintf(buf, " [%d:%d]", msb, lsb); + VL_SNPRINTF(buf, bufsize, " [%d:%d]", msb, lsb); decl += buf; } decl += " $end\n"; @@ -656,8 +665,8 @@ void VerilatedVcd::emitWData(vluint32_t code, const WData* newvalp, int bits) { VL_ATTR_ALWINLINE void VerilatedVcd::emitDouble(vluint32_t code, double newval) { char* wp = m_writep; - // Buffer can't overflow before sprintf; we sized during declaration - sprintf(wp, "r%.16g", newval); + // Buffer can't overflow before VL_SNPRINTF; we sized during declaration + VL_SNPRINTF(wp, m_wrChunkSize, "r%.16g", newval); wp += strlen(wp); finishLine(code, wp); } @@ -770,8 +779,8 @@ void VerilatedVcd::fullTriArray(vluint32_t code, const vluint32_t* newvalp, void VerilatedVcd::fullDouble(vluint32_t code, const double newval) { // cppcheck-suppress invalidPointerCast (*(reinterpret_cast(oldp(code)))) = newval; - // Buffer can't overflow before sprintf; we sized during declaration - sprintf(m_writep, "r%.16g", newval); + // Buffer can't overflow before VL_SNPRINTF; we sized during declaration + VL_SNPRINTF(m_writep, m_wrChunkSize, "r%.16g", newval); m_writep += strlen(m_writep); *m_writep++ = ' '; m_writep = writeCode(m_writep, code); diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 7b25337e6..6814e819e 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -336,8 +336,8 @@ public: virtual const VerilatedRange* rangep() const override { return &(varp()->packed()); } virtual const char* fullname() const override { static VL_THREAD_LOCAL std::string t_out; - char num[20]; - sprintf(num, "%d", m_index); + char num[25]; + VL_SNPRINTF(num, 25, "%d", m_index); t_out = std::string(scopep()->name()) + "." + name() + "[" + num + "]"; return t_out.c_str(); } diff --git a/include/verilatedos.h b/include/verilatedos.h index 55be76d6e..abd61195f 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -481,7 +481,7 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type #endif //========================================================================= -// String related OS-specific functions +// String/time related OS-specific functions #ifdef _MSC_VER # define VL_STRCASECMP _stricmp @@ -489,6 +489,12 @@ typedef unsigned long long vluint64_t; ///< 64-bit unsigned type # define VL_STRCASECMP strcasecmp #endif +#ifdef _MSC_VER +# define VL_LOCALTIME_R(timep, tmp) localtime_c((tmp), (timep)) +#else +# define VL_LOCALTIME_R(timep, tmp) localtime_r((timep), (tmp)) +#endif + //========================================================================= // Macros controlling target specific optimizations diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 31f0b4d42..7be1e7a4d 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -120,9 +120,9 @@ string AstNode::encodeName(const string& namein) { // We also do *NOT* use __DOT__ etc, as we search for those // in some replacements, and don't want to mangle the user's names. unsigned val = pos[0] & 0xff; // Mask to avoid sign extension - char hex[10]; - sprintf(hex, "__0%02X", val); - out += hex; + std::stringstream hex; + hex << std::setfill('0') << std::setw(2) << std::hex << val; + out += "__0" + hex.str(); } } // Shorten names diff --git a/src/V3File.cpp b/src/V3File.cpp index a9be42ed6..fbcd02eac 100644 --- a/src/V3File.cpp +++ b/src/V3File.cpp @@ -873,9 +873,7 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L } else if (isprint(c)) { out += c; } else { - char decimal[10]; - sprintf(decimal, "&#%u;", (unsigned char)c); - out += decimal; + out += string("&#") + cvtToStr((unsigned int)(c & 0xff)) + ";"; } } } else { @@ -893,9 +891,8 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L out += c; } else { // This will also cover \a etc - // Can't use %03o as messes up when signed - char octal[10]; - sprintf(octal, "\\%o%o%o", (c >> 6) & 3, (c >> 3) & 7, c & 7); + string octal = string("\\") + cvtToStr((c >> 6) & 3) + cvtToStr((c >> 3) & 7) + + cvtToStr(c & 7); out += octal; } } @@ -907,10 +904,11 @@ string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::L // Simple wrappers void V3OutFormatter::printf(const char* fmt...) { - char sbuff[5000]; + constexpr size_t bufsize = 5000; + char sbuff[bufsize]; va_list ap; va_start(ap, fmt); - vsprintf(sbuff, fmt, ap); + VL_VSNPRINTF(sbuff, bufsize, fmt, ap); va_end(ap); this->puts(sbuff); } diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index 5fae3ce6a..de02e9026 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -165,11 +165,8 @@ string FileLine::xmlDetailedLocation() const { } string FileLine::lineDirectiveStrg(int enterExit) const { - char numbuf[20]; - sprintf(numbuf, "%d", lastLineno()); - char levelbuf[20]; - sprintf(levelbuf, "%d", enterExit); - return (string("`line ") + numbuf + " \"" + filename() + "\" " + levelbuf + "\n"); + return std::string("`line ") + cvtToStr(lastLineno()) + " \"" + filename() + "\" " + + cvtToStr(enterExit) + "\n"; } void FileLine::lineDirective(const char* textp, int& enterExitRef) { diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 73115a7b3..73e78711a 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -80,6 +80,18 @@ void V3Global::readFiles() { } } +string V3Global::debugFilename(const string& nameComment, int newNumber) { + ++m_debugFileNumber; + if (newNumber) m_debugFileNumber = newNumber; + return opt.hierTopDataDir() + "/" + opt.prefix() + "_" + digitsFilename(m_debugFileNumber) + + "_" + nameComment; +} +string V3Global::digitsFilename(int number) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(3) << number; + return ss.str(); +} + void V3Global::dumpCheckGlobalTree(const string& stagename, int newNumber, bool doDump) { v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(stagename + ".tree", newNumber), false, doDump); diff --git a/src/V3Global.h b/src/V3Global.h index f2b028038..693b213a3 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -140,13 +140,8 @@ public: void widthMinUsage(const VWidthMinUsage& flag) { m_widthMinUsage = flag; } bool constRemoveXs() const { return m_constRemoveXs; } void constRemoveXs(bool flag) { m_constRemoveXs = flag; } - string debugFilename(const string& nameComment, int newNumber = 0) { - ++m_debugFileNumber; - if (newNumber) m_debugFileNumber = newNumber; - char digits[100]; - sprintf(digits, "%03d", m_debugFileNumber); - return opt.hierTopDataDir() + "/" + opt.prefix() + "_" + digits + "_" + nameComment; - } + string debugFilename(const string& nameComment, int newNumber = 0); + static string digitsFilename(int number); bool needHeavy() const { return m_needHeavy; } void needHeavy(bool flag) { m_needHeavy = flag; } bool needTraceDumper() const { return m_needTraceDumper; } diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 91a52ece2..b6bd7937b 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -696,7 +696,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { case 'g': case '^': { // Realtime char tmp[MAX_SPRINTF_DOUBLE_SIZE]; - sprintf(tmp, vformat.c_str(), toDouble()); + VL_SNPRINTF(tmp, MAX_SPRINTF_DOUBLE_SIZE, vformat.c_str(), toDouble()); return tmp; } // 'l' // Library - converted to text by V3LinkResolve diff --git a/src/V3PreLex.l b/src/V3PreLex.l index dbe22e3f6..f1c37cd94 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -125,8 +125,8 @@ bom [\357\273\277] rtnfile += '"'; yytext = (char*)rtnfile.c_str(); yyleng = rtnfile.length(); return VP_STRING; } "`__LINE__" { FL_FWDC; - static char buf[10]; - sprintf(buf, "%d", LEXP->curFilelinep()->lastLineno()); + static char buf[25]; + VL_SNPRINTF(buf, 25, "%d", LEXP->curFilelinep()->lastLineno()); yytext = buf; yyleng = strlen(yytext); return VP_TEXT; } diff --git a/src/V3StatsReport.cpp b/src/V3StatsReport.cpp index 0eb3b9961..530a06901 100644 --- a/src/V3StatsReport.cpp +++ b/src/V3StatsReport.cpp @@ -208,9 +208,7 @@ void V3Stats::statsStage(const string& name) { static double lastWallTime = -1; static int fileNumber = 0; - char digits[100]; - sprintf(digits, "%03d", ++fileNumber); - const string digitName = string(digits) + "_" + name; + const string digitName = V3Global::digitsFilename(++fileNumber) + "_" + name; double wallTime = V3Os::timeUsecs() / 1.0e6; if (lastWallTime < 0) lastWallTime = wallTime; diff --git a/test_regress/t/t_dpi_open_elem_c.cpp b/test_regress/t/t_dpi_open_elem_c.cpp index 72a8793a4..01ee72427 100644 --- a/test_regress/t/t_dpi_open_elem_c.cpp +++ b/test_regress/t/t_dpi_open_elem_c.cpp @@ -83,7 +83,6 @@ static void _dpii_bit_elem_ux(int p, int u, const svOpenArrayHandle i, const svO #ifndef NC // NC always returns zero and warns CHECK_RESULT_HEX(dim, u); - // svSizeOfArray(i) undeterministic as not in C representation #endif for (int a = svLow(i, 1); a <= svHigh(i, 1); ++a) { @@ -135,13 +134,14 @@ void dpii_bit_elem_p0_u3(int p, int u, const svOpenArrayHandle i, const svOpenAr static void _dpii_logic_elem_ux(int p, int u, const svOpenArrayHandle i, const svOpenArrayHandle o, const svOpenArrayHandle q) { - int sizeInputOfArray = svSizeOfArray(i); int dim = svDimensions(i); #ifndef NC // NC always returns zero and warns CHECK_RESULT_HEX(dim, u); - // svSizeOfArray(i) undeterministic as not in C representation #endif + int sizeInputOfArray = svSizeOfArray(i); + // svSizeOfArray(i) undeterministic as not in C representation + if (sizeInputOfArray) {} for (int a = svLow(i, 1); a <= svHigh(i, 1); ++a) { if (dim == 1) { diff --git a/test_regress/t/t_dpi_open_oob_bad_c.cpp b/test_regress/t/t_dpi_open_oob_bad_c.cpp index 5ed9853c0..ddd9e821a 100644 --- a/test_regress/t/t_dpi_open_oob_bad_c.cpp +++ b/test_regress/t/t_dpi_open_oob_bad_c.cpp @@ -107,9 +107,13 @@ void dpii_int_u3(const svOpenArrayHandle i) { CHECK_RESULT_HEX_NE(ip, 0); // Out of bounds ip = (intptr_t)svGetArrElemPtr3(i, 1, 2, 30); + CHECK_RESULT_HEX(ip, 0); ip = (intptr_t)svGetArrElemPtr3(i, 1, 20, 3); + CHECK_RESULT_HEX(ip, 0); ip = (intptr_t)svGetArrElemPtr3(i, 10, 2, 3); + CHECK_RESULT_HEX(ip, 0); ip = (intptr_t)svGetArrElemPtr1(i, 30); + CHECK_RESULT_HEX(ip, 0); } void dpii_real_u1(const svOpenArrayHandle i) { diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index d9ec774b2..9ddd720e2 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -498,7 +498,7 @@ int _mon_check_putget_str(p_cb_data cb_data) { static TestVpiHandle cb; static struct { TestVpiHandle scope, sig, rfr, check, verbose; - char str[128 + 1]; // char per bit plus null terminator + std::string str; int type; // value type in .str union { PLI_INT32 integer; @@ -532,10 +532,10 @@ int _mon_check_putget_str(p_cb_data cb_data) { vpi_get_value(data[i].sig, &v); TEST_MSG("%s\n", v.value.str); if (data[i].type) { - CHECK_RESULT_CSTR(v.value.str, data[i].str); + CHECK_RESULT_CSTR(v.value.str, data[i].str.c_str()); } else { data[i].type = v.format; - strcpy(data[i].str, v.value.str); + data[i].str = std::string(v.value.str); } } @@ -556,7 +556,7 @@ int _mon_check_putget_str(p_cb_data cb_data) { if (callback_count_strs & 7) { // put same value back - checking encoding/decoding equivalent v.format = data[i].type; - v.value.str = data[i].str; + v.value.str = (PLI_BYTE8*)(data[i].str.c_str()); // Can't reinterpret_cast vpi_put_value(data[i].sig, &v, &t, vpiNoDelay); v.format = vpiIntVal; v.value.integer = 1;