Map sdbusplus exception to phosphor exception

Errors will be created by using the sdbusplus error types, which results
in an sdbusplus exception being thrown.

Error metadata can be verified at compile-time by checking the error
against phosphor-logging error types. This commit maps the sdbusplus
error type to the phosphor type, for this purpose, via template
specializations.

Change-Id: Iee37e2a3846cc3acf3a62270a520ff0c395fd36d
Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
diff --git a/callouts/callout_test.cpp b/callouts/callout_test.cpp
index 2af6dc0..2eff6a0 100644
--- a/callouts/callout_test.cpp
+++ b/callouts/callout_test.cpp
@@ -1,4 +1,5 @@
 #include <iostream>
+#include <sdbusplus/exception.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 #include "elog_meta.hpp"
@@ -21,7 +22,7 @@
             TestCallout::CALLOUT_ERRNO_TEST(0),
             TestCallout::CALLOUT_DEVICE_PATH_TEST(argv[1]));
     }
-    catch (elogException<TestCallout>& e)
+    catch (TestCallout& e)
     {
         commit(e.name());
     }
diff --git a/logging_test.cpp b/logging_test.cpp
index f9d9739..2325444 100644
--- a/logging_test.cpp
+++ b/logging_test.cpp
@@ -4,8 +4,10 @@
 #include <iostream>
 #include <systemd/sd-journal.h>
 #include <sstream>
+#include <sdbusplus/exception.hpp>
 #include <phosphor-logging/elog.hpp>
 #include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
 
 using namespace phosphor;
 using namespace logging;
@@ -97,8 +99,7 @@
                 example::xyz::openbmc_project::Example::Elog::
                     TestErrorTwo::DEV_NAME("test case 3"));
     }
-    catch (elogException<example::xyz::openbmc_project::Example::Elog::
-           TestErrorOne>& e)
+    catch (example::xyz::openbmc_project::Example::Elog::TestErrorOne& e)
     {
         std::cout << "elog exception caught: " << e.what() << std::endl;
     }
@@ -150,7 +151,7 @@
                            TestErrorTwo::DEV_ID(100),
                            TestErrorTwo::DEV_NAME("test case 4"));
     }
-    catch (elogExceptionBase& e)
+    catch (sdbusplus::exception_t& e)
     {
         std::cout << "elog exception caught: " << e.what() << std::endl;
     }
@@ -214,8 +215,8 @@
                     example::xyz::openbmc_project::Example::Elog::
                         AutoTestSimple::STRING("FOO"));
         }
