Browse Source

[master] Merge branch 'trac3431'

Stephen Morris 10 years ago
parent
commit
5b1b54fe86
2 changed files with 99 additions and 50 deletions
  1. 62 23
      src/lib/log/logger.h
  2. 37 27
      src/lib/log/tests/logger_unittest.cc

+ 62 - 23
src/lib/log/logger.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2014  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -20,6 +20,8 @@
 #include <string>
 #include <string>
 #include <cstring>
 #include <cstring>
 
 
+#include <boost/static_assert.hpp>
+
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <log/logger_level.h>
 #include <log/logger_level.h>
 #include <log/message_types.h>
 #include <log/message_types.h>
@@ -27,6 +29,7 @@
 
 
 namespace isc {
 namespace isc {
 namespace log {
 namespace log {
+
 namespace interprocess {
 namespace interprocess {
 // Forward declaration to hide implementation details from normal
 // Forward declaration to hide implementation details from normal
 // applications.
 // applications.
@@ -91,24 +94,44 @@ class InterprocessSync;
 
 
 class LoggerImpl;   // Forward declaration of the implementation class
 class LoggerImpl;   // Forward declaration of the implementation class
 
 
-/// \brief Logging Not Initialized
+/// \brief Bad Interprocess Sync
 ///
 ///
-/// Exception thrown if an attempt is made to access a logging function
-/// if the logging system has not been initialized.
-class LoggingNotInitialized : public isc::Exception {
+/// Exception thrown if a bad InterprocessSync object (such as NULL) is
+/// used.
+class BadInterprocessSync : public isc::Exception {
 public:
 public:
-    LoggingNotInitialized(const char* file, size_t line, const char* what) :
+    BadInterprocessSync(const char* file, size_t line, const char* what) :
         isc::Exception(file, line, what)
         isc::Exception(file, line, what)
     {}
     {}
 };
 };
 
 
-/// \brief Bad Interprocess Sync
+/// \brief Logger Name Error
 ///
 ///
-/// Exception thrown if a bad InterprocessSync object (such as NULL) is
-/// used.
-class BadInterprocessSync : public isc::Exception {
+/// Exception thrown if a logger name is too short or too long.
+class LoggerNameError : public isc::Exception {
 public:
 public:
-    BadInterprocessSync(const char* file, size_t line, const char* what) :
+    LoggerNameError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// \brief Logger Name is Null
+///
+/// Exception thrown if a logger name is null
+class LoggerNameNull : public isc::Exception {
+public:
+    LoggerNameNull(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// \brief Logging Not Initialized
+///
+/// Exception thrown if an attempt is made to access a logging function
+/// if the logging system has not been initialized.
+class LoggingNotInitialized : public isc::Exception {
+public:
+    LoggingNotInitialized(const char* file, size_t line, const char* what) :
         isc::Exception(file, line, what)
         isc::Exception(file, line, what)
     {}
     {}
 };
 };
@@ -139,23 +162,39 @@ public:
     /// child of the root logger.
     /// child of the root logger.
     ///
     ///
     /// \note The name of the logger may be no longer than MAX_LOGGER_NAME_SIZE
     /// \note The name of the logger may be no longer than MAX_LOGGER_NAME_SIZE
-    /// else the program will halt with an assertion failure.  This restriction
-    /// allows loggers to be declared statically: the name is stored in a
-    /// fixed-size array to avoid the need to allocate heap storage during
-    /// program initialization (which causes problems on some operating
-    /// systems).
+    /// else the program will throw an exception.  This restriction allows
+    /// loggers to be declared statically: the name is stored in a fixed-size
+    /// array to avoid the need to allocate heap storage during program
+    /// initialization (which causes problems on some operating systems).
     ///
     ///
     /// \note Note also that there is no constructor taking a std::string. This
     /// \note Note also that there is no constructor taking a std::string. This
     /// minimises the possibility of initializing a static logger with a
     /// minimises the possibility of initializing a static logger with a
     /// string, so leading to problems mentioned above.
     /// string, so leading to problems mentioned above.
     Logger(const char* name) : loggerptr_(NULL) {
     Logger(const char* name) : loggerptr_(NULL) {
-        assert(std::strlen(name) < sizeof(name_));
-        // Do the copy.  Note that the assertion above has checked that the
-        // contents of "name" and a trailing null will fit within the space
-        // allocated for name_, so we could use strcpy here and be safe.
-        // However, a bit of extra paranoia doesn't hurt.
-        std::strncpy(name_, name, sizeof(name_));
-        assert(name_[sizeof(name_) - 1] == '\0');
+
+        // Validate the name of the logger.
+        if (name == NULL) {
+            isc_throw(LoggerNameNull, "logger names may not be null");
+
+        } else {
+            // Name not null, is it too short or too long?
+            size_t namelen = std::strlen(name);
+            if ((namelen == 0) || (namelen > MAX_LOGGER_NAME_SIZE)) {
+                isc_throw(LoggerNameError, "'" << name << "' is not a valid "
+                          << "name for a logger: valid names must be between 1 "
+                          << "and " << MAX_LOGGER_NAME_SIZE << " characters in "
+                          << "length");
+            }
+        }
+
+        // The checks above and the assertion below ensure that the contents of
+        // "name" plus a trailing null will fit into the space allocated for
+        // "name_".
+        BOOST_STATIC_ASSERT(MAX_LOGGER_NAME_SIZE < sizeof(name_));
+
+        // Do the copy, ensuring a trailing NULL in all cases.
+        std::strncpy(name_, name, MAX_LOGGER_NAME_SIZE);
+        name_[MAX_LOGGER_NAME_SIZE] = '\0';
     }
     }
 
 
     /// \brief Destructor
     /// \brief Destructor

+ 37 - 27
src/lib/log/tests/logger_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011, 2014  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -11,6 +11,7 @@
 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <util/unittests/resource.h>
 #include <util/unittests/resource.h>
@@ -353,34 +354,43 @@ TEST_F(LoggerTest, IsDebugEnabledLevel) {
     EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
     EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
 }
 }
 
 
-// Check that if a logger name is too long, it triggers the appropriate
-// assertion.
+// Check that loggers with invalid names give an error.
+
 TEST_F(LoggerTest, LoggerNameLength) {
 TEST_F(LoggerTest, LoggerNameLength) {
-    // The following statements should just declare a logger and nothing
-    // should happen.
-    string ok1(Logger::MAX_LOGGER_NAME_SIZE - 1, 'x');
-    Logger l1(ok1.c_str());
-    EXPECT_EQ(getRootLoggerName() + "." + ok1, l1.getName());
-
-    string ok2(Logger::MAX_LOGGER_NAME_SIZE, 'x');
-    Logger l2(ok2.c_str());
-    EXPECT_EQ(getRootLoggerName() + "." + ok2, l2.getName());
-
-    // Note: Not all systems have EXPECT_DEATH.  As it is a macro we can just
-    // test for its presence and bypass the test if not available.
-#ifdef EXPECT_DEATH
-    // Too long a logger name should trigger an assertion failure.
-    // Note that we just check that it dies - we don't check what message is
-    // output.
-    if (!isc::util::unittests::runningOnValgrind()) {
-        EXPECT_DEATH({
-            isc::util::unittests::dontCreateCoreDumps();
-
-            string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
-            Logger l3(ok3.c_str());
-        }, ".*");
+    // Null name
+    EXPECT_THROW(Logger(NULL), LoggerNameNull);
+
+    // Declare space for the logger name.  The length of names checked
+    // will range from 0 through MAX_LOGGER_NAME_SIZE + 1: to allow for
+    // the trailing null, at least one more byte than the longest name size
+    // must be reserved.
+    char name[Logger::MAX_LOGGER_NAME_SIZE + 2];
+
+    // Zero-length name should throw an exception
+    name[0] = '\0';
+    EXPECT_THROW({
+            Logger dummy(name);
+            }, LoggerNameError);
+
+    // Work through all valid names.
+    for (size_t i = 0; i < Logger::MAX_LOGGER_NAME_SIZE; ++i) {
+
+        // Append a character to the name and check that a logger with that
+        // name can be created without throwing an exception.
+        name[i] = 'X';
+        name[i + 1] = '\0';
+        EXPECT_NO_THROW({
+                Logger dummy(name);
+                }) << "Size of logger name is " << (i + 1);
     }
     }
-#endif
+
+    // ... and check that an overly long name throws an exception.
+    name[Logger::MAX_LOGGER_NAME_SIZE] = 'X';
+    name[Logger::MAX_LOGGER_NAME_SIZE + 1] = '\0';
+    EXPECT_THROW({
+            Logger dummy(name);
+            }, LoggerNameError);
+
 }
 }
 
 
 TEST_F(LoggerTest, setInterprocessSync) {
 TEST_F(LoggerTest, setInterprocessSync) {