1 /*****************************************************************************
2 
3 Copyright (c) 2014, 2016, Oracle and/or its affiliates. All Rights Reserved.
4 Copyright (c) 2019, MariaDB Corporation.
5 
6 This program is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free Software
8 Foundation; version 2 of the License.
9 
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
17 
18 *****************************************************************************/
19 
20 /**************************************************//**
21 @file ut/ut0new.cc
22 Instrumented memory allocator.
23 
24 Created May 26, 2014 Vasil Dimov
25 *******************************************************/
26 
27 #include "univ.i"
28 
29 /** Maximum number of retries to allocate memory. */
30 const size_t	alloc_max_retries = 60;
31 
32 /** Keys for registering allocations with performance schema.
33 Keep this list alphabetically sorted. */
34 #ifdef BTR_CUR_HASH_ADAPT
35 PSI_memory_key	mem_key_ahi;
36 #endif /* BTR_CUR_HASH_ADAPT */
37 PSI_memory_key	mem_key_buf_buf_pool;
38 PSI_memory_key	mem_key_dict_stats_bg_recalc_pool_t;
39 PSI_memory_key	mem_key_dict_stats_index_map_t;
40 PSI_memory_key	mem_key_dict_stats_n_diff_on_level;
41 PSI_memory_key	mem_key_other;
42 PSI_memory_key	mem_key_row_log_buf;
43 PSI_memory_key	mem_key_row_merge_sort;
44 PSI_memory_key	mem_key_std;
45 
46 #ifdef UNIV_PFS_MEMORY
47 
48 /** Auxiliary array of performance schema 'PSI_memory_info'.
49 Each allocation appears in
50 performance_schema.memory_summary_global_by_event_name (and alike) in the form
51 of e.g. 'memory/innodb/NAME' where the last component NAME is picked from
52 the list below:
53 1. If key is specified, then the respective name is used
54 2. Without a specified key, allocations from inside std::* containers use
55    mem_key_std
56 3. Without a specified key, allocations from outside std::* pick up the key
57    based on the file name, and if file name is not found in the predefined list
58    (in ut_new_boot()) then mem_key_other is used.
59 Keep this list alphabetically sorted. */
60 static PSI_memory_info	pfs_info[] = {
61 #ifdef BTR_CUR_HASH_ADAPT
62 	{&mem_key_ahi, "adaptive hash index", 0},
63 #endif /* BTR_CUR_HASH_ADAPT */
64 	{&mem_key_buf_buf_pool, "buf_buf_pool", 0},
65 	{&mem_key_dict_stats_bg_recalc_pool_t, "dict_stats_bg_recalc_pool_t", 0},
66 	{&mem_key_dict_stats_index_map_t, "dict_stats_index_map_t", 0},
67 	{&mem_key_dict_stats_n_diff_on_level, "dict_stats_n_diff_on_level", 0},
68 	{&mem_key_other, "other", 0},
69 	{&mem_key_row_log_buf, "row_log_buf", 0},
70 	{&mem_key_row_merge_sort, "row_merge_sort", 0},
71 	{&mem_key_std, "std", 0},
72 };
73 
74 /** Map used for default performance schema keys, based on file name of the
75 caller. The key is the file name of the caller and the value is a pointer
76 to a PSI_memory_key variable to be passed to performance schema methods.
77 We use ut_strcmp_functor because by default std::map will compare the pointers
78 themselves (cont char*) and not do strcmp(). */
79 typedef std::map<const char*, PSI_memory_key*, ut_strcmp_functor>
80 	mem_keys_auto_t;
81 
82 /** Map of filename/pfskey, used for tracing allocations that have not
83 provided a manually created pfs key. This map is only ever modified (bulk
84 insert) at startup in a single-threaded environment by ut_new_boot().
85 Later it is only read (only std::map::find() is called) from multithreaded
86 environment, thus it is not protected by any latch. */
87 static mem_keys_auto_t	mem_keys_auto;
88 
89 #endif /* UNIV_PFS_MEMORY */
90 
91 /** Setup the internal objects needed for UT_NEW() to operate.
92 This must be called before the first call to UT_NEW(). */
93 void
94 ut_new_boot()
95 {
96 #ifdef UNIV_PFS_MEMORY
97 	static const char*	auto_event_names[] = {
98 		/* Keep this list alphabetically sorted. */
99 		"btr0btr",
100 		"btr0bulk",
101 		"btr0cur",
102 		"btr0pcur",
103 		"btr0sea",
104 		"buf0buf",
105 		"buf0dblwr",
106 		"buf0dump",
107 		"buf0flu",
108 		"buf0lru",
109 		"dict0dict",
110 		"dict0mem",
111 		"dict0stats",
112 		"dict0stats_bg",
113 		"eval0eval",
114 		"fil0fil",
115 		"fsp0file",
116 		"fsp0space",
117 		"fsp0sysspace",
118 		"fts0ast",
119 		"fts0config",
120 		"fts0fts",
121 		"fts0opt",
122 		"fts0pars",
123 		"fts0que",
124 		"fts0sql",
125 		"gis0sea",
126 		"ha0ha",
127 		"ha_innodb",
128 		"handler0alter",
129 		"hash0hash",
130 		"i_s",
131 		"ibuf0ibuf",
132 		"lexyy",
133 		"lock0lock",
134 		"log0log",
135 		"log0recv",
136 		"mem0mem",
137 		"os0event",
138 		"os0file",
139 		"page0cur",
140 		"page0zip",
141 		"pars0lex",
142 		"read0read",
143 		"rem0rec",
144 		"row0ftsort",
145 		"row0import",
146 		"row0log",
147 		"row0merge",
148 		"row0mysql",
149 		"row0sel",
150 		"row0trunc",
151 		"srv0conc",
152 		"srv0srv",
153 		"srv0start",
154 		"sync0arr",
155 		"sync0debug",
156 		"sync0rw",
157 		"sync0types",
158 		"trx0i_s",
159 		"trx0purge",
160 		"trx0roll",
161 		"trx0rseg",
162 		"trx0sys",
163 		"trx0trx",
164 		"trx0undo",
165 		"ut0list",
166 		"ut0mem",
167 		"ut0mutex",
168 		"ut0pool",
169 		"ut0rbt",
170 		"ut0wqueue",
171 	};
172 	static const size_t	n_auto = UT_ARR_SIZE(auto_event_names);
173 	static PSI_memory_key	auto_event_keys[n_auto];
174 	static PSI_memory_info	pfs_info_auto[n_auto];
175 
176 	for (size_t i = 0; i < n_auto; i++) {
177 
178 		const std::pair<mem_keys_auto_t::iterator, bool>	ret
179 			MY_ATTRIBUTE((unused))
180 			= mem_keys_auto.insert(
181 			mem_keys_auto_t::value_type(auto_event_names[i],
182 						    &auto_event_keys[i]));
183 
184 		/* ret.second is true if new element has been inserted */
185 		ut_a(ret.second);
186 
187 		/* e.g. "btr0btr" */
188 		pfs_info_auto[i].m_name = auto_event_names[i];
189 
190 		/* a pointer to the pfs key */
191 		pfs_info_auto[i].m_key = &auto_event_keys[i];
192 
193 		pfs_info_auto[i].m_flags = 0;
194 	}
195 
196 	PSI_MEMORY_CALL(register_memory)("innodb",
197 					 pfs_info,
198 					 UT_ARR_SIZE(pfs_info));
199 	PSI_MEMORY_CALL(register_memory)("innodb",
200 					 pfs_info_auto,
201 					 n_auto);
202 #endif /* UNIV_PFS_MEMORY */
203 }
204 
205 #ifdef UNIV_PFS_MEMORY
206 
207 /** Retrieve a memory key (registered with PFS), given a portion of the file
208 name of the caller.
209 @param[in]	file	portion of the filename - basename without an extension
210 @return registered memory key or PSI_NOT_INSTRUMENTED if not found */
211 PSI_memory_key
212 ut_new_get_key_by_file(
213 	const char*	file)
214 {
215 	mem_keys_auto_t::const_iterator	el = mem_keys_auto.find(file);
216 
217 	if (el != mem_keys_auto.end()) {
218 		return(*(el->second));
219 	}
220 
221 	return(PSI_NOT_INSTRUMENTED);
222 }
223 
224 #endif /* UNIV_PFS_MEMORY */
225