1 /*
  2  * Copyright (c) 2016, 2023, 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 "classfile/symbolTable.hpp"
 26 #include "runtime/interfaceSupport.inline.hpp"
 27 #include "threadHelper.inline.hpp"
 28 #include "unittest.hpp"
 29 
 30 // Helper to avoid interference from the cleanup delay queue by draining it
 31 // immediately after creation.
 32 TempNewSymbol stable_temp_symbol(Symbol* sym) {
 33   TempNewSymbol t = sym;
 34   TempSymbolCleanupDelayer::drain_queue();
 35   return t;
 36 }
 37 
 38 TEST_VM(SymbolTable, temp_new_symbol) {
 39   // Assert messages assume these symbols are unique, and the refcounts start at
 40   // one, but code does not rely on this.
 41   JavaThread* THREAD = JavaThread::current();
 42   // the thread should be in vm to use locks
 43   ThreadInVMfromNative ThreadInVMfromNative(THREAD);
 44 
 45   Symbol* abc = SymbolTable::new_symbol("abc");
 46   int abccount = abc->refcount();
 47   TempNewSymbol ss = stable_temp_symbol(abc);
 48   ASSERT_EQ(ss->refcount(), abccount) << "only one abc";
 49   ASSERT_EQ(ss->refcount(), abc->refcount()) << "should match TempNewSymbol";
 50 
 51   Symbol* efg = SymbolTable::new_symbol("efg");
 52   Symbol* hij = SymbolTable::new_symbol("hij");
 53   int efgcount = efg->refcount();
 54   int hijcount = hij->refcount();
 55 
 56   TempNewSymbol s1 = stable_temp_symbol(efg);
 57   TempNewSymbol s2 = stable_temp_symbol(hij);
 58   ASSERT_EQ(s1->refcount(), efgcount) << "one efg";
 59   ASSERT_EQ(s2->refcount(), hijcount) << "one hij";
 60 
 61   // Assignment operator
 62   s1 = s2;
 63   ASSERT_EQ(hij->refcount(), hijcount + 1) << "should be two hij";
 64   ASSERT_EQ(efg->refcount(), efgcount - 1) << "should be no efg";
 65 
 66   s1 = ss; // s1 is abc
 67   ASSERT_EQ(s1->refcount(), abccount + 1) << "should be two abc (s1 and ss)";
 68   ASSERT_EQ(hij->refcount(), hijcount) << "should only have one hij now (s2)";
 69 
 70   s1 = *&s1; // self assignment
 71   ASSERT_EQ(s1->refcount(), abccount + 1) << "should still be two abc (s1 and ss)";
 72 
 73   TempNewSymbol s3;
 74   Symbol* klm = SymbolTable::new_symbol("klm");
 75   int klmcount = klm->refcount();
 76   s3 = stable_temp_symbol(klm); // assignment
 77   ASSERT_EQ(s3->refcount(), klmcount) << "only one klm now";
 78 
 79   Symbol* xyz = SymbolTable::new_symbol("xyz");
 80   int xyzcount = xyz->refcount();
 81   { // inner scope
 82     TempNewSymbol s_inner = stable_temp_symbol(xyz);
 83   }
 84   ASSERT_EQ(xyz->refcount(), xyzcount - 1)
 85           << "Should have been decremented by dtor in inner scope";
 86 
 87   // Test overflowing refcount making symbol permanent
 88   Symbol* bigsym = SymbolTable::new_symbol("bigsym");
 89   for (int i = 0; i < PERM_REFCOUNT + 100; i++) {
 90     bigsym->increment_refcount();
 91   }
 92   ASSERT_EQ(bigsym->refcount(), PERM_REFCOUNT) << "should not have overflowed";
 93 
 94   // Test that PERM_REFCOUNT is sticky
 95   for (int i = 0; i < 10; i++) {
 96     bigsym->decrement_refcount();
 97   }
 98   ASSERT_EQ(bigsym->refcount(), PERM_REFCOUNT) << "should be sticky";
 99 }
100 
101 // TODO: Make two threads one decrementing the refcount and the other trying to increment.
102 // try_increment_refcount should return false
103 
104 TEST_VM(SymbolTable, test_symbol_refcount_parallel) {
105   constexpr int symbol_name_length = 30;
106   char symbol_name[symbol_name_length];
107   // Find a symbol where there will probably be only one instance.
108   for (int i = 0; i < 100; i++) {
109     os::snprintf(symbol_name, symbol_name_length, "some_symbol%d", i);
110     TempNewSymbol ts = SymbolTable::new_symbol(symbol_name);
111     if (ts->refcount() == 1) {
112       EXPECT_TRUE(ts->refcount() == 1) << "Symbol is just created";
113       break;  // found a unique symbol
114     }
115   }
116 
117   constexpr int symTestThreadCount = 5;
118   auto symbolThread= [&](Thread* _current, int _id) {
119     for (int i = 0; i < 1000; i++) {
120       TempNewSymbol sym = SymbolTable::new_symbol(symbol_name);
121       // Create and destroy new symbol
122       EXPECT_TRUE(sym->refcount() != 0) << "Symbol refcount unexpectedly zeroed";
123     }
124   };
125   TestThreadGroup<decltype(symbolThread)> ttg(symbolThread, symTestThreadCount);
126   ttg.doit();
127   ttg.join();
128 }
129 
130 TEST_VM_FATAL_ERROR_MSG(SymbolTable, test_symbol_underflow, ".*refcount has gone to zero.*") {
131   Symbol* my_symbol = SymbolTable::new_symbol("my_symbol2023");
132   EXPECT_TRUE(my_symbol->refcount() == 1) << "Symbol refcount just created is 1";
133   my_symbol->decrement_refcount();
134   my_symbol->increment_refcount();  // Should crash even in PRODUCT mode
135 }
136 
137 TEST_VM(SymbolTable, test_cleanup_leak) {
138   // Check that dead entry cleanup doesn't increment refcount of live entry in same bucket.
139 
140   // Create symbol and release ref, marking it available for cleanup.
141   Symbol* entry1 = SymbolTable::new_symbol("hash_collision_123");
142   entry1->decrement_refcount();
143 
144   // Create a new symbol in the same bucket, which will notice the dead entry and trigger cleanup.
145   // Note: relies on SymbolTable's use of String::hashCode which collides for these two values.
146   Symbol* entry2 = SymbolTable::new_symbol("hash_collision_397476851");
147 
148   ASSERT_EQ(entry2->refcount(), 1) << "Symbol refcount just created is 1";
149 }
150 
151 TEST_VM(SymbolTable, test_cleanup_delay) {
152   // Check that new temp symbols have an extra refcount increment, which is then
153   // decremented when the queue spills over.
154 
155   TempNewSymbol s1 = SymbolTable::new_symbol("temp-s1");
156   ASSERT_EQ(s1->refcount(), 2) << "TempNewSymbol refcount just created is 2";
157 
158   // Fill up the queue
159   constexpr int symbol_name_length = 30;
160   char symbol_name[symbol_name_length];
161   for (uint i = 1; i < TempSymbolCleanupDelayer::QueueSize; i++) {
162     os::snprintf(symbol_name, symbol_name_length, "temp-filler-%d", i);
163     TempNewSymbol s = SymbolTable::new_symbol(symbol_name);
164     ASSERT_EQ(s->refcount(), 2) << "TempNewSymbol refcount just created is 2";
165   }
166 
167   // Add one more
168   TempNewSymbol spillover = SymbolTable::new_symbol("temp-spillover");
169   ASSERT_EQ(spillover->refcount(), 2) << "TempNewSymbol refcount just created is 2";
170 
171   // The first symbol should have been removed from the queue and decremented
172   ASSERT_EQ(s1->refcount(), 1) << "TempNewSymbol off queue refcount is 1";
173 }
174 
175 TEST_VM(SymbolTable, test_cleanup_delay_drain) {
176   // Fill up the queue
177   constexpr int symbol_name_length = 30;
178   char symbol_name[symbol_name_length];
179   TempNewSymbol symbols[TempSymbolCleanupDelayer::QueueSize] = {};
180   for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
181     os::snprintf(symbol_name, symbol_name_length, "temp-%d", i);
182     TempNewSymbol s = SymbolTable::new_symbol(symbol_name);
183     symbols[i] = s;
184   }
185 
186   // While in the queue refcounts are incremented
187   for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
188     ASSERT_EQ(symbols[i]->refcount(), 2) << "TempNewSymbol refcount in queue is 2";
189   }
190 
191   // Draining the queue should decrement the refcounts
192   TempSymbolCleanupDelayer::drain_queue();
193   for (uint i = 0; i < TempSymbolCleanupDelayer::QueueSize; i++) {
194     ASSERT_EQ(symbols[i]->refcount(), 1) << "TempNewSymbol refcount after drain is 1";
195   }
196 }