From 57d8bb5d1fe3575a378d8d518e9a34c402bc3433 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 10 Sep 2025 18:20:19 +0100 Subject: [PATCH] Internals: Always attempt to release resources on termination (#6416) Replace std::exit with v3Global.exit, and make V3Error::vlAbort call v3Global.shutdown. This gives us an opportunity to release resources to facilitate leak checking even when exiting early on an error. Note we still don't release most resources by default without VL_LEAK_CHECKS, so there is no behaviour change there. --- src/V3Error.cpp | 21 ++++++++++++--------- src/V3Error.h | 8 ++------ src/V3Global.cpp | 8 ++++++++ src/V3Global.h | 2 ++ src/V3Options.cpp | 16 ++++++++-------- src/Verilator.cpp | 12 +++++------- 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 662625feb..300437768 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -97,19 +97,19 @@ void V3ErrorGuarded::vlAbortOrExit() VL_REQUIRES(m_mutex) { std::cerr << msgPrefix() << "Aborting since under --debug" << endl; V3Error::vlAbort(); } -#ifndef V3ERROR_NO_GLOBAL_ - else if (v3Global.opt.verilateJobs() > 1 - && v3Global.mainThreadId() != std::this_thread::get_id()) { - VL_GCOV_DUMP(); // No static destructors are called, thus must be called manually. - // Exit without triggering any global destructors. - // Used to prevent detached V3ThreadPool jobs accessing destroyed static objects. +#ifndef V3ERROR_NO_GLOBAL_ + if (v3Global.opt.verilateJobs() > 1 && v3Global.mainThreadId() != std::this_thread::get_id()) { + // No static destructors are called, thus must be called manually. + VL_GCOV_DUMP(); + // Exit without triggering any global destructors. Used to prevent + // detached V3ThreadPool jobs accessing destroyed static objects. ::_exit(1); } + v3Global.vlExit(1); +#else + std::exit(1); #endif - else { - std::exit(1); - } } string V3ErrorGuarded::warnMoreSpaces() VL_REQUIRES(m_mutex) { @@ -369,6 +369,9 @@ void V3Error::abortIfWarnings() { void V3Error::vlAbort() { VL_GCOV_DUMP(); +#ifndef V3ERROR_NO_GLOBAL_ + v3Global.shutdown(); +#endif std::abort(); } std::ostringstream& V3Error::v3errorPrep(V3ErrorCode code) VL_ACQUIRE(s().m_mutex) { diff --git a/src/V3Error.h b/src/V3Error.h index 008b70900..b945a3926 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -459,12 +459,8 @@ public: // ###################################################################### class V3Error final { - // Base class for any object that wants debugging and error reporting - // CONSTRUCTORS - V3Error() { - std::cerr << ("Static class"); - V3Error::vlAbort(); - } + // Static members only + V3Error() = delete; public: static V3ErrorGuarded& s() VL_MT_SAFE { // Singleton diff --git a/src/V3Global.cpp b/src/V3Global.cpp index a3e208d4d..1c60a2d76 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -24,6 +24,7 @@ #include "V3HierBlock.h" #include "V3LinkCells.h" #include "V3Parse.h" +#include "V3PreShell.h" #include "V3Stats.h" #include "V3ThreadPool.h" @@ -53,11 +54,18 @@ void V3Global::boot() { } void V3Global::shutdown() { + V3PreShell::shutdown(); VL_DO_CLEAR(delete m_hierPlanp, m_hierPlanp = nullptr); // delete nullptr is safe VL_DO_CLEAR(delete m_threadPoolp, m_threadPoolp = nullptr); // delete nullptr is safe #ifdef VL_LEAK_CHECKS if (m_rootp) VL_DO_CLEAR(m_rootp->deleteTree(), m_rootp = nullptr); #endif + FileLine::deleteAllRemaining(); +} + +void V3Global::vlExit(int status) { + shutdown(); + std::exit(status); } void V3Global::checkTree() const { rootp()->checkTree(); } diff --git a/src/V3Global.h b/src/V3Global.h index 157a90eef..3c4e502ff 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -147,6 +147,8 @@ public: void boot(); void shutdown(); // Release allocated resources + void vlExit(int status); + // ACCESSORS (general) AstNetlist* rootp() const VL_MT_SAFE { return m_rootp; } V3ThreadPool* threadPoolp() const VL_PURE { return m_threadPoolp; } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index ccc4b1d03..08e0a8596 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1272,9 +1272,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-debugi-", CbPartialMatchVal, [this](const char* optp, const char* valp) { m_debugLevel[optp] = std::atoi(valp); }); - DECL_OPTION("-debug-abort", CbCall, - V3Error::vlAbort) - .undocumented(); // See also --debug-sigsegv + DECL_OPTION("-debug-abort", CbCall, []() { + V3Error::vlAbort(); + }).undocumented(); // See also --debug-sigseg DECL_OPTION("-debug-check", OnOff, &m_debugCheck); DECL_OPTION("-debug-collision", OnOff, &m_debugCollision).undocumented(); DECL_OPTION("-debug-emitv", OnOff, &m_debugEmitV).undocumented(); @@ -1392,15 +1392,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-gdbbt", CbCall, []() {}); // Processed only in bin/verilator shell DECL_OPTION("-generate-key", CbCall, [this]() { cout << protectKeyDefaulted() << endl; - std::exit(0); + v3Global.vlExit(0); }); DECL_OPTION("-getenv", CbVal, [](const char* valp) { cout << V3Options::getenvBuiltins(valp) << endl; - std::exit(0); + v3Global.vlExit(0); }); DECL_OPTION("-get-supported", CbVal, [](const char* valp) { cout << V3Options::getSupported(valp) << endl; - std::exit(0); + v3Global.vlExit(0); }); DECL_OPTION("-hierarchical", OnOff, &m_hierarchical); @@ -1719,7 +1719,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-V", CbCall, [this]() { showVersion(true); - std::exit(0); + v3Global.vlExit(0); }); DECL_OPTION("-v", CbVal, [this, &optdir](const char* valp) { V3Options::addLibraryFile(parseFileArg(optdir, valp), work()); @@ -1739,7 +1739,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-version", CbCall, [this]() { showVersion(false); - std::exit(0); + v3Global.vlExit(0); }); DECL_OPTION("-vpi", OnOff, &m_vpi); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index fbd1648e2..7479b269e 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -147,7 +147,7 @@ static void process() { V3Error::abortIfErrors(); if (v3Global.opt.debugExitParse()) { cout << "--debug-exit-parse: Exiting after parse\n"; - std::exit(0); + v3Global.vlExit(0); } // Convert parseref's to varrefs, and other directly post parsing fixups @@ -173,7 +173,7 @@ static void process() { V3Error::abortIfErrors(); if (v3Global.opt.serializeOnly()) emitXmlOrJson(); cout << "--debug-exit-uvm23: Exiting after UVM-supported pass\n"; - std::exit(0); + v3Global.vlExit(0); } // Remove parameters by cloning modules to de-parameterized versions @@ -204,7 +204,7 @@ static void process() { V3Error::abortIfErrors(); if (v3Global.opt.serializeOnly()) emitXmlOrJson(); cout << "--debug-exit-uvm: Exiting after UVM-supported pass\n"; - std::exit(0); + v3Global.vlExit(0); } // Calculate and check widths, edit tree to TRUNC/EXTRACT any width mismatches @@ -819,7 +819,7 @@ static void execBuildJob() { if (exit_code != 0) { v3error(cmdStr << " exited with " << exit_code << std::endl); - std::exit(exit_code); + v3Global.vlExit(exit_code); } } @@ -832,7 +832,7 @@ static void execHierVerilation() { const int exit_code = V3Os::system(cmdStr); if (exit_code != 0) { v3error(cmdStr << " exited with " << exit_code << std::endl); - std::exit(exit_code); + v3Global.vlExit(exit_code); } } @@ -884,9 +884,7 @@ int main(int argc, char** argv) { V3DiagSarif::output(true); // Explicitly release resources - V3PreShell::shutdown(); v3Global.shutdown(); - FileLine::deleteAllRemaining(); if (!v3Global.opt.quietStats() && !v3Global.opt.preprocOnly()) { V3Stats::addStatPerf(V3Stats::STAT_CPUTIME, cpuTimeTotal.deltaTime());