changeset 12257:0b1e2f0e62b5

Merge
author mgronlun
date Thu, 27 Oct 2016 11:20:37 +0200
parents 3609eb7f27fa 46a9564b8513
children d13e2c90fcef
files
diffstat 19 files changed, 258 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/src/os/posix/vm/os_posix.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/os/posix/vm/os_posix.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -188,6 +188,10 @@
   return NOT_AIX(::)fileno(fp);
 }
 
+struct tm* os::gmtime_pd(const time_t* clock, struct tm*  res) {
+  return gmtime_r(clock, res);
+}
+
 void os::Posix::print_load_average(outputStream* st) {
   st->print("load average:");
   double loadavg[3];
--- a/src/os/windows/vm/os_windows.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/os/windows/vm/os_windows.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -403,6 +403,15 @@
   return NULL;
 }
 
+struct tm* os::gmtime_pd(const time_t* clock, struct tm* res) {
+  const struct tm* time_struct_ptr = gmtime(clock);
+  if (time_struct_ptr != NULL) {
+    *res = *time_struct_ptr;
+    return res;
+  }
+  return NULL;
+}
+
 LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo);
 
 // Thread start routine for all newly created threads
--- a/src/share/vm/logging/logConfiguration.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logConfiguration.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -98,14 +98,14 @@
   LogDecorations::initialize(vm_start_time);
   assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
   _outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
-  _outputs[0] = LogOutput::Stdout;
-  _outputs[1] = LogOutput::Stderr;
+  _outputs[0] = &StdoutLog;
+  _outputs[1] = &StderrLog;
   _n_outputs = 2;
 }
 
 void LogConfiguration::finalize() {
-  for (size_t i = 2; i < _n_outputs; i++) {
-    delete _outputs[i];
+  for (size_t i = _n_outputs; i > 0; i--) {
+    disable_output(i - 1);
   }
   FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
 }
@@ -279,8 +279,8 @@
     ts->update_decorators();
   }
 
-  // Delete the output unless stdout/stderr
-  if (out != LogOutput::Stderr && out != LogOutput::Stdout) {
+  // Delete the output unless stdout or stderr (idx 0 or 1)
+  if (idx > 1) {
     delete_output(idx);
   } else {
     out->set_config_string("all=off");
@@ -322,7 +322,7 @@
 
   // Apply configuration to stdout (output #0), with the same decorators as before.
   ConfigurationLock cl;
-  configure_output(0, expr, LogOutput::Stdout->decorators());
+  configure_output(0, expr, _outputs[0]->decorators());
   notify_update_listeners();
 }
 
--- a/src/share/vm/logging/logDecorations.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logDecorations.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -72,6 +72,12 @@
   ASSERT_AND_RETURN(written, pos)
 }
 