-        catch (elogException<example::xyz::openbmc_project::Example::Elog::
-            AutoTestSimple>& e)
+        catch (example::xyz::openbmc_project::Example::Elog::
+            AutoTestSimple& e)
         {
             std::cout << "elog exception caught: " << e.what() << std::endl;
             commit(e.name());
diff --git a/phosphor-logging/elog.hpp b/phosphor-logging/elog.hpp
index fe79bdc..329dc0d 100644
--- a/phosphor-logging/elog.hpp
+++ b/phosphor-logging/elog.hpp
@@ -3,7 +3,6 @@
 #include <tuple>
 #include <utility>
 #include <phosphor-logging/log.hpp>
-#include <phosphor-logging/elog-errors.hpp>
 
 namespace phosphor
 {
@@ -65,27 +64,28 @@
 template <typename T> using deduce_entry_type_t =
         typename deduce_entry_type<T>::type;
 
-} // namespace details
-
 /**
- * @brief Error log exception base class
+ * @brief Used to map an sdbusplus error to a phosphor-logging error type
  *
- * This allows people to capture all error log exceptions if desired
+ * Users log errors via the sdbusplus error name, and the execption that's
+ * thrown is the corresponding sdbusplus exception. However, there's a need
+ * to map the sdbusplus error name to the phosphor-logging error name, in order
+ * to verify the error metadata at compile-time.
  */
-class elogExceptionBase : public std::exception {};
-
-/**
- * @brief Error log exception class
- *
- * This is for capturing specific error log exceptions
- */
-template <typename T> class elogException : public elogExceptionBase
+template <typename T>
+struct map_exception_type
 {
-    public:
-        const char* what() const noexcept override { return T::err_code; }
-        const char* name() const noexcept { return T::err_code; }
+    using type = T;
 };
 
+/**
+ * @brief Typedef for above structure usage
+ */
+template <typename T> using map_exception_type_t =
+    typename map_exception_type<T>::type;
+
+} // namespace details
+
 /** @fn commit()
  *  @brief Create an error log entry based on journal
  *          entry with a specified MSG_ID
@@ -103,17 +103,19 @@
 void elog(Args... i_args)
 {
     // Validate the caller passed in the required parameters
-    static_assert(std::is_same<typename T::metadata_types,
+    static_assert(std::is_same<typename details::
+                               map_exception_type_t<T>::metadata_types,
                                std::tuple<
                                details::deduce_entry_type_t<Args>...>>
                                ::value,
                   "You are not passing in required arguments for this error");
 
-    log<T::L>(T::err_msg,
-              details::deduce_entry_type<Args>{i_args}.get()...);
+    log<details::map_exception_type_t<T>::L>(
+        details::map_exception_type_t<T>::err_msg,
+        details::deduce_entry_type<Args>{i_args}.get()...);
 
     // Now throw an exception for this error
-    throw elogException<T>();
+    throw T();
 }
 
 } // namespace logging
diff --git a/tools/phosphor-logging/templates/elog-gen-template.mako.hpp b/tools/phosphor-logging/templates/elog-gen-template.mako.hpp
index 9728d44..7ee4268 100644
--- a/tools/phosphor-logging/templates/elog-gen-template.mako.hpp
+++ b/tools/phosphor-logging/templates/elog-gen-template.mako.hpp
@@ -7,7 +7,37 @@
 #include <string>
 #include <tuple>
 #include <type_traits>
+#include <sdbusplus/exception.hpp>
 #include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog.hpp>
+
+<% exceptions = [] %>\
+% for name in errors:
+<%
+    if("example.xyz.openbmc_project" not in name):
+        exception = name.replace(".", "::")
+        exception = "sdbusplus::" + exception
+        index = exception.rfind("::")
+        exception = exception[:index] + "::Error::" + exception[index+2:]
+        exceptions.append(exception)
+%>\
+% endfor
+% for exception in set(exceptions):
+<%
+    ns = exception.split("::")
+    exception_name = ns[-1]
+    ns = ns[:-1]
+%>\
+    % for s in ns:
+namespace ${s}
+{
+    % endfor
+    struct ${exception_name};
+    % for s in reversed(ns):
+} // namespace ${s}
+    % endfor
+
+% endfor
 
 namespace phosphor
 {
@@ -69,8 +99,10 @@
         else:
             meta_string = parent_meta_short
         parent = parents[parent]
+
+    error_type = classname + " : public sdbusplus::exception_t"
 %>
-struct ${classname}
+struct ${error_type}
 {
     static constexpr auto err_code = "${name}";
     static constexpr auto err_msg = "${error_msg[name]}";
@@ -82,12 +114,54 @@
     using ${b.split("::").pop()} = ${b};
     % endfor
     using metadata_types = std::tuple<${meta_string}>;
+
+    const char* name() const noexcept
+    {
+        return err_code;
+    }
+
+    const char* description() const noexcept
+    {
+        return err_msg;
+    }
+
+    const char* what() const noexcept
+    {
+        return err_code;
+    }
 };
 
 % for s in reversed(namespaces):
 } // namespace ${s}
 % endfor
 
+<%
+    sdbusplus_name = name
+    if("example.xyz.openbmc_project" not in name):
+        sdbusplus_name = "sdbusplus." + sdbusplus_name
+        pos = sdbusplus_name.rfind(".")
+        sdbusplus_name = (sdbusplus_name[:pos] + ".Error." +
+                          sdbusplus_name[pos+1:])
+    sdbusplus_type = sdbusplus_name.replace(".", "::")
+    phosphor_type = sdbusplus_type
+    if("example.xyz.openbmc_project" not in name):
+        phosphor_type = sdbusplus_type.replace("sdbusplus::", "")
+        phosphor_type = phosphor_type.replace("Error::", "")
+%>\
+
+% if sdbusplus_type != phosphor_type:
+namespace details
+{
+
+template <>
+struct map_exception_type<${sdbusplus_type}>
+{
+    using type = ${phosphor_type};
+};
+
+}
+%endif
+
     % endfor
 
 } // namespace logging