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/logMessage.hpp" 52 #include "logging/logMessageBuffer.hpp" 53 #include "logging/logOutput.hpp" 54 #include "memory/resourceArea.hpp" 55 56 #define assert_str_eq(s1, s2) \ 57 assert(strcmp(s1, s2) == 0, "Expected '%s' to equal '%s'", s1, s2) 58 59 #define assert_char_in(c, s) \ 60 assert(strchr(s, c) != NULL, "Expected '%s' to contain character '%c'", s, c) 61 62 #define assert_char_not_in(c, s) \ 63 assert(strchr(s, c) == NULL, "Expected '%s' to *not* contain character '%c'", s, c) 64 65 static bool file_contains_substrings_in_order(const char* filename, const char* substrs[]) { 66 FILE* fp = fopen(filename, "r"); 67 assert(fp, "File read error"); 68 char buffer[1024]; 69 for (int i = 0; substrs[i] != NULL; i++) { 70 assert(sizeof(buffer) > strlen(substrs[i]), "insufficient buffer"); 71 bool found = false; 72 while (fgets(buffer, sizeof(buffer), fp) != NULL) { 73 if (strstr(buffer, substrs[i]) != NULL) { 74 found = true; 75 break; 76 } 77 } 78 if (!found) { 79 return false; 80 } 81 } 82 fclose(fp); 83 return true; 84 } 85 86 static bool file_contains_substring(const char* filename, const char* substr) { 87 const char* strs[] = {substr, NULL}; 88 return file_contains_substrings_in_order(filename, strs); 89 } 90 91 class TestLogFile { 92 private: 93 char file_name[256]; 94 95 void set_name(const char* test_name) { 96 const char* tmpdir = os::get_temp_directory(); 97 int pos = jio_snprintf(file_name, sizeof(file_name), "%s%svmtest.%s.%d.log", tmpdir, os::file_separator(), test_name, os::current_process_id()); 98 assert(pos > 0, "too small log file name buffer"); 99 assert((size_t)pos < sizeof(file_name), "too small log file name buffer"); 100 } 101 102 public: 103 TestLogFile(const char* test_name) { 104 set_name(test_name); 105 remove(name()); 106 } 107 108 ~TestLogFile() { 109 remove(name()); 110 } 111 112 const char* name() { 113 return file_name; 114 } 115 }; 116 117 class TestLogSavedConfig { 118 private: 119 char* _saved_config; 120 char* _new_output; 121 LogHandle(logging) _log; 122 public: 123 TestLogSavedConfig(const char* apply_output = NULL, const char* apply_setting = NULL) : _new_output(0) { 124 _saved_config = os::strdup_check_oom(LogOutput::Stdout->config_string()); 125 bool success = LogConfiguration::parse_log_arguments("stdout", "all=off", NULL, NULL, _log.error_stream()); 126 assert(success, "test unable to turn all off"); 127 128 if (apply_output) { 129 _new_output = os::strdup_check_oom(apply_output); 130 bool success = LogConfiguration::parse_log_arguments(_new_output, apply_setting, NULL, NULL, _log.error_stream()); 131 assert(success, "test unable to apply test log configuration"); 132 } 133 } 134 135 ~TestLogSavedConfig() { 136 if (_new_output) { 137 bool success = LogConfiguration::parse_log_arguments(_new_output, "all=off", NULL, NULL, _log.error_stream()); 138 assert(success, "test unable to turn all off"); 139 os::free(_new_output); 140 } 141 142 bool success = LogConfiguration::parse_log_arguments("stdout", _saved_config, NULL, NULL, _log.error_stream()); 143 assert(success, "test unable to restore log configuration"); 144 os::free(_saved_config); 145 } 146 }; 147 148 void Test_configure_stdout() { 149 ResourceMark rm; 150 LogOutput* stdoutput = LogOutput::Stdout; 151 TestLogSavedConfig tlsc; 152 153 // Enable 'logging=info', verifying it has been set 154 LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(logging)); 155 assert_str_eq("logging=info,", stdoutput->config_string()); 156 assert(log_is_enabled(Info, logging), "logging was not properly enabled"); 157 158 // Enable 'gc=debug' (no wildcard), verifying no other tags are enabled 159 LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc)); 160 // No '+' character means only single tags are enabled, and no combinations 161 assert_char_not_in('+', stdoutput->config_string()); 162 assert(log_is_enabled(Debug, gc), "logging was not properly enabled"); 163 164 // Enable 'gc*=trace' (with wildcard), verifying at least one tag combination is enabled (gc+...) 165 LogConfiguration::configure_stdout(LogLevel::Trace, false, LOG_TAGS(gc)); 166 assert_char_in('+', stdoutput->config_string()); 167 assert(log_is_enabled(Trace, gc), "logging was not properly enabled"); 168 169 // Disable 'gc*' and 'logging', verifying all logging is properly disabled 170 LogConfiguration::configure_stdout(LogLevel::Off, false, LOG_TAGS(gc)); 171 LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(logging)); 172 assert_str_eq("all=off", stdoutput->config_string()); 173 } 174 175 class LogMessageTest { 176 private: 177 static LogHandle(logging) _log; 178 static const char* _level_filename[]; 179 180 static void test_level_inclusion(); 181 static void test_long_message(); 182 static void test_message_with_many_lines(); 183 static void test_line_order(); 184 static void test_prefixing(); 185 static void test_scoped_messages(); 186 static void test_scoped_flushing(); 187 static void test_scoped_reset(); 188 189 public: 190 static void test(); 191 }; 192 193 const char* LogMessageTest::_level_filename[] = { 194 NULL, // LogLevel::Off 195 #define LOG_LEVEL(name, printname) "multiline-" #printname ".log", 196 LOG_LEVEL_LIST 197 #undef LOG_LEVEL 198 }; 199 200 void Test_multiline_logging() { 201 LogMessageTest::test(); 202 } 203 204 void LogMessageTest::test() { 205 ResourceMark rm; 206 207 for (int i = 0; i < LogLevel::Count; i++) { 208 char buf[32]; 209 // Attempt to remove possibly pre-existing log files 210 remove(_level_filename[i]); 211 212 jio_snprintf(buf, sizeof(buf), "logging=%s", LogLevel::name(static_cast<LogLevelType>(i))); 213 bool success = LogConfiguration::parse_log_arguments(_level_filename[i], buf, 214 NULL, NULL, _log.error_stream()); 215 assert(success, "unable to configure logging to file '%s'", _level_filename[i]); 216 } 217 218 test_level_inclusion(); 219 test_line_order(); 220 test_long_message(); 221 test_message_with_many_lines(); 222 test_prefixing(); 223 test_scoped_messages(); 224 test_scoped_flushing(); 225 test_scoped_reset(); 226 227 // Stop logging to the files and remove them. 228 for (int i = 0; i < LogLevel::Count; i++) { 229 LogConfiguration::parse_log_arguments(_level_filename[i], "all=off", NULL, NULL, _log.error_stream()); 230 remove(_level_filename[i]); 231 } 232 } 233 234 // Verify that messages with multiple levels are written 235 // to outputs configured for all the corresponding levels 236 void LogMessageTest::test_level_inclusion() { 237 const size_t message_count = 10; 238 LogMessageBuffer msg[message_count]; 239 240 struct { 241 int message_number; 242 LogLevelType level; 243 } lines[] = { 244 { 0, LogLevel::Error }, 245 { 1, LogLevel::Info }, 246 { 2, LogLevel::Info }, { 2, LogLevel::Debug }, 247 { 3, LogLevel::Info }, { 3, LogLevel::Warning }, 248 { 4, LogLevel::Debug }, { 4, LogLevel::Warning }, 249 { 5, LogLevel::Trace }, { 5, LogLevel::Debug }, 250 { 6, LogLevel::Warning }, { 6, LogLevel::Error }, 251 { 7, LogLevel::Trace }, { 7, LogLevel::Info }, { 7, LogLevel::Debug }, 252 { 8, LogLevel::Trace }, { 8, LogLevel::Debug }, { 8, LogLevel::Info }, 253 { 8, LogLevel::Warning }, { 8, LogLevel::Error}, 254 { 9, LogLevel::Trace } 255 }; 256 257 // Fill in messages with the above lines 258 for (size_t i = 0; i < ARRAY_SIZE(lines); i++) { 259 switch (lines[i].level) { 260 #define LOG_LEVEL(name, printname) \ 261 case LogLevel::name: \ 262 msg[lines[i].message_number].printname("msg[%d]: " #printname, lines[i].message_number); \ 263 break; 264 LOG_LEVEL_LIST 265 #undef LOG_LEVEL 266 } 267 } 268 269 for (size_t i = 0; i < message_count; i++) { 270 _log.write(msg[i]); 271 } 272 273 // Verify that lines are written to the expected log files 274 for (size_t i = 0; i < ARRAY_SIZE(lines); i++) { 275 char expected[256]; 276 jio_snprintf(expected, sizeof(expected), "msg[%d]: %s", 277 lines[i].message_number, LogLevel::name(lines[i].level)); 278 for (int level = lines[i].level; level > 0; level--) { 279 assert(file_contains_substring(_level_filename[level], expected), 280 "line #" SIZE_FORMAT " missing from log file '%s'", i, _level_filename[level]); 281 } 282 for (int level = lines[i].level + 1; level < LogLevel::Count; level++) { 283 assert(!file_contains_substring(_level_filename[level], expected), 284 "line #" SIZE_FORMAT " erroneously included in log file '%s'", i, _level_filename[level]); 285 } 286 } 287 } 288 289 // Verify that messages are logged in the order they are added to the log message 290 void LogMessageTest::test_line_order() { 291 LogMessageBuffer msg; 292 msg.info("info line").error("error line").trace("trace line") 293 .error("another error").warning("warning line").debug("debug line"); 294 _log.write(msg); 295 296 const char* expected[] = { "info line", "error line", "trace line", 297 "another error", "warning line", "debug line", NULL }; 298 assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected), 299 "output missing or in incorrect order"); 300 } 301 302 void LogMessageTest::test_long_message() { 303 // Write 10K bytes worth of log data 304 LogMessageBuffer msg; 305 const size_t size = 10 * K; 306 const char* start_marker = "#start#"; 307 const char* end_marker = "#the end#"; 308 char* data = NEW_C_HEAP_ARRAY(char, size, mtLogging); 309 310 // fill buffer with start_marker...some data...end_marker 311 sprintf(data, "%s", start_marker); 312 for (size_t i = strlen(start_marker); i < size; i++) { 313 data[i] = '0' + (i % 10); 314 } 315 sprintf(data + size - strlen(end_marker) - 1, "%s", end_marker); 316 317 msg.trace("%s", data); // Adds a newline, making the message exactly 10K in length. 318 _log.write(msg); 319 320 const char* expected[] = { start_marker, "0123456789", end_marker, NULL }; 321 assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected), 322 "unable to print long line"); 323 FREE_C_HEAP_ARRAY(char, data); 324 } 325 326 void LogMessageTest::test_message_with_many_lines() { 327 const size_t lines = 100; 328 const size_t line_length = 16; 329 330 LogMessageBuffer msg; 331 for (size_t i = 0; i < lines; i++) { 332 msg.info("Line #" SIZE_FORMAT, i); 333 } 334 _log.write(msg); 335 336 char expected_lines_data[lines][line_length]; 337 const char* expected_lines[lines + 1]; 338 for (size_t i = 0; i < lines; i++) { 339 jio_snprintf(&expected_lines_data[i][0], line_length, "Line #" SIZE_FORMAT, i); 340 expected_lines[i] = expected_lines_data[i]; 341 } 342 expected_lines[lines] = NULL; 343 344 assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected_lines), 345 "couldn't find all lines in multiline message"); 346 } 347 348 static size_t dummy_prefixer(char* buf, size_t len) { 349 static int i = 0; 350 const char* prefix = "some prefix: "; 351 const size_t prefix_len = strlen(prefix); 352 if (len < prefix_len) { 353 return prefix_len; 354 } 355 jio_snprintf(buf, len, "%s", prefix); 356 return prefix_len; 357 } 358 359 void LogMessageTest::test_prefixing() { 360 LogMessageBuffer msg; 361 msg.set_prefix(dummy_prefixer); 362 for (int i = 0; i < 3; i++) { 363 msg.info("test %d", i); 364 } 365 msg.set_prefix(NULL); 366 msg.info("test 3"); 367 _log.write(msg); 368 369 const char* expected[] = { 370 "] some prefix: test 0", 371 "] some prefix: test 1", 372 "] some prefix: test 2", 373 "] test 3", 374 NULL 375 }; 376 assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected), "error in prefixed output"); 377 } 378 379 void LogMessageTest::test_scoped_messages() { 380 { 381 LogMessage(logging) msg; 382 msg.info("scoped info"); 383 msg.warning("scoped warn"); 384 assert(!file_contains_substring(_level_filename[LogLevel::Info], "scoped info"), 385 "scoped log message written prematurely"); 386 } 387 assert(file_contains_substring(_level_filename[LogLevel::Info], "scoped info"), 388 "missing output from scoped log message"); 389 assert(file_contains_substring(_level_filename[LogLevel::Warning], "scoped warn"), 390 "missing output from scoped log message"); 391 } 392 393 void LogMessageTest::test_scoped_flushing() { 394 { 395 LogMessage(logging) msg; 396 msg.info("manual flush info"); 397 msg.flush(); 398 assert(file_contains_substring(_level_filename[LogLevel::Info], "manual flush info"), 399 "missing output from manually flushed scoped log message"); 400 } 401 const char* tmp[] = {"manual flush info", "manual flush info", NULL}; 402 assert(!file_contains_substrings_in_order(_level_filename[LogLevel::Info], tmp), 403 "log file contains duplicate lines from single scoped log message"); 404 } 405 406 void LogMessageTest::test_scoped_reset() { 407 { 408 LogMessage(logging) msg, partial; 409 msg.info("%s", "info reset msg"); 410 msg.reset(); 411 partial.info("%s", "info reset msg"); 412 partial.reset(); 413 partial.trace("%s", "trace reset msg"); 414 } 415 assert(!file_contains_substring(_level_filename[LogLevel::Info], "info reset msg"), 416 "reset message written anyway"); 417 assert(file_contains_substring(_level_filename[LogLevel::Trace], "trace reset msg"), 418 "missing message from partially reset scoped log message"); 419 } 420 421 422 static int Test_logconfiguration_subscribe_triggered = 0; 423 424 static void Test_logconfiguration_subscribe_helper() { 425 Test_logconfiguration_subscribe_triggered++; 426 } 427 428 void Test_logconfiguration_subscribe() { 429 ResourceMark rm; 430 LogHandle(logging) log; 431 432 TestLogSavedConfig log_cfg("stdout", "logging+test=trace"); 433 434 LogConfiguration::register_update_listener(&Test_logconfiguration_subscribe_helper); 435 436 LogConfiguration::parse_log_arguments("stdout", "logging=trace", NULL, NULL, log.error_stream()); 437 assert(Test_logconfiguration_subscribe_triggered == 1, "subscription not triggered (1)"); 438 439 LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc)); 440 assert(Test_logconfiguration_subscribe_triggered == 2, "subscription not triggered (2)"); 441 442 LogConfiguration::disable_logging(); 443 assert(Test_logconfiguration_subscribe_triggered == 3, "subscription not triggered (3)"); 444 445 // We need to renable stderr error logging since "disable_logging" disable it all. 446 // TestLogSavedConfig log_cfg will only renable stdout for us. 447 LogConfiguration::parse_log_arguments("stderr", "all=warning", NULL, NULL, log.error_stream()); 448 assert(Test_logconfiguration_subscribe_triggered == 4, "subscription not triggered (3)"); 449 } 450 451 #define LOG_PREFIX_STR "THE_PREFIX " 452 #define LOG_LINE_STR "a log line" 453 454 size_t Test_log_prefix_prefixer(char* buf, size_t len) { 455 int ret = jio_snprintf(buf, len, LOG_PREFIX_STR); 456 assert(ret > 0, "Failed to print prefix. Log buffer too small?"); 457 return (size_t) ret; 458 } 459 460 void Test_log_prefix() { 461 ResourceMark rm; 462 TestLogFile log_file("log_prefix"); 463 TestLogSavedConfig log_cfg(log_file.name(), "logging+test=trace"); 464 465 log_trace(logging, test)(LOG_LINE_STR); 466 467 FILE* fp = fopen(log_file.name(), "r"); 468 assert(fp, "File read error"); 469 char output[1024]; 470 if (fgets(output, 1024, fp) != NULL) { 471 assert(strstr(output, LOG_PREFIX_STR LOG_LINE_STR), "logging prefix error"); 472 } 473 fclose(fp); 474 } 475 476 void Test_log_big() { 477 char big_msg[4096] = {0}; 478 char Xchar = '~'; 479 480 ResourceMark rm; 481 TestLogFile log_file("log_big"); 482 TestLogSavedConfig log_cfg(log_file.name(), "logging+test=trace"); 483 484 memset(big_msg, Xchar, sizeof(big_msg) - 1); 485 486 log_trace(logging, test)("%s", big_msg); 487 488 FILE* fp = fopen(log_file.name(), "r"); 489 assert(fp, "File read error"); 490 char output[sizeof(big_msg)+128 /*decorators*/ ]; 491 if (fgets(output, sizeof(output), fp) != NULL) { 492 assert(strstr(output, LOG_PREFIX_STR), "logging prefix error"); 493 size_t count = 0; 494 for (size_t ps = 0 ; output[ps + count] != '\0'; output[ps + count] == Xchar ? count++ : ps++); 495 assert(count == (sizeof(big_msg) - 1) , "logging msg error"); 496 } 497 fclose(fp); 498 } 499 500 void Test_logtagset_duplicates() { 501 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 502 char ts_name[512]; 503 ts->label(ts_name, sizeof(ts_name), ","); 504 505 // verify that NO_TAG is never followed by a real tag 506 for (size_t i = 0; i < LogTag::MaxTags; i++) { 507 if (ts->tag(i) == LogTag::__NO_TAG) { 508 for (i++; i < LogTag::MaxTags; i++) { 509 assert(ts->tag(i) == LogTag::__NO_TAG, 510 "NO_TAG was followed by a real tag (%s) in tagset %s", 511 LogTag::name(ts->tag(i)), ts_name); 512 } 513 } 514 } 515 516 // verify that there are no duplicate tagsets (same tags in different order) 517 for (LogTagSet* other = ts->next(); other != NULL; other = other->next()) { 518 if (ts->ntags() != other->ntags()) { 519 continue; 520 } 521 bool equal = true; 522 for (size_t i = 0; i < ts->ntags(); i++) { 523 LogTagType tag = ts->tag(i); 524 if (!other->contains(tag)) { 525 equal = false; 526 break; 527 } 528 } 529 // Since tagsets are implemented using template arguments, using both of 530 // the (logically equivalent) tagsets (t1, t2) and (t2, t1) somewhere will 531 // instantiate two different LogTagSetMappings. This causes multiple 532 // tagset instances to be created for the same logical set. We want to 533 // avoid this to save time, memory and prevent any confusion around it. 534 if (equal) { 535 char other_name[512]; 536 other->label(other_name, sizeof(other_name), ","); 537 assert(false, "duplicate LogTagSets found: '%s' vs '%s' " 538 "(tags must always be specified in the same order for each tagset)", 539 ts_name, other_name); 540 } 541 } 542 } 543 } 544 545 #endif // PRODUCT