+char* LogDecorations::create_utctime_decoration(char* pos) {
+  char* buf = os::iso8601_time(pos, 29, true);
+  int written = buf == NULL ? -1 : 29;
+  ASSERT_AND_RETURN(written, pos)
+}
+
 char * LogDecorations::create_uptime_decoration(char* pos) {
   int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%.3fs", os::elapsedTime());
   ASSERT_AND_RETURN(written, pos)
--- a/src/share/vm/logging/logDecorations.hpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logDecorations.hpp	Thu Oct 27 11:20:37 2016 +0200
@@ -36,7 +36,7 @@
   char _decorations_buffer[DecorationsBufferSize];
   char* _decoration_offset[LogDecorators::Count];
   LogLevelType _level;
-  LogTagSet _tagset;
+  const LogTagSet& _tagset;
   jlong _millis;
   static jlong _vm_start_time_millis;
   static const char* _host_name;
--- a/src/share/vm/logging/logDecorators.hpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logDecorators.hpp	Thu Oct 27 11:20:37 2016 +0200
@@ -41,6 +41,7 @@
 // tags         - The tag-set associated with the log message
 #define DECORATOR_LIST          \
   DECORATOR(time,         t)    \
+  DECORATOR(utctime,      utc)  \
   DECORATOR(uptime,       u)    \
   DECORATOR(timemillis,   tm)   \
   DECORATOR(uptimemillis, um)   \
--- a/src/share/vm/logging/logFileStreamOutput.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logFileStreamOutput.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -28,8 +28,20 @@
 #include "logging/logMessageBuffer.hpp"
 #include "memory/allocation.inline.hpp"
 
-LogStdoutOutput LogStdoutOutput::_instance;
-LogStderrOutput LogStderrOutput::_instance;
+static bool initialized;
+static char stdoutmem[sizeof(LogStdoutOutput)];
+static char stderrmem[sizeof(LogStderrOutput)];
+
+LogStdoutOutput &StdoutLog = reinterpret_cast<LogStdoutOutput&>(stdoutmem);
+LogStderrOutput &StderrLog = reinterpret_cast<LogStderrOutput&>(stderrmem);
+
+LogFileStreamInitializer::LogFileStreamInitializer() {
+  if (!initialized) {
+    ::new (&StdoutLog) LogStdoutOutput();
+    ::new (&StderrLog) LogStderrOutput();
+    initialized = true;
+  }
+}
 
 int LogFileStreamOutput::write_decorations(const LogDecorations& decorations) {
   int total_written = 0;
--- a/src/share/vm/logging/logFileStreamOutput.hpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logFileStreamOutput.hpp	Thu Oct 27 11:20:37 2016 +0200
@@ -30,6 +30,14 @@
 
 class LogDecorations;
 
+class LogFileStreamInitializer {
+ public:
+  LogFileStreamInitializer();
+};
+
+// Ensure the default log streams have been initialized (stdout, stderr) using the static initializer below
+static LogFileStreamInitializer log_stream_initializer;
+
 // Base class for all FileStream-based log outputs.
 class LogFileStreamOutput : public LogOutput {
  protected:
@@ -50,9 +58,8 @@
 };
 
 class LogStdoutOutput : public LogFileStreamOutput {
-  friend class LogOutput;
+  friend class LogFileStreamInitializer;
  private:
-  static LogStdoutOutput _instance;
   LogStdoutOutput() : LogFileStreamOutput(stdout) {
     set_config_string("all=warning");
   }
@@ -66,9 +73,8 @@
 };
 
 class LogStderrOutput : public LogFileStreamOutput {
-  friend class LogOutput;
+  friend class LogFileStreamInitializer;
  private:
-  static LogStderrOutput _instance;
   LogStderrOutput() : LogFileStreamOutput(stderr) {
     set_config_string("all=off");
   }
@@ -81,4 +87,7 @@
   }
 };
 
+extern LogStderrOutput &StderrLog;
+extern LogStdoutOutput &StdoutLog;
+
 #endif // SHARE_VM_LOGGING_LOGFILESTREAMOUTPUT_HPP
--- a/src/share/vm/logging/logOutput.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logOutput.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -29,9 +29,6 @@
 #include "runtime/mutexLocker.hpp"
 #include "runtime/os.inline.hpp"
 
-LogOutput* const LogOutput::Stdout = &LogStdoutOutput::_instance;
-LogOutput* const LogOutput::Stderr = &LogStderrOutput::_instance;
-
 LogOutput::~LogOutput() {
   os::free(_config_string);
 }
--- a/src/share/vm/logging/logOutput.hpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logOutput.hpp	Thu Oct 27 11:20:37 2016 +0200
@@ -57,9 +57,6 @@
   void set_config_string(const char* string);
 
  public:
-  static LogOutput* const Stdout;
-  static LogOutput* const Stderr;
-
   void set_decorators(const LogDecorators &decorators) {
     _decorators = decorators;
   }
--- a/src/share/vm/logging/logTagSet.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/logging/logTagSet.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -23,6 +23,7 @@
  */
 #include "precompiled.hpp"
 #include "logging/logDecorations.hpp"
+#include "logging/logFileStreamOutput.hpp"
 #include "logging/logLevel.hpp"
 #include "logging/logMessageBuffer.hpp"
 #include "logging/logOutput.hpp"
@@ -50,7 +51,7 @@
   _ntagsets++;
 
   // Set the default output to warning and error level for all new tagsets.
-  _output_list.set_output_level(LogOutput::Stdout, LogLevel::Default);
+  _output_list.set_output_level(&StdoutLog, LogLevel::Default);
 }
 
 void LogTagSet::update_decorators(const LogDecorators& decorator) {
--- a/src/share/vm/runtime/os.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/runtime/os.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -97,7 +97,7 @@
 // except that on Windows the %z behaves badly, so we do it ourselves.
 // Also, people wanted milliseconds on there,
 // and strftime doesn't do milliseconds.
-char* os::iso8601_time(char* buffer, size_t buffer_length) {
+char* os::iso8601_time(char* buffer, size_t buffer_length, bool utc) {
   // Output will be of the form "YYYY-MM-DDThh:mm:ss.mmm+zzzz\0"
   //                                      1         2
   //                             12345678901234567890123456789
@@ -122,9 +122,16 @@
     milliseconds_since_19700101 % milliseconds_per_microsecond;
   // Convert the time value to a tm and timezone variable
   struct tm time_struct;
-  if (localtime_pd(&seconds_since_19700101, &time_struct) == NULL) {
-    assert(false, "Failed localtime_pd");
-    return NULL;
+  if (utc) {
+    if (gmtime_pd(&seconds_since_19700101, &time_struct) == NULL) {
+      assert(false, "Failed gmtime_pd");
+      return NULL;
+    }
+  } else {
+    if (localtime_pd(&seconds_since_19700101, &time_struct) == NULL) {
+      assert(false, "Failed localtime_pd");
+      return NULL;
+    }
   }
 #if defined(_ALLBSD_SOURCE)
   const time_t zone = (time_t) time_struct.tm_gmtoff;
@@ -141,6 +148,12 @@
   if (time_struct.tm_isdst > 0) {
     UTC_to_local = UTC_to_local - seconds_per_hour;
   }
+
+  // No offset when dealing with UTC
+  if (utc) {
+    UTC_to_local = 0;
+  }
+
   // Compute the time zone offset.
   //    localtime_pd() sets timezone to the difference (in seconds)
   //    between UTC and and local time.
--- a/src/share/vm/runtime/os.hpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/src/share/vm/runtime/os.hpp	Thu Oct 27 11:20:37 2016 +0200
@@ -197,10 +197,11 @@
   // information may require a lock on some platforms.
   static char*      local_time_string(char *buf, size_t buflen);
   static struct tm* localtime_pd     (const time_t* clock, struct tm*  res);
+  static struct tm* gmtime_pd        (const time_t* clock, struct tm*  res);
   // Fill in buffer with current local time as an ISO-8601 string.
   // E.g., YYYY-MM-DDThh:mm:ss.mmm+zzzz.
   // Returns buffer, or NULL if it failed.
-  static char* iso8601_time(char* buffer, size_t buffer_length);
+  static char* iso8601_time(char* buffer, size_t buffer_length, bool utc = false);
 
   // Interface for detecting multiprocessor system
   static inline bool is_MP() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/logging/TestG1LoggingFailure.java	Thu Oct 27 11:20:37 2016 +0200
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /*
+ * @test TestG1LoggingFailure
+ * @bug 8151034
+ * @summary Regression test for G1 logging at OOME
+ * @requires vm.gc.G1
+ * @library /test/lib
+ * @modules java.management
+ * @build gc.g1.logging.TestG1LoggingFailure
+ * @run main/timeout=300 gc.g1.logging.TestG1LoggingFailure
+ */
+package gc.g1.logging;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Utils;
+
+public class TestG1LoggingFailure {
+
+    public static void main(String[] args) throws Throwable {
+        List<String> options = new ArrayList<>();
+        Collections.addAll(options, Utils.getTestJavaOpts());
+        Collections.addAll(options,
+                "-XX:+UseG1GC",
+                "-Xmx20m",
+                "-Xmn10m",
+                "-Xlog:gc=info",
+                "-XX:G1HeapRegionSize=1m"
+        );
+
+        options.add(Alloc.class.getName());
+
+        // According to https://bugs.openjdk.java.net/browse/JDK-8146009 failure happens not every time.
+        // Will try to reproduce this failure.
+        for (int iteration = 0; iteration < 40; ++iteration) {
+            startVM(options);
+        }
+    }
+
+    private static void startVM(List<String> options) throws Throwable, RuntimeException {
+        OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
+
+        out.shouldNotContain("pure virtual method called");
+        out.shouldContain("Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread \"main\"");
+
+        if (out.getExitValue() == 0) {
+            System.out.println(out.getOutput());
+            throw new RuntimeException("Expects Alloc failure.");
+        }
+    }
+
+    // Simple class to be executed in separate VM.
+    static class Alloc {
+
+        public static final int CHUNK = 1024;
+        public static ArrayList<Object> arr = new ArrayList<>();
+
+        public static void main(String[] args) {
+            try {
+                while (true) {
+                    arr.add(new byte[CHUNK]);
+                }
+            } catch (OutOfMemoryError oome) {
+            }
+            while (true) {
+                arr.add(new byte[CHUNK]);
+            }
+        }
+    }
+}
--- a/test/native/logging/test_logConfiguration.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/test/native/logging/test_logConfiguration.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -25,6 +25,7 @@
 #include "logTestFixture.hpp"
 #include "logTestUtils.inline.hpp"
 #include "logging/logConfiguration.hpp"
+#include "logging/logFileStreamOutput.hpp"
 #include "logging/logLevel.hpp"
 #include "logging/logOutput.hpp"
 #include "logging/logTag.hpp"
@@ -68,8 +69,8 @@
   const char* description = ss.as_string();
 
   // Verify that stdout and stderr are listed by default
-  EXPECT_PRED2(string_contains_substring, description, LogOutput::Stdout->name());
-  EXPECT_PRED2(string_contains_substring, description, LogOutput::Stderr->name());
+  EXPECT_PRED2(string_contains_substring, description, StdoutLog.name());
+  EXPECT_PRED2(string_contains_substring, description, StderrLog.name());
 
   // Verify that each tag, level and decorator is listed
   for (size_t i = 0; i < LogTag::Count; i++) {
@@ -126,7 +127,7 @@
     EXPECT_TRUE(is_described("logging=info"));
 
     // Verify by iterating over tagsets
-    LogOutput* o = LogOutput::Stdout;
+    LogOutput* o = &StdoutLog;
     for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
       EXPECT_TRUE(ts->has_output(o));
       EXPECT_TRUE(ts->is_level(LogLevel::Info));
@@ -178,8 +179,8 @@
 
   // Verify that no tagset has logging enabled
   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
-    EXPECT_FALSE(ts->has_output(LogOutput::Stdout));
-    EXPECT_FALSE(ts->has_output(LogOutput::Stderr));
+    EXPECT_FALSE(ts->has_output(&StdoutLog));
+    EXPECT_FALSE(ts->has_output(&StderrLog));
     EXPECT_FALSE(ts->is_level(LogLevel::Error));
   }
 }
@@ -193,7 +194,7 @@
   EXPECT_TRUE(is_described("#0: stdout all=off"));
 
   // Verify by iterating over tagsets
-  LogOutput* o = LogOutput::Stdout;
+  LogOutput* o = &StdoutLog;
   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
     EXPECT_FALSE(ts->has_output(o));
     EXPECT_FALSE(ts->is_level(LogLevel::Error));
@@ -247,7 +248,7 @@
     bool ret = LogConfiguration::parse_command_line_arguments(cmdline);
     EXPECT_TRUE(ret) << "Error parsing command line arguments '" << cmdline << "'";
     for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
-      EXPECT_EQ(LogLevel::Unspecified, ts->level_for(LogOutput::Stdout));
+      EXPECT_EQ(LogLevel::Unspecified, ts->level_for(&StdoutLog));
     }
   }
 }
