-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathlibmesh.cc
More file actions
502 lines (442 loc) · 16.9 KB
/
Copy pathlibmesh.cc
File metadata and controls
502 lines (442 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright 2019 The Mesh Authors. All rights reserved.
// Use of this source code is governed by the Apache License,
// Version 2.0, that can be found in the LICENSE file.
#include <stdlib.h>
#ifdef __linux__
#include <sys/auxv.h>
#include <unistd.h>
#endif
#include "runtime.h"
#include "thread_local_heap.h"
#include "runtime_impl.h"
#include "dispatch_utils.h"
#include "ifunc_resolver.h"
using namespace mesh;
static __attribute__((constructor)) void libmesh_init() {
mesh::real::init();
const size_t pageSize = getPageSize();
// We ONLY support 4KB and 16KB page sizes. Fail immediately if unsupported.
if (pageSize != kPageSize4K && pageSize != kPageSize16K) {
mesh::debug("FATAL: Unsupported page size %zu bytes. Mesh only supports 4KB and 16KB pages.\n", pageSize);
abort();
}
dispatchByPageSize([](auto &rt) {
rt.createSignalFd();
rt.installSegfaultHandler();
rt.initMaxMapCount();
});
if (pageSize == kPageSize4K) {
ThreadLocalHeap<kPageSize4K>::InitTLH();
} else {
ThreadLocalHeap<kPageSize16K>::InitTLH();
}
char *meshPeriodStr = getenv("MESH_PERIOD_MS");
if (meshPeriodStr) {
long period = strtol(meshPeriodStr, nullptr, 10);
if (period < 0) {
period = 0;
}
dispatchByPageSize([period](auto &rt) { rt.setMeshPeriodMs(std::chrono::milliseconds{period}); });
}
char *bgThread = getenv("MESH_BACKGROUND_THREAD");
if (!bgThread)
return;
int shouldThread = atoi(bgThread);
if (shouldThread) {
dispatchByPageSize([](auto &rt) { rt.startBgThread(); });
}
}
static __attribute__((destructor)) void libmesh_fini() {
char *mstats = getenv("MALLOCSTATS");
if (!mstats)
return;
int mlevel = atoi(mstats);
if (mlevel < 0)
mlevel = 0;
else if (mlevel > 2)
mlevel = 2;
dispatchByPageSize([mlevel](auto &rt) { rt.heap().dumpStats(mlevel, false); });
}
namespace mesh {
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE void *allocSlowpath(size_t sz) {
ThreadLocalHeap<PageSize> *localHeap = ThreadLocalHeap<PageSize>::GetHeap();
return localHeap->malloc(sz);
}
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE __attribute__((unused)) void *cxxNewSlowpath(size_t sz) {
ThreadLocalHeap<PageSize> *localHeap = ThreadLocalHeap<PageSize>::GetHeap();
return localHeap->cxxNew(sz);
}
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE void freeSlowpath(void *ptr) {
// instead of instantiating a thread-local heap on free, just free
// to the global heap directly
runtime<PageSize>().heap().free(ptr);
}
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE void *reallocSlowpath(void *oldPtr, size_t newSize) {
ThreadLocalHeap<PageSize> *localHeap = ThreadLocalHeap<PageSize>::GetHeap();
return localHeap->realloc(oldPtr, newSize);
}
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE void *callocSlowpath(size_t count, size_t size) {
ThreadLocalHeap<PageSize> *localHeap = ThreadLocalHeap<PageSize>::GetHeap();
return localHeap->calloc(count, size);
}
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE size_t usableSizeSlowpath(void *ptr) {
ThreadLocalHeap<PageSize> *localHeap = ThreadLocalHeap<PageSize>::GetHeap();
return localHeap->getSize(ptr);
}
template <size_t PageSize>
ATTRIBUTE_NEVER_INLINE void *memalignSlowpath(size_t alignment, size_t size) {
ThreadLocalHeap<PageSize> *localHeap = ThreadLocalHeap<PageSize>::GetHeap();
return localHeap->memalign(alignment, size);
}
} // namespace mesh
// Implementation templates for IFUNC or dispatch
template <size_t PageSize>
static void *mesh_malloc_impl(size_t sz) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
return mesh::allocSlowpath<PageSize>(sz);
}
return localHeap->malloc(sz);
}
template <size_t PageSize>
static void mesh_free_impl(void *ptr) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
mesh::freeSlowpath<PageSize>(ptr);
return;
}
localHeap->free(ptr);
}
template <size_t PageSize>
static void mesh_sized_free_impl(void *ptr, size_t sz) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
mesh::freeSlowpath<PageSize>(ptr);
return;
}
localHeap->sizedFree(ptr, sz);
}
template <size_t PageSize>
static void *mesh_realloc_impl(void *oldPtr, size_t newSize) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
return mesh::reallocSlowpath<PageSize>(oldPtr, newSize);
}
return localHeap->realloc(oldPtr, newSize);
}
template <size_t PageSize>
static size_t mesh_malloc_usable_size_impl(void *ptr) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
return mesh::usableSizeSlowpath<PageSize>(ptr);
}
return localHeap->getSize(ptr);
}
template <size_t PageSize>
static void *mesh_memalign_impl(size_t alignment, size_t size) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
return mesh::memalignSlowpath<PageSize>(alignment, size);
}
return localHeap->memalign(alignment, size);
}
template <size_t PageSize>
static void *mesh_calloc_impl(size_t count, size_t size) {
auto *localHeap = ThreadLocalHeap<PageSize>::GetHeapIfPresent();
if (unlikely(localHeap == nullptr)) {
return mesh::callocSlowpath<PageSize>(count, size);
}
return localHeap->calloc(count, size);
}
#if defined(__linux__) && defined(__aarch64__)
// ===================================================================
// IFUNC Resolver Functions (ARM64 Linux only)
// ===================================================================
// These resolver functions are called by the dynamic linker to determine
// which implementation to use for each memory allocation function.
// They run ONCE per function at program startup, before main().
//
// The resolver selects between 4KB and 16KB page implementations based
// on the actual system page size detected from the auxiliary vector.
//
// CRITICAL: These functions run in the restricted IFUNC environment:
// - No access to global variables (not initialized yet)
// - No library functions available
// - Must be completely self-contained
// - Must use no_stack_protector attribute (stack guard not set up)
//
// The dynamic linker replaces calls to mesh_malloc, mesh_free, etc.
// with direct calls to the selected implementation (mesh_malloc_impl<4096>
// or mesh_malloc_impl<16384>), eliminating runtime overhead.
//
// Note: x86_64 Linux always uses 4KB pages, so we use compile-time
// dispatch instead of IFUNC there (the branch is optimized away).
// ===================================================================
extern "C" {
typedef void *(*malloc_func)(size_t);
typedef void (*free_func)(void *);
typedef void (*sized_free_func)(void *, size_t);
typedef void *(*realloc_func)(void *, size_t);
typedef size_t (*usable_size_func)(void *);
typedef void *(*memalign_func)(size_t, size_t);
typedef void *(*calloc_func)(size_t, size_t);
__attribute__((no_stack_protector)) static malloc_func resolve_mesh_malloc() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_malloc_impl<kPageSize4K> : mesh_malloc_impl<kPageSize16K>;
}
__attribute__((no_stack_protector)) static free_func resolve_mesh_free() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_free_impl<kPageSize4K> : mesh_free_impl<kPageSize16K>;
}
__attribute__((no_stack_protector)) static sized_free_func resolve_mesh_sized_free() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_sized_free_impl<kPageSize4K> : mesh_sized_free_impl<kPageSize16K>;
}
__attribute__((no_stack_protector)) static realloc_func resolve_mesh_realloc() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_realloc_impl<kPageSize4K> : mesh_realloc_impl<kPageSize16K>;
}
__attribute__((no_stack_protector)) static usable_size_func resolve_mesh_malloc_usable_size() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_malloc_usable_size_impl<kPageSize4K>
: mesh_malloc_usable_size_impl<kPageSize16K>;
}
__attribute__((no_stack_protector)) static memalign_func resolve_mesh_memalign() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_memalign_impl<kPageSize4K> : mesh_memalign_impl<kPageSize16K>;
}
__attribute__((no_stack_protector)) static calloc_func resolve_mesh_calloc() {
size_t pageSize = mesh::ifunc::getPageSizeFromAuxv();
return (pageSize == kPageSize4K) ? mesh_calloc_impl<kPageSize4K> : mesh_calloc_impl<kPageSize16K>;
}
}
#endif
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void *mesh_malloc(size_t sz)
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_malloc")));
#else
{
if (likely(getPageSize() == kPageSize4K)) {
return mesh_malloc_impl<kPageSize4K>(sz);
} else {
return mesh_malloc_impl<kPageSize16K>(sz);
}
}
#endif
#define xxmalloc mesh_malloc
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void mesh_free(void *ptr)
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_free")));
#else
{
if (likely(getPageSize() == kPageSize4K)) {
mesh_free_impl<kPageSize4K>(ptr);
} else {
mesh_free_impl<kPageSize16K>(ptr);
}
}
#endif
#define xxfree mesh_free
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void mesh_sized_free(void *ptr, size_t sz)
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_sized_free")));
#else
{
if (likely(getPageSize() == kPageSize4K)) {
mesh_sized_free_impl<kPageSize4K>(ptr, sz);
} else {
mesh_sized_free_impl<kPageSize16K>(ptr, sz);
}
}
#endif
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void *mesh_realloc(void *oldPtr, size_t newSize)
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_realloc")));
#else
{
if (likely(getPageSize() == kPageSize4K)) {
return mesh_realloc_impl<kPageSize4K>(oldPtr, newSize);
} else {
return mesh_realloc_impl<kPageSize16K>(oldPtr, newSize);
}
}
#endif
#if defined(__FreeBSD__)
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void *mesh_reallocarray(void *oldPtr, size_t count, size_t size) {
size_t total;
if (unlikely(__builtin_umull_overflow(count, size, &total))) {
return NULL;
} else {
return mesh_realloc(oldPtr, total);
}
}
#endif
#ifndef __FreeBSD__
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN size_t mesh_malloc_usable_size(void *ptr)
#else
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN size_t mesh_malloc_usable_size(const void *cptr)
#endif
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_malloc_usable_size")));
#else
{
#ifdef __FreeBSD__
void *ptr = const_cast<void *>(cptr);
#endif
if (likely(getPageSize() == kPageSize4K)) {
return mesh_malloc_usable_size_impl<kPageSize4K>(ptr);
} else {
return mesh_malloc_usable_size_impl<kPageSize16K>(ptr);
}
}
#endif
#define xxmalloc_usable_size mesh_malloc_usable_size
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void *mesh_memalign(size_t alignment, size_t size)
#if !defined(__FreeBSD__) && !defined(__SVR4)
throw()
#endif
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_memalign")));
#else
{
if (likely(getPageSize() == kPageSize4K)) {
return mesh_memalign_impl<kPageSize4K>(alignment, size);
} else {
return mesh_memalign_impl<kPageSize16K>(alignment, size);
}
}
#endif
extern "C" MESH_EXPORT CACHELINE_ALIGNED_FN void *mesh_calloc(size_t count, size_t size)
#if defined(__linux__) && defined(__aarch64__)
__attribute__((ifunc("resolve_mesh_calloc")));
#else
{
if (likely(getPageSize() == kPageSize4K)) {
return mesh_calloc_impl<kPageSize4K>(count, size);
} else {
return mesh_calloc_impl<kPageSize16K>(count, size);
}
}
#endif
extern "C" {
#ifdef __linux__
size_t MESH_EXPORT mesh_usable_size(void *ptr) __attribute__((weak, alias("mesh_malloc_usable_size")));
#else
// aliases are not supported on darwin
size_t MESH_EXPORT mesh_usable_size(void *ptr) {
return mesh_malloc_usable_size(ptr);
}
#endif // __linux__
// ensure we don't concurrently allocate/mess with internal heap data
// structures while forking. This is not normally invoked when
// libmesh is dynamically linked or LD_PRELOADed into a binary.
void MESH_EXPORT xxmalloc_lock(void) {
mesh::dispatchByPageSize([](auto &rt) { rt.lock(); });
}
// ensure we don't concurrently allocate/mess with internal heap data
// structures while forking. This is not normally invoked when
// libmesh is dynamically linked or LD_PRELOADed into a binary.
void MESH_EXPORT xxmalloc_unlock(void) {
mesh::dispatchByPageSize([](auto &rt) { rt.unlock(); });
}
int MESH_EXPORT sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) MESH_THROW {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.sigaction(signum, act, oldact); });
}
int MESH_EXPORT sigprocmask(int how, const sigset_t *set, sigset_t *oldset) MESH_THROW {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.sigprocmask(how, set, oldset); });
}
// we need to wrap pthread_create and pthread_exit so that we can
// install our segfault handler and cleanup thread-local heaps.
int MESH_EXPORT pthread_create(pthread_t *thread, const pthread_attr_t *attr, mesh::PthreadFn startRoutine,
void *arg) MESH_THROW {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.createThread(thread, attr, startRoutine, arg); });
}
void MESH_EXPORT ATTRIBUTE_NORETURN pthread_exit(void *retval) {
if (likely(getPageSize() == kPageSize4K)) {
mesh::runtime<kPageSize4K>().exitThread(retval);
} else {
mesh::runtime<kPageSize16K>().exitThread(retval);
}
}
// Same API as je_mallctl, allows a program to query stats and set
// allocator-related options.
int MESH_EXPORT mesh_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.heap().mallctl(name, oldp, oldlenp, newp, newlen); });
}
#ifdef __linux__
int MESH_EXPORT epoll_wait(int __epfd, struct epoll_event *__events, int __maxevents, int __timeout) {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.epollWait(__epfd, __events, __maxevents, __timeout); });
}
int MESH_EXPORT epoll_pwait(int __epfd, struct epoll_event *__events, int __maxevents, int __timeout,
const __sigset_t *__ss) {
return mesh::dispatchByPageSize(
[=](auto &rt) { return rt.epollPwait(__epfd, __events, __maxevents, __timeout, __ss); });
}
#endif
#if __linux__
ssize_t MESH_EXPORT recv(int sockfd, void *buf, size_t len, int flags) {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.recv(sockfd, buf, len, flags); });
}
ssize_t MESH_EXPORT recvmsg(int sockfd, struct msghdr *msg, int flags) {
return mesh::dispatchByPageSize([=](auto &rt) { return rt.recvmsg(sockfd, msg, flags); });
}
#endif
} // extern "C"
namespace {
// RAII guard for the three locks needed during spawn operations.
// Acquires locks in the same order as prepareForFork to prevent races.
template <size_t PageSize>
class SpawnLockGuard {
public:
SpawnLockGuard() {
mesh::runtime<PageSize>().heap().lock();
mesh::runtime<PageSize>().lock();
mesh::internal::Heap().lock();
}
~SpawnLockGuard() {
mesh::internal::Heap().unlock();
mesh::runtime<PageSize>().unlock();
mesh::runtime<PageSize>().heap().unlock();
}
SpawnLockGuard(const SpawnLockGuard &) = delete;
SpawnLockGuard &operator=(const SpawnLockGuard &) = delete;
};
} // namespace
extern "C" {
int MESH_EXPORT mesh_posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) {
if (likely(getPageSize() == kPageSize4K)) {
SpawnLockGuard<kPageSize4K> guard;
return mesh::real::posix_spawn(pid, path, file_actions, attrp, argv, envp);
} else {
SpawnLockGuard<kPageSize16K> guard;
return mesh::real::posix_spawn(pid, path, file_actions, attrp, argv, envp);
}
}
int MESH_EXPORT mesh_posix_spawnp(pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) {
if (likely(getPageSize() == kPageSize4K)) {
SpawnLockGuard<kPageSize4K> guard;
return mesh::real::posix_spawnp(pid, file, file_actions, attrp, argv, envp);
} else {
SpawnLockGuard<kPageSize16K> guard;
return mesh::real::posix_spawnp(pid, file, file_actions, attrp, argv, envp);
}
}
} // extern "C"
#if defined(__linux__)
#include "gnu_wrapper.cc"
#elif defined(__APPLE__)
#include "mac_wrapper.cc"
#elif defined(__FreeBSD__)
#include "fbsd_wrapper.cc"
#else
#error "only linux, macOS and FreeBSD support for now"
#endif