34
34
#include < string.h>
35
35
#include < sys/types.h>
36
36
37
+ #ifdef _WIN32
38
+ #include " jemalloc_win.h"
39
+ #endif // _WIN32
40
+
37
41
#include " memory_debugging.h"
38
42
#include " my_compiler.h"
39
43
#include " my_dbug.h"
@@ -56,6 +60,192 @@ static void *my_raw_malloc(size_t size, myf my_flags);
56
60
static void my_raw_free (void *ptr);
57
61
extern void my_free (void *ptr);
58
62
63
+ #ifdef _WIN32
64
+ #include < memory>
65
+ #include < mutex>
66
+ #include < sstream>
67
+ #include < string>
68
+
69
+ extern " C" bool use_jemalloc_allocations ();
70
+ namespace mysys {
71
+ namespace detail {
72
+ void *(*pfn_malloc)(size_t size);
73
+ void *(*pfn_calloc)(size_t number, size_t size);
74
+ void *(*pfn_realloc)(void *ptr, size_t size);
75
+ void (*pfn_free)(void *ptr);
76
+ } // namespace detail
77
+ } // namespace mysys
78
+
79
+ static std::string win_error_to_string (DWORD err) {
80
+ struct local_freer {
81
+ void operator ()(LPTSTR ptr) { LocalFree (ptr); }
82
+ };
83
+ LPTSTR pmsg = nullptr ;
84
+ if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
85
+ FORMAT_MESSAGE_IGNORE_INSERTS |
86
+ FORMAT_MESSAGE_ALLOCATE_BUFFER,
87
+ nullptr , err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
88
+ (LPTSTR)&pmsg, 0 , nullptr )) {
89
+ return " " ;
90
+ }
91
+ std::unique_ptr<TCHAR, local_freer> scope_guard{pmsg};
92
+ std::string message (pmsg);
93
+ // Remove any trailing CRLF
94
+ if (message.rfind (" \r\n " ) == (message.length () - 2 )) {
95
+ message.resize (message.length () - 2 );
96
+ }
97
+ return message;
98
+ }
99
+
100
+ namespace mysys {
101
+ static bool using_jemalloc = false ;
102
+ bool is_my_malloc_using_jemalloc () { return using_jemalloc; }
103
+ // This unwieldy add_or_return_messages function is a workaround for the fact
104
+ // that we can't control the order of global initializers, and the server
105
+ // logger is not available at the time the jemalloc allocation
106
+ // functions are loaded. We can't split the add_or_return_messages function
107
+ // into separate add_messages and return_messages functions with the
108
+ // messages stored in a file scoped static vector, as messages stored in
109
+ // that vector during the initialization of _another_ file scoped static
110
+ // object could be lost when a file scoped static vector is itself
111
+ // subsequently initialized.
112
+ // So message codes and strings are stored in a static function scoped vector
113
+ // for subsequent retrieval when the server logger IS available. This static
114
+ // function scoped vector is immune to the order of initialization of file
115
+ // scoped variables.
116
+ // Note also that we can't use error codes from mysqld_error.h
117
+ // here, as the mysys library is used in the generation of those
118
+ // error codes. To avoid a circular dependency, the codes stored
119
+ // in the LogMessageInfo::m_ecode are offsets to be applied to
120
+ // the base error code ER_MY_MALLOC_USING_JEMALLOC to find the
121
+ // "real" error code.
122
+ static void add_or_return_messages (
123
+ const LogMessageInfo &msg_info,
124
+ std::vector<LogMessageInfo> *pvec = nullptr ) {
125
+ static std::vector<LogMessageInfo> vec_messages;
126
+
127
+ if (pvec) {
128
+ *pvec = vec_messages;
129
+ return ;
130
+ }
131
+ vec_messages.emplace_back (msg_info);
132
+ }
133
+
134
+ static void log_err_deferred (loglevel severity, int64_t ecode,
135
+ const std::string &message) {
136
+ add_or_return_messages ({severity, ecode, message});
137
+ }
138
+
139
+ std::vector<LogMessageInfo> fetch_jemalloc_initialization_messages () {
140
+ std::vector<LogMessageInfo> jemalloc_init_messages;
141
+ LogMessageInfo dummy;
142
+ add_or_return_messages (dummy, &jemalloc_init_messages);
143
+ return jemalloc_init_messages;
144
+ }
145
+ template <class T >
146
+ static T get_proc_address (const char *function_name, HMODULE hlib,
147
+ const char *dll_path) {
148
+ auto tmp_pfn = reinterpret_cast <T>(GetProcAddress (hlib, function_name));
149
+ if (!tmp_pfn) {
150
+ DWORD err = GetLastError ();
151
+ std::ostringstream os;
152
+ os << " GetProcAddress failed for \" " << function_name << " \" from \" "
153
+ << dll_path << " \" with error code " << err << " : \" "
154
+ << win_error_to_string (err) << " \" Falling back to default malloc" ;
155
+
156
+ log_err_deferred (ERROR_LEVEL, MY_MALLOC_GETPROCADDRESS_FAILED_ER, os.str ());
157
+ }
158
+ return tmp_pfn;
159
+ }
160
+ namespace detail {
161
+ std::once_flag init_malloc_pointers_flag;
162
+ /* *
163
+ Initialize (Windows only) function pointers used for some memory allocations,
164
+ including the my_malloc family. Note that these memory allocations can occur
165
+ early in the process lifecycle (during construction of global objects) and
166
+ thus init_malloc_pointers is called using std::call_once from the functions
167
+ that wrap memory allocations rather than from my_win_init, where it would be
168
+ too late.
169
+ Note that as this function is invoked very early in the lifetime of the
170
+ process (typically before main), the logging system is not yet initialized
171
+ so calls to my_message_local send their output to stderr.
172
+ Also note that when used from the MySQL server on Windows, there are
173
+ typically two instances of the MySQL server process launched (see the
174
+ --no-monitor option for launching a single instance) and thus the information
175
+ message emitted on not finding the jemalloc dll will typically appear on
176
+ stderr twice.
177
+ */
178
+ void init_malloc_pointers () {
179
+ // Set up to use the MSVC library routines by default.
180
+ pfn_malloc = std::malloc;
181
+ pfn_calloc = std::calloc;
182
+ pfn_realloc = std::realloc;
183
+ pfn_free = std::free;
184
+ if (!use_jemalloc_allocations ()) {
185
+ return ;
186
+ }
187
+ // If we can find the jemalloc library in the same directory as this
188
+ // executable, and load all the required functions successfully, use the
189
+ // jemalloc routines instead.
190
+ // Note that the handle returned by LoadLibraryEx is deliberately NOT
191
+ // released with a FreeLibrary call, as the functions used from this
192
+ // library can be called from global object destructors late in the
193
+ // process lifecycle. Not calling FreeLibrary is benign is this case.
194
+ HMODULE hlib = LoadLibraryEx (jemalloc_dll_name, NULL ,
195
+ LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
196
+ if (NULL == hlib) {
197
+ DWORD err = GetLastError ();
198
+ if (ERROR_MOD_NOT_FOUND == err) {
199
+ // Normal behaviour: not finding the jemalloc dll means
200
+ // we don't want to use it.
201
+ std::ostringstream os;
202
+ os << jemalloc_dll_name << " not found. Falling back to default malloc" ;
203
+ log_err_deferred (INFORMATION_LEVEL, MY_MALLOC_USING_STD_MALLOC_ER,
204
+ os.str ());
205
+ return ;
206
+ }
207
+
208
+ std::ostringstream os;
209
+ os << " LoadLibraryEx(\" " << jemalloc_dll_name
210
+ << " \" , NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR) failed with error "
211
+ " code "
212
+ << err << " : \" " << win_error_to_string (err)
213
+ << " \" , using default malloc" ;
214
+
215
+ log_err_deferred (ERROR_LEVEL, MY_MALLOC_LOADLIBRARY_FAILED_ER, os.str ());
216
+ return ;
217
+ }
218
+
219
+ auto tmp_je_malloc = get_proc_address<decltype (pfn_malloc)>(
220
+ jemalloc_malloc_function_name, hlib, jemalloc_dll_name);
221
+ auto tmp_je_calloc = get_proc_address<decltype (pfn_calloc)>(
222
+ jemalloc_calloc_function_name, hlib, jemalloc_dll_name);
223
+ auto tmp_je_realloc = get_proc_address<decltype (pfn_realloc)>(
224
+ jemalloc_realloc_function_name, hlib, jemalloc_dll_name);
225
+ auto tmp_je_free = get_proc_address<decltype (pfn_free)>(
226
+ jemalloc_free_function_name, hlib, jemalloc_dll_name);
227
+
228
+ if (!tmp_je_malloc || !tmp_je_calloc || !tmp_je_realloc || !tmp_je_free) {
229
+ return ;
230
+ }
231
+
232
+ pfn_malloc = tmp_je_malloc;
233
+ pfn_calloc = tmp_je_calloc;
234
+ pfn_realloc = tmp_je_realloc;
235
+ pfn_free = tmp_je_free;
236
+ using_jemalloc = true ;
237
+ return ;
238
+ }
239
+ } // namespace detail
240
+ } // namespace mysys
241
+
242
+ #define malloc (size ) mysys::detail::pfn_malloc((size))
243
+ #define calloc (count, size ) mysys::detail::pfn_calloc((count), (size))
244
+ #define realloc (p, size ) mysys::detail::pfn_realloc((p), (size))
245
+ #define free (p ) mysys::detail::pfn_free((p))
246
+
247
+ #endif // _WIN32
248
+
59
249
#ifdef USE_MALLOC_WRAPPER
60
250
61
251
void *my_malloc (PSI_memory_key key, size_t size, myf flags) {
@@ -182,11 +372,15 @@ static void *my_raw_malloc(size_t size, myf my_flags) {
182
372
else
183
373
point = _malloc_dbg (size, _CLIENT_BLOCK, __FILE__, __LINE__);
184
374
#else
375
+ #ifdef _WIN32
376
+ std::call_once (mysys::detail::init_malloc_pointers_flag,
377
+ mysys::detail::init_malloc_pointers);
378
+ #endif // _WIN32
185
379
if (my_flags & MY_ZEROFILL)
186
380
point = calloc (size, 1 );
187
381
else
188
382
point = malloc (size);
189
- #endif
383
+ #endif // MY_MSCRT_DEBUG
190
384
191
385
DBUG_EXECUTE_IF (" simulate_out_of_memory" , {
192
386
free (point);
@@ -236,8 +430,12 @@ static void *my_raw_realloc(void *oldpoint, size_t size, myf my_flags) {
236
430
#if defined(MY_MSCRT_DEBUG)
237
431
point = _realloc_dbg (oldpoint, size, _CLIENT_BLOCK, __FILE__, __LINE__);
238
432
#else
433
+ #ifdef _WIN32
434
+ std::call_once (mysys::detail::init_malloc_pointers_flag,
435
+ mysys::detail::init_malloc_pointers);
436
+ #endif // _WIN32
239
437
point = realloc (oldpoint, size);
240
- #endif
438
+ #endif // MY_MSCRT_DEBUG
241
439
#ifndef NDEBUG
242
440
end:
243
441
#endif
@@ -253,7 +451,7 @@ static void *my_raw_realloc(void *oldpoint, size_t size, myf my_flags) {
253
451
DBUG_PRINT (" exit" , (" ptr: %p" , point));
254
452
return point;
255
453
}
256
- #endif
454
+ #endif // !USE_MALLOC_WRAPPER
257
455
258
456
/* *
259
457
Free memory allocated with my_raw_malloc.
@@ -266,8 +464,12 @@ static void my_raw_free(void *ptr) {
266
464
#if defined(MY_MSCRT_DEBUG)
267
465
_free_dbg (ptr, _CLIENT_BLOCK);
268
466
#else
467
+ #ifdef _WIN32
468
+ std::call_once (mysys::detail::init_malloc_pointers_flag,
469
+ mysys::detail::init_malloc_pointers);
470
+ #endif // _WIN32
269
471
free (ptr);
270
- #endif
472
+ #endif // MY_MSCRT_DEBUG
271
473
}
272
474
273
475
void *my_memdup (PSI_memory_key key, const void *from, size_t length,
0 commit comments