@@ -306,7 +307,7 @@
   EXPECT_FALSE(log_is_enabled(Debug, logging));
   EXPECT_FALSE(log_is_enabled(Info, gc));
   LogTagSet* logging_ts = &LogTagSetMapping<LOG_TAGS(logging)>::tagset();
-  EXPECT_EQ(LogLevel::Info, logging_ts->level_for(LogOutput::Stdout));
+  EXPECT_EQ(LogLevel::Info, logging_ts->level_for(&StdoutLog));
 
   // Enable 'gc=debug' (no wildcard), verifying no other tags are enabled
   LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc));
@@ -316,9 +317,9 @@
   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
     if (ts->contains(PREFIX_LOG_TAG(gc))) {
       if (ts->ntags() == 1) {
-        EXPECT_EQ(LogLevel::Debug, ts->level_for(LogOutput::Stdout));
+        EXPECT_EQ(LogLevel::Debug, ts->level_for(&StdoutLog));
       } else {
-        EXPECT_EQ(LogLevel::Off, ts->level_for(LogOutput::Stdout));
+        EXPECT_EQ(LogLevel::Off, ts->level_for(&StdoutLog));
       }
     }
   }
@@ -329,12 +330,12 @@
   EXPECT_TRUE(log_is_enabled(Trace, gc, heap));
   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
     if (ts->contains(PREFIX_LOG_TAG(gc))) {
-      EXPECT_EQ(LogLevel::Trace, ts->level_for(LogOutput::Stdout));
+      EXPECT_EQ(LogLevel::Trace, ts->level_for(&StdoutLog));
     } else if (ts == logging_ts) {
       // Previous setting for 'logging' should remain
-      EXPECT_EQ(LogLevel::Info, ts->level_for(LogOutput::Stdout));
+      EXPECT_EQ(LogLevel::Info, ts->level_for(&StdoutLog));
     } else {
-      EXPECT_EQ(LogLevel::Off, ts->level_for(LogOutput::Stdout));
+      EXPECT_EQ(LogLevel::Off, ts->level_for(&StdoutLog));
     }
   }
 
