1 /*
   2  * Copyright (c) 2016, 2017, 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 #include "precompiled.hpp"
  25 #include "logTestUtils.inline.hpp"
  26 #include "logging/logFileOutput.hpp"
  27 #include "memory/resourceArea.hpp"
  28 #include "prims/jvm.h"
  29 #include "runtime/os.hpp"
  30 #include "unittest.hpp"
  31 #include "utilities/globalDefinitions.hpp"
  32 #include "utilities/ostream.hpp"
  33 
  34 static const char* name = "file=testlog.pid%p.%t.log";
  35 
  36 // Test parsing a bunch of valid file output options
  37 TEST_VM(LogFileOutput, parse_valid) {
  38   const char* valid_options[] = {
  39     "", "filecount=10", "filesize=512",
  40     "filecount=11,filesize=256",
  41     "filesize=256,filecount=11",
  42     "filesize=0", "filecount=1",
  43     "filesize=1m", "filesize=1M",
  44     "filesize=1k", "filesize=1G"
  45   };
  46 
  47   // Override LogOutput's vm_start time to get predictable file name
  48   LogFileOutput::set_file_name_parameters(0);
  49   char expected_filename[1 * K];
  50   int ret = jio_snprintf(expected_filename, sizeof(expected_filename),
  51                          "testlog.pid%d.1970-01-01_01-00-00.log",
  52                          os::current_process_id());
  53   ASSERT_GT(ret, 0) << "Buffer too small";
  54 
  55   for (size_t i = 0; i < ARRAY_SIZE(valid_options); i++) {
  56     ResourceMark rm;
  57     stringStream ss;
  58     {
  59       LogFileOutput fo(name);
  60       EXPECT_STREQ(name, fo.name());
  61       EXPECT_TRUE(fo.initialize(valid_options[i], &ss))
  62         << "Did not accept valid option(s) '" << valid_options[i] << "': " << ss.as_string();
  63     }
  64     remove(expected_filename);
  65   }
  66 }
  67 
  68 // Test parsing a bunch of invalid file output options
  69 TEST_VM(LogFileOutput, parse_invalid) {
  70   const char* invalid_options[] = {
  71     "invalidopt", "filecount=",
  72     "filesize=,filecount=10",
  73     "fileco=10", "ilesize=512",
  74     "filecount=11,,filesize=256",
  75     ",filesize=256,filecount=11",
  76     "filesize=256,filecount=11,",
  77     "filesize=-1", "filecount=0.1",
  78     "filecount=-2", "filecount=2.0",
  79     "filecount= 2", "filesize=2 ",
  80     "filecount=ab", "filesize=0xz",
  81     "filecount=1MB", "filesize=99bytes",
  82     "filesize=9999999999999999999999999"
  83     "filecount=9999999999999999999999999"
  84   };
  85 
  86   for (size_t i = 0; i < ARRAY_SIZE(invalid_options); i++) {
  87     ResourceMark rm;
  88     stringStream ss;
  89     LogFileOutput fo(name);
  90     EXPECT_FALSE(fo.initialize(invalid_options[i], &ss))
  91       << "Accepted invalid option(s) '" << invalid_options[i] << "': " << ss.as_string();
  92   }
  93 }
  94 
  95 // Test for overflows with filesize
  96 TEST_VM(LogFileOutput, filesize_overflow) {
  97   char buf[256];
  98   int ret = jio_snprintf(buf, sizeof(buf), "filesize=" SIZE_FORMAT "K", SIZE_MAX);
  99   ASSERT_GT(ret, 0) << "Buffer too small";
 100 
 101   ResourceMark rm;
 102   stringStream ss;
 103   LogFileOutput fo(name);
 104   EXPECT_FALSE(fo.initialize(buf, &ss)) << "Accepted filesize that overflows";
 105 }
 106 
 107 TEST_VM(LogFileOutput, startup_rotation) {
 108   const size_t rotations = 5;
 109   const char* filename = "start-rotate-test";
 110   char* rotated_file[rotations];
 111 
 112   ResourceMark rm;
 113   for (size_t i = 0; i < rotations; i++) {
 114     size_t len = strlen(filename) + 3;
 115     rotated_file[i] = NEW_RESOURCE_ARRAY(char, len);
 116     int ret = jio_snprintf(rotated_file[i], len, "%s." SIZE_FORMAT, filename, i);
 117     ASSERT_NE(-1, ret);
 118     delete_file(rotated_file[i]);
 119   }
 120 
 121   delete_file(filename);
 122   init_log_file(filename);
 123   ASSERT_TRUE(file_exists(filename))
 124     << "configured logging to file '" << filename << "' but file was not found";
 125 
 126   // Initialize the same file a bunch more times to trigger rotations
 127   for (size_t i = 0; i < rotations; i++) {
 128     init_log_file(filename);
 129     EXPECT_TRUE(file_exists(rotated_file[i]));
 130   }
 131 
 132   // Remove a file and expect its slot to be re-used
 133   delete_file(rotated_file[1]);
 134   init_log_file(filename);
 135   EXPECT_TRUE(file_exists(rotated_file[1]));
 136 
 137   // Clean up after test
 138   delete_file(filename);
 139   for (size_t i = 0; i < rotations; i++) {
 140     delete_file(rotated_file[i]);
 141   }
 142 }
 143 
 144 TEST_VM(LogFileOutput, startup_truncation) {
 145   const char* filename = "start-truncate-test";
 146   const char* archived_filename = "start-truncate-test.0";
 147 
 148   delete_file(filename);
 149   delete_file(archived_filename);
 150 
 151   // Use the same log file twice and expect it to be overwritten/truncated
 152   init_log_file(filename, "filecount=0");
 153   ASSERT_TRUE(file_exists(filename))
 154     << "configured logging to file '" << filename << "' but file was not found";
 155 
 156   init_log_file(filename, "filecount=0");
 157   ASSERT_TRUE(file_exists(filename))
 158     << "configured logging to file '" << filename << "' but file was not found";
 159   EXPECT_FALSE(file_exists(archived_filename))
 160     << "existing log file was not properly truncated when filecount was 0";
 161 
 162   // Verify that the file was really truncated and not just appended
 163   EXPECT_TRUE(file_contains_substring(filename, LOG_TEST_STRING_LITERAL));
 164   const char* repeated[] = { LOG_TEST_STRING_LITERAL, LOG_TEST_STRING_LITERAL };
 165   EXPECT_FALSE(file_contains_substrings_in_order(filename, repeated))
 166     << "log file " << filename << " appended rather than truncated";
 167 
 168   delete_file(filename);
 169   delete_file(archived_filename);
 170 }
 171 
 172 TEST_VM(LogFileOutput, invalid_file) {
 173   ResourceMark rm;
 174   stringStream ss;
 175 
 176   // Attempt to log to a directory (existing log not a regular file)
 177   create_directory("tmplogdir");
 178   LogFileOutput bad_file("file=tmplogdir");
 179   EXPECT_FALSE(bad_file.initialize("", &ss))
 180     << "file was initialized when there was an existing directory with the same name";
 181   EXPECT_TRUE(string_contains_substring(ss.as_string(), "tmplogdir is not a regular file"))
 182     << "missing expected error message, received msg: %s" << ss.as_string();
 183   delete_empty_directory("tmplogdir");
 184 }