1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "logging/log.hpp"
  27 #include "memory/allocation.inline.hpp"
  28 
  29 void LogWriteHelper::write_large(LogTagSet& lts,
  30                                  LogLevelType level,
  31                                  const char* prefix,
  32                                  size_t prefix_len,
  33                                  size_t msg_len,
  34                                  const char* fmt,
  35                                  va_list args) {
  36   size_t newbuf_len = prefix_len + msg_len + 1;
  37   char* newbuf = NEW_C_HEAP_ARRAY(char, newbuf_len, mtLogging);
  38   memcpy(newbuf, prefix, prefix_len);
  39   int ret = os::log_vsnprintf(newbuf + prefix_len, newbuf_len - prefix_len, fmt, args);
  40   assert(ret >= 0, "Log message buffer issue");
  41   lts.log(level, newbuf);
  42   FREE_C_HEAP_ARRAY(char, newbuf);
  43 }
  44 
  45 /////////////// Unit tests ///////////////
  46 
  47 #ifndef PRODUCT
  48 
  49 #include "logging/log.hpp"
  50 #include "logging/logConfiguration.hpp"
  51 #include "logging/logOutput.hpp"
  52 #include "memory/resourceArea.hpp"
  53 
  54 #define assert_str_eq(s1, s2) \
  55   assert(strcmp(s1, s2) == 0, "Expected '%s' to equal '%s'", s1, s2)
  56 
  57 #define assert_char_in(c, s) \
  58   assert(strchr(s, c) != NULL, "Expected '%s' to contain character '%c'", s, c)
  59 
  60 #define assert_char_not_in(c, s) \
  61   assert(strchr(s, c) == NULL, "Expected '%s' to *not* contain character '%c'", s, c)
  62 
  63 class TestLogFile {
  64  private:
  65   char file_name[256];
  66 
  67   void set_name(const char* test_name) {
  68     const char* tmpdir = os::get_temp_directory();
  69     int pos = jio_snprintf(file_name, sizeof(file_name), "%s%svmtest.%s.%d.log", tmpdir, os::file_separator(), test_name, os::current_process_id());
  70     assert(pos > 0, "too small log file name buffer");
  71     assert((size_t)pos < sizeof(file_name), "too small log file name buffer");
  72   }
  73 
  74  public:
  75   TestLogFile(const char* test_name) {
  76     set_name(test_name);
  77     remove(name());
  78   }
  79 
  80   ~TestLogFile() {
  81     remove(name());
  82   }
  83 
  84   const char* name() {
  85     return file_name;
  86   }
  87 };
  88 
  89 class TestLogSavedConfig {
  90  private:
  91   char* _saved_config;
  92   char* _new_output;
  93   Log(logging) _log;
  94  public:
  95   TestLogSavedConfig(const char* apply_output = NULL, const char* apply_setting = NULL) : _new_output(0) {
  96     _saved_config = os::strdup_check_oom(LogOutput::Stdout->config_string());
  97     bool success = LogConfiguration::parse_log_arguments("stdout", "all=off", NULL, NULL, _log.error_stream());
  98     assert(success, "test unable to turn all off");
  99 
 100     if (apply_output) {
 101       _new_output = os::strdup_check_oom(apply_output);
 102       bool success = LogConfiguration::parse_log_arguments(_new_output, apply_setting,  NULL, NULL, _log.error_stream());
 103       assert(success, "test unable to apply test log configuration");
 104     }
 105   }
 106 
 107   ~TestLogSavedConfig() {
 108     if (_new_output) {
 109       bool success = LogConfiguration::parse_log_arguments(_new_output, "all=off", NULL, NULL, _log.error_stream());
 110       assert(success, "test unable to turn all off");
 111       os::free(_new_output);
 112     }
 113 
 114     bool success = LogConfiguration::parse_log_arguments("stdout", _saved_config, NULL, NULL, _log.error_stream());
 115     assert(success, "test unable to restore log configuration");
 116     os::free(_saved_config);
 117   }
 118 };
 119 
 120 void Test_configure_stdout() {
 121   ResourceMark rm;
 122   LogOutput* stdoutput = LogOutput::Stdout;
 123   TestLogSavedConfig tlsc;
 124 
 125   // Enable 'logging=info', verifying it has been set
 126   LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(logging));
 127   assert_str_eq("logging=info,", stdoutput->config_string());
 128   assert(log_is_enabled(Info, logging), "logging was not properly enabled");
 129 
 130   // Enable 'gc=debug' (no wildcard), verifying no other tags are enabled
 131   LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc));
 132   // No '+' character means only single tags are enabled, and no combinations
 133   assert_char_not_in('+', stdoutput->config_string());
 134   assert(log_is_enabled(Debug, gc), "logging was not properly enabled");
 135 
 136   // Enable 'gc*=trace' (with wildcard), verifying at least one tag combination is enabled (gc+...)
 137   LogConfiguration::configure_stdout(LogLevel::Trace, false, LOG_TAGS(gc));
 138   assert_char_in('+', stdoutput->config_string());
 139   assert(log_is_enabled(Trace, gc), "logging was not properly enabled");
 140 
 141   // Disable 'gc*' and 'logging', verifying all logging is properly disabled
 142   LogConfiguration::configure_stdout(LogLevel::Off, false, LOG_TAGS(gc));
 143   LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(logging));
 144   assert_str_eq("all=off", stdoutput->config_string());
 145 }
 146 
 147 static int Test_logconfiguration_subscribe_triggered = 0;
 148 
 149 static void Test_logconfiguration_subscribe_helper() {
 150   Test_logconfiguration_subscribe_triggered++;
 151 }
 152 
 153 void Test_logconfiguration_subscribe() {
 154   ResourceMark rm;
 155   Log(logging) log;
 156 
 157   TestLogSavedConfig log_cfg("stdout", "logging+test=trace");
 158 
 159   LogConfiguration::register_update_listener(&Test_logconfiguration_subscribe_helper);
 160 
 161   LogConfiguration::parse_log_arguments("stdout", "logging=trace", NULL, NULL, log.error_stream());
 162   assert(Test_logconfiguration_subscribe_triggered == 1, "subscription not triggered (1)");
 163 
 164   LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc));
 165   assert(Test_logconfiguration_subscribe_triggered == 2, "subscription not triggered (2)");
 166 
 167   LogConfiguration::disable_logging();
 168   assert(Test_logconfiguration_subscribe_triggered == 3, "subscription not triggered (3)");
 169 
 170   // We need to renable stderr error logging since "disable_logging" disable it all.
 171   // TestLogSavedConfig log_cfg will only renable stdout for us.
 172   LogConfiguration::parse_log_arguments("stderr", "all=warning", NULL, NULL, log.error_stream());
 173   assert(Test_logconfiguration_subscribe_triggered == 4, "subscription not triggered (3)");
 174 }
 175 
 176 #define LOG_PREFIX_STR "THE_PREFIX "
 177 #define LOG_LINE_STR "a log line"
 178 
 179 size_t Test_log_prefix_prefixer(char* buf, size_t len) {
 180   int ret = jio_snprintf(buf, len, LOG_PREFIX_STR);
 181   assert(ret > 0, "Failed to print prefix. Log buffer too small?");
 182   return (size_t) ret;
 183 }
 184 
 185 void Test_log_prefix() {
 186   ResourceMark rm;
 187   TestLogFile log_file("log_prefix");
 188   TestLogSavedConfig log_cfg(log_file.name(), "logging+test=trace");
 189 
 190   log_trace(logging, test)(LOG_LINE_STR);
 191 
 192   FILE* fp = fopen(log_file.name(), "r");
 193   assert(fp, "File read error");
 194   char output[1024];
 195   if (fgets(output, 1024, fp) != NULL) {
 196     assert(strstr(output, LOG_PREFIX_STR LOG_LINE_STR), "logging prefix error");
 197   }
 198   fclose(fp);
 199 }
 200 
 201 void Test_log_big() {
 202   char big_msg[4096] = {0};
 203   char Xchar = '~';
 204 
 205   ResourceMark rm;
 206   TestLogFile log_file("log_big");
 207   TestLogSavedConfig log_cfg(log_file.name(), "logging+test=trace");
 208 
 209   memset(big_msg, Xchar, sizeof(big_msg) - 1);
 210 
 211   log_trace(logging, test)("%s", big_msg);
 212 
 213   FILE* fp = fopen(log_file.name(), "r");
 214   assert(fp, "File read error");
 215   char output[sizeof(big_msg)+128 /*decorators*/ ];
 216   if (fgets(output, sizeof(output), fp) != NULL) {
 217     assert(strstr(output, LOG_PREFIX_STR), "logging prefix error");
 218     size_t count = 0;
 219     for (size_t ps = 0 ; output[ps + count] != '\0'; output[ps + count] == Xchar ? count++ : ps++);
 220     assert(count == (sizeof(big_msg) - 1) , "logging msg error");
 221   }
 222   fclose(fp);
 223 }
 224 
 225 void Test_logtagset_duplicates() {
 226   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
 227     char ts_name[512];
 228     ts->label(ts_name, sizeof(ts_name), ",");
 229 
 230     // verify that NO_TAG is never followed by a real tag
 231     for (size_t i = 0; i < LogTag::MaxTags; i++) {
 232       if (ts->tag(i) == LogTag::__NO_TAG) {
 233         for (i++; i < LogTag::MaxTags; i++) {
 234           assert(ts->tag(i) == LogTag::__NO_TAG,
 235                  "NO_TAG was followed by a real tag (%s) in tagset %s",
 236                  LogTag::name(ts->tag(i)), ts_name);
 237         }
 238       }
 239     }
 240 
 241     // verify that there are no duplicate tagsets (same tags in different order)
 242     for (LogTagSet* other = ts->next(); other != NULL; other = other->next()) {
 243       if (ts->ntags() != other->ntags()) {
 244         continue;
 245       }
 246       bool equal = true;
 247       for (size_t i = 0; i < ts->ntags(); i++) {
 248         LogTagType tag = ts->tag(i);
 249         if (!other->contains(tag)) {
 250           equal = false;
 251           break;
 252         }
 253       }
 254       // Since tagsets are implemented using template arguments, using both of
 255       // the (logically equivalent) tagsets (t1, t2) and (t2, t1) somewhere will
 256       // instantiate two different LogTagSetMappings. This causes multiple
 257       // tagset instances to be created for the same logical set. We want to
 258       // avoid this to save time, memory and prevent any confusion around it.
 259       if (equal) {
 260         char other_name[512];
 261         other->label(other_name, sizeof(other_name), ",");
 262         assert(false, "duplicate LogTagSets found: '%s' vs '%s' "
 263                "(tags must always be specified in the same order for each tagset)",
 264                ts_name, other_name);
 265       }
 266     }
 267   }
 268 }
 269 
 270 #endif // PRODUCT