@@ -345,7 +346,7 @@
   EXPECT_FALSE(log_is_enabled(Error, gc));
   EXPECT_FALSE(log_is_enabled(Error, gc, heap));
   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
-    EXPECT_EQ(LogLevel::Off, ts->level_for(LogOutput::Stdout));
+    EXPECT_EQ(LogLevel::Off, ts->level_for(&StdoutLog));
   }
 }
 
--- a/test/native/logging/test_logDecorations.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/test/native/logging/test_logDecorations.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -133,8 +133,8 @@
   // Verify format
   int y, M, d, h, m;
   double s;
-  int read = sscanf(timestr, "%d-%d-%dT%d:%d:%lfZ", &y, &M, &d, &h, &m, &s);
-  ASSERT_EQ(6, read);
+  int read = sscanf(timestr, "%d-%d-%dT%d:%d:%lf", &y, &M, &d, &h, &m, &s);
+  ASSERT_EQ(6, read) << "Invalid format: " << timestr;
 
   // Verify reported time & date
   struct tm reported_time = {0};
@@ -156,6 +156,48 @@
       << ", expected time: " << expected_ts;
 }
 
+// Test the utctime decoration
+TEST(LogDecorations, iso8601_utctime) {
+  LogDecorators decorator_selection;
+  ASSERT_TRUE(decorator_selection.parse("utctime"));
+  LogDecorations decorations(LogLevel::Info, tagset, decorator_selection);
+
+  const char *timestr = decorations.decoration(LogDecorators::utctime_decorator);
+  time_t expected_ts = time(NULL);
+
+  // Verify format
+  char trailing_character;
+  int y, M, d, h, m, offset;
+  double s;
+  int read = sscanf(timestr, "%d-%d-%dT%d:%d:%lf%c%d", &y, &M, &d, &h, &m, &s, &trailing_character, &offset);
+  ASSERT_GT(read, 7) << "Invalid format: " << timestr;
+
+  // Ensure time is UTC (no offset)
+  if (trailing_character == '+') {
+    ASSERT_EQ(0, offset) << "Invalid offset: " << timestr;
+  } else {
+    ASSERT_EQ('Z', trailing_character) << "Invalid offset: " << timestr;
+  }
+
+  struct tm reported_time = {0};
+  reported_time.tm_year = y - 1900;
+  reported_time.tm_mon = M - 1;
+  reported_time.tm_mday = d;
+  reported_time.tm_hour = h;
+  reported_time.tm_min = m;
+  reported_time.tm_sec = s;
+  reported_time.tm_isdst = 0; // No DST for UTC timestamps
+  time_t reported_ts = mktime(&reported_time);
+  expected_ts = mktime(gmtime(&expected_ts));
+  time_t diff = reported_ts - expected_ts;
+  if (diff < 0) {
+    diff = -diff;
+  }
+  // Allow up to 10 seconds in difference
+  ASSERT_LE(diff, 10) << "Reported time: " << reported_ts << " (" << timestr << ")"
+      << ", expected time: " << expected_ts;
+}
+
 // Test the pid and tid decorations
 TEST(LogDecorations, identifiers) {
   LogDecorators decorator_selection;
--- a/test/native/logging/test_logDecorators.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/test/native/logging/test_logDecorators.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -172,20 +172,20 @@
 
   // Select first and third decorator for dec1
   char input[64];
-  sprintf(input, "%s,%s", decorator_name_array[0], decorator_name_array[2]);
+  sprintf(input, "%s,%s", decorator_name_array[0], decorator_name_array[3]);
   dec1.parse(input);
   EXPECT_TRUE(dec1.is_decorator(decorator_array[0]));
-  EXPECT_TRUE(dec1.is_decorator(decorator_array[2]));
+  EXPECT_TRUE(dec1.is_decorator(decorator_array[3]));
 
   // Select the default decorators for dec2
   EXPECT_FALSE(dec2.is_decorator(decorator_array[0]));
-  EXPECT_FALSE(dec2.is_decorator(decorator_array[2]));
+  EXPECT_FALSE(dec2.is_decorator(decorator_array[3]));
   assert_default_decorators(&dec2);
 
   // Combine and verify that the combination includes first, third and default decorators
   dec2.combine_with(dec1);
   EXPECT_TRUE(dec2.is_decorator(decorator_array[0]));
-  EXPECT_TRUE(dec2.is_decorator(decorator_array[2]));
+  EXPECT_TRUE(dec2.is_decorator(decorator_array[3]));
   assert_default_decorators(&dec2, false);
 }
 
