systemc: Fortify how exceptions are caught and passed around.
authorGabe Black <gabeblack@google.com>
Fri, 7 Sep 2018 08:37:57 +0000 (01:37 -0700)
committerGabe Black <gabeblack@google.com>
Tue, 9 Oct 2018 21:39:36 +0000 (21:39 +0000)
This change tightens up exception catching and makes gem5's systemc
code react to exceptions more in line with the Accellera
implementation. This prevents exceptions from being caught by the
pybind11 integration which makes it very difficult to see where an
exception came from, and makes the output differ by including a
(mostly useless) backtrace.

Change-Id: I7130d53a98fadd137073d1718f780f32f57c658c
Reviewed-on: https://gem5-review.googlesource.com/c/12601
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>

src/systemc/core/process.cc
src/systemc/core/process_types.hh
src/systemc/core/sc_main.cc
src/systemc/core/scheduler.cc
src/systemc/core/scheduler.hh
src/systemc/ext/utils/sc_report_handler.hh
src/systemc/tests/config.py
src/systemc/utils/sc_report_handler.cc

index 317e8c3a28cb5f902cb27e59c037b587a663f2d9..cc2eff5128807ca206f42b33f189a4770f507837 100644 (file)
@@ -343,6 +343,8 @@ Process::run()
         } catch(const ::sc_core::sc_unwind_exception &exc) {
             reset = exc.is_reset();
             _isUnwinding = false;
+        } catch (...) {
+            throw;
         }
     } while (reset);
     needsStart(true);
index 6f603e73f63f2065ac300be0ab9bd8c0e5bbb946..748c249147a47a9f3f2bc8c6f9b7182772447064 100644 (file)
@@ -92,7 +92,13 @@ class Thread : public Process
         main() override
         {
             thread->_needsStart = false;
-            thread->run();
+            try {
+                thread->run();
+            } catch (...) {
+                thread->terminate();
+                scheduler.throwToScMain();
+                return;
+            }
             thread->terminate();
             scheduler.yield();
         }
index 2637cef0e6b2fca406ac6fa63c1e1a5791df3e42..5e1cd4fe6402e3c61a12e57d70f0e8782651a9c2 100644 (file)
@@ -79,6 +79,10 @@ class ScMainFiber : public Fiber
             } catch (const sc_report &r) {
                 // There was an exception nobody caught.
                 resultStr = r.what();
+            } catch (...) {
+                // There was some other type of exception we need to wrap.
+                const sc_report *r = ::sc_gem5::reportifyException();
+                resultStr = r->what();
             }
             ::sc_gem5::Kernel::scMainFinished(true);
             ::sc_gem5::scheduler.clear();
index 4c98b68aa78af96630dffc91ba91b8dff2788e53..5348e6665ee526f789891384dfa42fef8b4910c0 100644 (file)
@@ -34,6 +34,8 @@
 #include "sim/eventq.hh"
 #include "systemc/core/kernel.hh"
 #include "systemc/ext/core/sc_main.hh"
+#include "systemc/ext/utils/sc_report.hh"
+#include "systemc/ext/utils/sc_report_handler.hh"
 
 namespace sc_gem5
 {
@@ -42,7 +44,7 @@ Scheduler::Scheduler() :
     eq(nullptr), readyEvent(this, false, ReadyPriority),
     pauseEvent(this, false, PausePriority),
     stopEvent(this, false, StopPriority),
-    scMain(nullptr),
+    scMain(nullptr), _throwToScMain(nullptr),
     starvationEvent(this, false, StarvationPriority),
     _started(false), _paused(false), _stopped(false), _stopNow(false),
     maxTickEvent(this, false, MaxTickPriority),
@@ -184,7 +186,11 @@ Scheduler::yield()
         // If the current process needs to be manually started, start it.
         if (_current && _current->needsStart()) {
             _current->needsStart(false);
-            _current->run();
+            try {
+                _current->run();
+            } catch (...) {
+                throwToScMain();
+            }
         }
     }
     if (_current && _current->excWrapper) {
@@ -336,7 +342,8 @@ Scheduler::pause()
     _paused = true;
     kernel->status(::sc_core::SC_PAUSED);
     runOnce = false;
-    scMain->run();
+    if (scMain && !scMain->finished())
+        scMain->run();
 }
 
 void
@@ -348,7 +355,8 @@ Scheduler::stop()
     clear();
 
     runOnce = false;
-    scMain->run();
+    if (scMain && !scMain->finished())
+        scMain->run();
 }
 
 void
@@ -385,6 +393,12 @@ Scheduler::start(Tick max_tick, bool run_to_time)
         deschedule(&maxTickEvent);
     if (starvationEvent.scheduled())
         deschedule(&starvationEvent);
+
+    if (_throwToScMain) {
+        const ::sc_core::sc_report *to_throw = _throwToScMain;
+        _throwToScMain = nullptr;
+        throw *to_throw;
+    }
 }
 
 void
