1 /*
2  * Copyright (c) 2014, 2019, 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 #ifndef SHARE_SERVICES_MALLOCSITETABLE_HPP
26 #define SHARE_SERVICES_MALLOCSITETABLE_HPP
27 
28 #include "utilities/macros.hpp"
29 
30 #if INCLUDE_NMT
31 
32 #include "memory/allocation.hpp"
33 #include "runtime/atomic.hpp"
34 #include "services/allocationSite.hpp"
35 #include "services/mallocTracker.hpp"
36 #include "services/nmtCommon.hpp"
37 #include "utilities/nativeCallStack.hpp"
38 
39 // MallocSite represents a code path that eventually calls
40 // os::malloc() to allocate memory
41 class MallocSite : public AllocationSite {
42   MemoryCounter _c;
43  public:
MallocSite(const NativeCallStack & stack,MEMFLAGS flags)44   MallocSite(const NativeCallStack& stack, MEMFLAGS flags) :
45     AllocationSite(stack, flags) {}
46 
allocate(size_t size)47   void allocate(size_t size)      { _c.allocate(size);   }
deallocate(size_t size)48   void deallocate(size_t size)    { _c.deallocate(size); }
49 
50   // Memory allocated from this code path
size() const51   size_t size()  const { return _c.size(); }
52   // The number of calls were made
count() const53   size_t count() const { return _c.count(); }
54 };
55 
56 // Malloc site hashtable entry
57 class MallocSiteHashtableEntry : public CHeapObj<mtNMT> {
58  private:
59   MallocSite                         _malloc_site;
60   const unsigned int                 _hash;
61   MallocSiteHashtableEntry* volatile _next;
62 
63  public:
64 
MallocSiteHashtableEntry(NativeCallStack stack,MEMFLAGS flags)65   MallocSiteHashtableEntry(NativeCallStack stack, MEMFLAGS flags):
66     _malloc_site(stack, flags), _hash(stack.calculate_hash()), _next(NULL) {
67     assert(flags != mtNone, "Expect a real memory type");
68   }
69 
next() const70   inline const MallocSiteHashtableEntry* next() const {
71     return _next;
72   }
73 
74   // Insert an entry atomically.
75   // Return true if the entry is inserted successfully.
76   // The operation can be failed due to contention from other thread.
77   bool atomic_insert(MallocSiteHashtableEntry* entry);
78 
hash() const79   unsigned int hash() const { return _hash; }
80 
peek() const81   inline const MallocSite* peek() const { return &_malloc_site; }
data()82   inline MallocSite* data()             { return &_malloc_site; }
83 
84   // Allocation/deallocation on this allocation site
allocate(size_t size)85   inline void allocate(size_t size)   { _malloc_site.allocate(size);   }
deallocate(size_t size)86   inline void deallocate(size_t size) { _malloc_site.deallocate(size); }
87   // Memory counters
size() const88   inline size_t size() const  { return _malloc_site.size();  }
count() const89   inline size_t count() const { return _malloc_site.count(); }
90 };
91 
92 // The walker walks every entry on MallocSiteTable
93 class MallocSiteWalker : public StackObj {
94  public:
do_malloc_site(const MallocSite * e)95    virtual bool do_malloc_site(const MallocSite* e) { return false; }
96 };
97 
98 /*
99  * Native memory tracking call site table.
100  * The table is only needed when detail tracking is enabled.
101  */
102 class MallocSiteTable : AllStatic {
103  private:
104   // The number of hash bucket in this hashtable. The number should
105   // be tuned if malloc activities changed significantly.
106   // The statistics data can be obtained via Jcmd
107   // jcmd <pid> VM.native_memory statistics.
108 
109   // Currently, (number of buckets / number of entires) ratio is
110   // about 1 / 6
111   enum {
112     table_base_size = 128,   // The base size is calculated from statistics to give
113                              // table ratio around 1:6
114     table_size = (table_base_size * NMT_TrackingStackDepth - 1)
115   };
116 
117 
118   // This is a very special lock, that allows multiple shared accesses (sharedLock), but
119   // once exclusive access (exclusiveLock) is requested, all shared accesses are
120   // rejected forever.
121   class AccessLock : public StackObj {
122     enum LockState {
123       NoLock,
124       SharedLock,
125       ExclusiveLock
126     };
127 
128    private:
129     // A very large negative number. The only possibility to "overflow"
130     // this number is when there are more than -min_jint threads in
131     // this process, which is not going to happen in foreseeable future.
132     const static int _MAGIC_ = min_jint;
133 
134     LockState      _lock_state;
135     volatile int*  _lock;
136    public:
AccessLock(volatile int * lock)137     AccessLock(volatile int* lock) :
138       _lock_state(NoLock), _lock(lock) {
139     }
140 
~AccessLock()141     ~AccessLock() {
142       if (_lock_state == SharedLock) {
143         Atomic::dec(_lock);
144       }
145     }
146     // Acquire shared lock.
147     // Return true if shared access is granted.
sharedLock()148     inline bool sharedLock() {
149       jint res = Atomic::add(_lock, 1);
150       if (res < 0) {
151         Atomic::dec(_lock);
152         return false;
153       }
154       _lock_state = SharedLock;
155       return true;
156     }
157     // Acquire exclusive lock
158     void exclusiveLock();
159  };
160 
161  public:
162   static bool initialize();
163   static void shutdown();
164 
NOT_PRODUCT(static int access_peak_count (){ return _peak_count; })165   NOT_PRODUCT(static int access_peak_count() { return _peak_count; })
166 
167   // Number of hash buckets
168   static inline int hash_buckets()      { return (int)table_size; }
169 
170   // Access and copy a call stack from this table. Shared lock should be
171   // acquired before access the entry.
access_stack(NativeCallStack & stack,size_t bucket_idx,size_t pos_idx)172   static inline bool access_stack(NativeCallStack& stack, size_t bucket_idx,
173     size_t pos_idx) {
174     AccessLock locker(&_access_count);
175     if (locker.sharedLock()) {
176       NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
177       MallocSite* site = malloc_site(bucket_idx, pos_idx);
178       if (site != NULL) {
179         stack = *site->call_stack();
180         return true;
181       }
182     }
183     return false;
184   }
185 
186   // Record a new allocation from specified call path.
187   // Return true if the allocation is recorded successfully, bucket_idx
188   // and pos_idx are also updated to indicate the entry where the allocation
189   // information was recorded.
190   // Return false only occurs under rare scenarios:
191   //  1. out of memory
192   //  2. overflow hash bucket
allocation_at(const NativeCallStack & stack,size_t size,size_t * bucket_idx,size_t * pos_idx,MEMFLAGS flags)193   static inline bool allocation_at(const NativeCallStack& stack, size_t size,
194     size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags) {
195     AccessLock locker(&_access_count);
196     if (locker.sharedLock()) {
197       NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
198       MallocSite* site = lookup_or_add(stack, bucket_idx, pos_idx, flags);
199       if (site != NULL) site->allocate(size);
200       return site != NULL;
201     }
202     return false;
203   }
204 
205   // Record memory deallocation. bucket_idx and pos_idx indicate where the allocation
206   // information was recorded.
deallocation_at(size_t size,size_t bucket_idx,size_t pos_idx)207   static inline bool deallocation_at(size_t size, size_t bucket_idx, size_t pos_idx) {
208     AccessLock locker(&_access_count);
209     if (locker.sharedLock()) {
210       NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
211       MallocSite* site = malloc_site(bucket_idx, pos_idx);
212       if (site != NULL) {
213         site->deallocate(size);
214         return true;
215       }
216     }
217     return false;
218   }
219 
220   // Walk this table.
221   static bool walk_malloc_site(MallocSiteWalker* walker);
222 
223   static void print_tuning_statistics(outputStream* st);
224 
225  private:
226   static MallocSiteHashtableEntry* new_entry(const NativeCallStack& key, MEMFLAGS flags);
227   static void reset();
228 
229   // Delete a bucket linked list
230   static void delete_linked_list(MallocSiteHashtableEntry* head);
231 
232   static MallocSite* lookup_or_add(const NativeCallStack& key, size_t* bucket_idx, size_t* pos_idx, MEMFLAGS flags);
233   static MallocSite* malloc_site(size_t bucket_idx, size_t pos_idx);
234   static bool walk(MallocSiteWalker* walker);
235 
hash_to_index(unsigned int hash)236   static inline unsigned int hash_to_index(unsigned int hash) {
237     return (hash % table_size);
238   }
239 
hash_entry_allocation_stack()240   static inline const NativeCallStack* hash_entry_allocation_stack() {
241     assert(_hash_entry_allocation_stack != NULL, "Must be set");
242     return _hash_entry_allocation_stack;
243   }
244 
hash_entry_allocation_site()245   static inline const MallocSiteHashtableEntry* hash_entry_allocation_site() {
246     assert(_hash_entry_allocation_site != NULL, "Must be set");
247     return _hash_entry_allocation_site;
248   }
249 
250  private:
251   // Counter for counting concurrent access
252   static volatile int                _access_count;
253 
254   // The callsite hashtable. It has to be a static table,
255   // since malloc call can come from C runtime linker.
256   static MallocSiteHashtableEntry*        _table[table_size];
257   static const NativeCallStack*           _hash_entry_allocation_stack;
258   static const MallocSiteHashtableEntry*  _hash_entry_allocation_site;
259 
260 
261   NOT_PRODUCT(static int     _peak_count;)
262 };
263 
264 #endif // INCLUDE_NMT
265 #endif // SHARE_SERVICES_MALLOCSITETABLE_HPP
266