--- a/test/native/logging/test_logOutputList.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/test/native/logging/test_logOutputList.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -22,6 +22,7 @@
  */
 
 #include "precompiled.hpp"
+#include "logging/logFileStreamOutput.hpp"
 #include "logging/logLevel.hpp"
 #include "logging/logOutput.hpp"
 #include "logging/logOutputList.hpp"
@@ -173,7 +174,7 @@
   for (size_t i = LogLevel::First; i < LogLevel::Count; i++) {
     LogLevelType level = static_cast<LogLevelType>(i);
     LogOutputList list;
-    list.set_output_level(LogOutput::Stdout, level);
+    list.set_output_level(&StdoutLog, level);
     for (size_t j = LogLevel::First; j < LogLevel::Count; j++) {
       LogLevelType other = static_cast<LogLevelType>(j);
       // Verify that levels finer than the current level for stdout are reported as disabled,
@@ -201,8 +202,8 @@
 // Test is_level() on lists with two outputs on different levels
 TEST(LogOutputList, is_level_multiple_outputs) {
   for (size_t i = LogLevel::First; i < LogLevel::Count - 1; i++) {
-      LogOutput* dummy1 = LogOutput::Stdout;
-      LogOutput* dummy2 = LogOutput::Stderr;
+      LogOutput* dummy1 = &StdoutLog;
+      LogOutput* dummy2 = &StderrLog;
       LogLevelType first = static_cast<LogLevelType>(i);
       LogLevelType second = static_cast<LogLevelType>(i + 1);
       LogOutputList list;
@@ -226,19 +227,19 @@
   LogOutputList list;
 
   // Ask the empty list about stdout, stderr
-  EXPECT_EQ(LogLevel::Off, list.level_for(LogOutput::Stdout));
-  EXPECT_EQ(LogLevel::Off, list.level_for(LogOutput::Stderr));
+  EXPECT_EQ(LogLevel::Off, list.level_for(&StdoutLog));
+  EXPECT_EQ(LogLevel::Off, list.level_for(&StderrLog));
 
   // Ask for level in a list with two outputs on different levels
-  list.set_output_level(LogOutput::Stdout, LogLevel::Info);
-  list.set_output_level(LogOutput::Stderr, LogLevel::Trace);
-  EXPECT_EQ(LogLevel::Info, list.level_for(LogOutput::Stdout));
-  EXPECT_EQ(LogLevel::Trace, list.level_for(LogOutput::Stderr));
+  list.set_output_level(&StdoutLog, LogLevel::Info);
+  list.set_output_level(&StderrLog, LogLevel::Trace);
+  EXPECT_EQ(LogLevel::Info, list.level_for(&StdoutLog));
+  EXPECT_EQ(LogLevel::Trace, list.level_for(&StderrLog));
 
   // Remove and ask again
-  list.set_output_level(LogOutput::Stdout, LogLevel::Off);
-  EXPECT_EQ(LogLevel::Off, list.level_for(LogOutput::Stdout));
-  EXPECT_EQ(LogLevel::Trace, list.level_for(LogOutput::Stderr));
+  list.set_output_level(&StdoutLog, LogLevel::Off);
+  EXPECT_EQ(LogLevel::Off, list.level_for(&StdoutLog));
+  EXPECT_EQ(LogLevel::Trace, list.level_for(&StderrLog));
 
   // Ask about an unknown output
   LogOutput* dummy = dummy_output(4711);
@@ -251,5 +252,5 @@
   }
 
   // Make sure the stderr level is still the same
-  EXPECT_EQ(LogLevel::Trace, list.level_for(LogOutput::Stderr));
+  EXPECT_EQ(LogLevel::Trace, list.level_for(&StderrLog));
 }
--- a/test/native/logging/test_logTagSet.cpp	Thu Oct 27 11:14:30 2016 +0200
+++ b/test/native/logging/test_logTagSet.cpp	Thu Oct 27 11:20:37 2016 +0200
@@ -22,6 +22,7 @@
  */
 
 #include "precompiled.hpp"
+#include "logging/logFileStreamOutput.hpp"
 #include "logging/logLevel.hpp"
 #include "logging/logOutput.hpp"
 #include "logging/logTag.hpp"
@@ -37,18 +38,18 @@
     EXPECT_TRUE(ts->is_level(LogLevel::Error));
     EXPECT_TRUE(ts->is_level(LogLevel::Warning));
     EXPECT_FALSE(ts->is_level(LogLevel::Info));
-    EXPECT_TRUE(ts->has_output(LogOutput::Stdout));
-    EXPECT_FALSE(ts->has_output(LogOutput::Stderr));
+    EXPECT_TRUE(ts->has_output(&StdoutLog));
+    EXPECT_FALSE(ts->has_output(&StderrLog));
   }
 }
 
 TEST(LogTagSet, has_output) {
   LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
-  ts.set_output_level(LogOutput::Stderr, LogLevel::Trace);
-  EXPECT_TRUE(ts.has_output(LogOutput::Stderr));
+  ts.set_output_level(&StderrLog, LogLevel::Trace);
+  EXPECT_TRUE(ts.has_output(&StderrLog));
   EXPECT_FALSE(ts.has_output(NULL));
-  ts.set_output_level(LogOutput::Stderr, LogLevel::Off);
-  EXPECT_FALSE(ts.has_output(LogOutput::Stderr));
+  ts.set_output_level(&StderrLog, LogLevel::Off);
+  EXPECT_FALSE(ts.has_output(&StderrLog));
 }
 
 TEST(LogTagSet, ntags) {
@@ -61,18 +62,18 @@
 TEST(LogTagSet, is_level) {
   LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
   // Set info level on stdout and verify that is_level() reports correctly
-  ts.set_output_level(LogOutput::Stdout, LogLevel::Info);
+  ts.set_output_level(&StdoutLog, LogLevel::Info);
   EXPECT_TRUE(ts.is_level(LogLevel::Error));
   EXPECT_TRUE(ts.is_level(LogLevel::Warning));
   EXPECT_TRUE(ts.is_level(LogLevel::Info));
   EXPECT_FALSE(ts.is_level(LogLevel::Debug));
   EXPECT_FALSE(ts.is_level(LogLevel::Trace));
-  ts.set_output_level(LogOutput::Stdout, LogLevel::Default);
+  ts.set_output_level(&StdoutLog, LogLevel::Default);
   EXPECT_TRUE(ts.is_level(LogLevel::Default));
 }
 
 TEST(LogTagSet, level_for) {
-  LogOutput* output = LogOutput::Stdout;
+  LogOutput* output = &StdoutLog;
   LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
   for (uint i = 0; i < LogLevel::Count; i++) {
     LogLevelType level = static_cast<LogLevelType>(i);