@@ -404,6 +418,15 @@ Scheduler::schedulePause()
     schedule(&pauseEvent);
 }
 
+void
+Scheduler::throwToScMain(const ::sc_core::sc_report *r)
+{
+    if (!r)
+        r = reportifyException();
+    _throwToScMain = r;
+    scMain->run();
+}
+
 void
 Scheduler::scheduleStop(bool finish_delta)
 {
@@ -421,4 +444,46 @@ Scheduler::scheduleStop(bool finish_delta)
 
 Scheduler scheduler;
 
+namespace {
+
+void
+throwingReportHandler(const ::sc_core::sc_report &r,
+                      const ::sc_core::sc_actions &)
+{
+    throw r;
+}
+
+} // anonymous namespace
+
+const ::sc_core::sc_report *
+reportifyException()
+{
+    ::sc_core::sc_report_handler_proc old_handler =
+        ::sc_core::sc_report_handler::get_handler();
+    ::sc_core::sc_report_handler::set_handler(&throwingReportHandler);
+
+    try {
+        try {
+            // Rethrow the current exception so we can catch it and throw an
+            // sc_report instead if it's not a type we recognize/can handle.
+            throw;
+        } catch (const ::sc_core::sc_report &) {
+            // It's already a sc_report, so nothing to do.
+            throw;
+        } catch (const ::sc_core::sc_unwind_exception &) {
+            panic("Kill/reset exception escaped a Process::run()");
+        } catch (const std::exception &e) {
+            SC_REPORT_ERROR("uncaught exception", e.what());
+        } catch (const char *msg) {
+            SC_REPORT_ERROR("uncaught exception", msg);
+        } catch (...) {
+            SC_REPORT_ERROR("uncaught exception", "UNKNOWN EXCEPTION");
+        }
+    } catch (const ::sc_core::sc_report &r) {
+        ::sc_core::sc_report_handler::set_handler(old_handler);
+        return &r;
+    }
+    panic("No exception thrown in reportifyException.");
+}
+
 } // namespace sc_gem5
index 29501bc24efc2ed7b1f7b667d9a164bd6da0e0c7..dda483f0cf3ef8b5c6cf3d17955aa9a94ce64d01 100644 (file)
@@ -333,6 +333,8 @@ class Scheduler
 
     uint64_t changeStamp() { return _changeStamp; }
 
+    void throwToScMain(const ::sc_core::sc_report *r=nullptr);
+
   private:
     typedef const EventBase::Priority Priority;
     static Priority DefaultPriority = EventBase::Default_Pri;
@@ -377,7 +379,9 @@ class Scheduler
     void stop();
     EventWrapper<Scheduler, &Scheduler::pause> pauseEvent;
     EventWrapper<Scheduler, &Scheduler::stop> stopEvent;
+
     Fiber *scMain;
+    const ::sc_core::sc_report *_throwToScMain;
 
     bool
     starved()
@@ -437,6 +441,8 @@ Scheduler::TimeSlot::process()
     scheduler.completeTimeSlot(this);
 }
 
+const ::sc_core::sc_report *reportifyException();
+
 } // namespace sc_gem5
 
 #endif // __SYSTEMC_CORE_SCHEDULER_H__
index f65b32ccaeca30668404a309727cb14471a9293f..6ce20afb4558cc1f5317e1a88fd07c7c754b35bb 100644 (file)
@@ -105,6 +105,10 @@ class sc_report_handler
     static void set_handler(sc_report_handler_proc);
     static void default_handler(const sc_report &, const sc_actions &);
     static sc_actions get_new_action_id();
+    // Nonstandard
+    // Returns the current handler so it can be restored if it needs to be
+    // changed temporarily.
+    static sc_report_handler_proc get_handler();
 
     static sc_report *get_cached_report();
     static void clear_cached_report();
index 439a6828cedd7143b08cc7aac410bf8eba54346f..d9ae7499427777a6fe5c32a0c0943fdef4ab8404 100755 (executable)
@@ -31,6 +31,7 @@ import argparse
 import m5
 import os
 import re
+import sys
 
 from m5.objects import SystemC_Kernel, Root
 
@@ -58,7 +59,5 @@ if result.code != 0:
     # generate errors, and as long as their output matches that's still
     # considered correct. A "real" systemc config should expect sc_main
     # (if present) not to fail.
-    scrubbed = re.sub(r'In file: .*$',
-            'In file: <removed by verify.pl>',
-            result.message)
-    print('\n' + scrubbed)
+    print('\n' + result.message)
+    sys.exit(int(result.code))
index e08ad66248466222a8c356acf525bf8e9d3b19f2..1ca9d975d7994fbdbb28769dec2f2da45ef6a055 100644 (file)
@@ -341,6 +341,12 @@ sc_report_handler::get_new_action_id()
     return maxAction;
 }
 
+sc_report_handler_proc
+sc_report_handler::get_handler()
+{
+    return reportHandlerProc;
+}
+
 sc_report *
 sc_report_handler::get_cached_report()
 {