[utils] Fix mono_valloc and mono_valloc_aligned malloc-based implementation.
[mono.git] / mono / utils / mono-mmap.c
1 /**
2  * \file
3  * Support for mapping code into the process address space
4  *
5  * Author:
6  *   Mono Team (mono-list@lists.ximian.com)
7  *
8  * Copyright 2001-2008 Novell, Inc.
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #include <config.h>
13
14 #ifndef HOST_WIN32
15 #include <sys/types.h>
16 #if HAVE_SYS_STAT_H
17 #include <sys/stat.h>
18 #endif
19 #if HAVE_SYS_MMAN_H
20 #include <sys/mman.h>
21 #endif
22 #ifdef HAVE_SIGNAL_H
23 #include <signal.h>
24 #endif
25 #include <fcntl.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #endif /* !HOST_WIN32 */
31
32 #include "mono-mmap.h"
33 #include "mono-mmap-internals.h"
34 #include "mono-proclib.h"
35 #include <mono/utils/mono-threads.h>
36 #include <mono/utils/atomic.h>
37 #include <mono/utils/mono-counters.h>
38
39 #define BEGIN_CRITICAL_SECTION do { \
40         MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
41         if (__info) __info->inside_critical_region = TRUE;      \
42
43 #define END_CRITICAL_SECTION \
44         if (__info) __info->inside_critical_region = FALSE;     \
45 } while (0)     \
46
47 #ifndef MAP_ANONYMOUS
48 #define MAP_ANONYMOUS MAP_ANON
49 #endif
50
51 #ifndef MAP_32BIT
52 #define MAP_32BIT 0
53 #endif
54
55 typedef struct {
56         int size;
57         int pid;
58         int reserved;
59         short stats_start;
60         short stats_end;
61 } SAreaHeader;
62
63 void*
64 malloc_shared_area (int pid)
65 {
66         int size = mono_pagesize ();
67         SAreaHeader *sarea = (SAreaHeader *) g_malloc0 (size);
68         sarea->size = size;
69         sarea->pid = pid;
70         sarea->stats_start = sizeof (SAreaHeader);
71         sarea->stats_end = sizeof (SAreaHeader);
72
73         return sarea;
74 }
75
76 char*
77 aligned_address (char *mem, size_t size, size_t alignment)
78 {
79         char *aligned = (char*)((size_t)(mem + (alignment - 1)) & ~(alignment - 1));
80         g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((size_t)aligned & (alignment - 1)));
81         return aligned;
82 }
83
84 static size_t allocation_count [MONO_MEM_ACCOUNT_MAX];
85 static size_t total_allocation_count;
86 static size_t alloc_limit;
87
88 void
89 account_mem (MonoMemAccountType type, ssize_t size)
90 {
91         InterlockedAddP (&allocation_count [type], size);
92         InterlockedAddP (&total_allocation_count, size);
93 }
94
95 void
96 mono_valloc_set_limit (size_t size)
97 {
98         alloc_limit = size;
99 }
100
101 gboolean
102 mono_valloc_can_alloc (size_t size)
103 {
104         if (alloc_limit)
105                 return (total_allocation_count + size) < alloc_limit;
106         return TRUE;
107 }
108
109 const char*
110 mono_mem_account_type_name (MonoMemAccountType type)
111 {
112         static const char *names[] = {
113                 "code",
114                 "hazard pointers",
115                 "domain",
116                 "SGen internal",
117                 "SGen nursery",
118                 "SGen LOS",
119                 "SGen mark&sweep",
120                 "SGen card table",
121                 "SGen shadow card table",
122                 "SGen debugging",
123                 "SGen binary protocol",
124                 "exceptions",
125                 "profiler",
126                 "other"
127         };
128
129         return names [type];
130 }
131
132 void
133 mono_mem_account_register_counters (void)
134 {
135         for (int i = 0; i < MONO_MEM_ACCOUNT_MAX; ++i) {
136                 const char *prefix = "Valloc ";
137                 const char *name = mono_mem_account_type_name (i);
138                 char descr [128];
139                 g_assert (strlen (prefix) + strlen (name) < sizeof (descr));
140                 sprintf (descr, "%s%s", prefix, name);
141                 mono_counters_register (descr, MONO_COUNTER_WORD | MONO_COUNTER_RUNTIME | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&allocation_count [i]);
142         }
143 }
144
145 #ifdef HOST_WIN32
146 // Windows specific implementation in mono-mmap-windows.c
147 #define HAVE_VALLOC_ALIGNED
148
149 #else
150
151 static void* malloced_shared_area = NULL;
152 #if defined(HAVE_MMAP)
153
154 /**
155  * mono_pagesize:
156  * Get the page size in use on the system. Addresses and sizes in the
157  * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
158  * aligned.
159  *
160  * Returns: the page size in bytes.
161  */
162 int
163 mono_pagesize (void)
164 {
165         static int saved_pagesize = 0;
166
167         if (saved_pagesize)
168                 return saved_pagesize;
169
170         // Prefer sysconf () as it's signal safe.
171 #if defined (HAVE_SYSCONF) && defined (_SC_PAGESIZE)
172         saved_pagesize = sysconf (_SC_PAGESIZE);
173 #else
174         saved_pagesize = getpagesize ();
175 #endif
176
177         return saved_pagesize;
178 }
179
180 int
181 mono_valloc_granule (void)
182 {
183         return mono_pagesize ();
184 }
185
186 static int
187 prot_from_flags (int flags)
188 {
189         int prot = PROT_NONE;
190         /* translate the protection bits */
191         if (flags & MONO_MMAP_READ)
192                 prot |= PROT_READ;
193         if (flags & MONO_MMAP_WRITE)
194                 prot |= PROT_WRITE;
195         if (flags & MONO_MMAP_EXEC)
196                 prot |= PROT_EXEC;
197         return prot;
198 }
199
200 /**
201  * mono_valloc:
202  * \param addr memory address
203  * \param length memory area size
204  * \param flags protection flags
205  * Allocates \p length bytes of virtual memory with the \p flags
206  * protection. \p addr can be a preferred memory address or a
207  * mandatory one if MONO_MMAP_FIXED is set in \p flags.
208  * \p addr must be pagesize aligned and can be NULL.
209  * \p length must be a multiple of pagesize.
210  * \returns NULL on failure, the address of the memory area otherwise
211  */
212 void*
213 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
214 {
215         void *ptr;
216         int mflags = 0;
217         int prot = prot_from_flags (flags);
218
219         if (!mono_valloc_can_alloc (length))
220                 return NULL;
221
222         /* translate the flags */
223         if (flags & MONO_MMAP_FIXED)
224                 mflags |= MAP_FIXED;
225         if (flags & MONO_MMAP_32BIT)
226                 mflags |= MAP_32BIT;
227
228         mflags |= MAP_ANONYMOUS;
229         mflags |= MAP_PRIVATE;
230
231         BEGIN_CRITICAL_SECTION;
232         ptr = mmap (addr, length, prot, mflags, -1, 0);
233         if (ptr == MAP_FAILED) {
234                 int fd = open ("/dev/zero", O_RDONLY);
235                 if (fd != -1) {
236                         ptr = mmap (addr, length, prot, mflags, fd, 0);
237                         close (fd);
238                 }
239         }
240         END_CRITICAL_SECTION;
241
242         if (ptr == MAP_FAILED)
243                 return NULL;
244
245         account_mem (type, (ssize_t)length);
246
247         return ptr;
248 }
249
250 /**
251  * mono_vfree:
252  * \param addr memory address returned by mono_valloc ()
253  * \param length size of memory area
254  * Remove the memory mapping at the address \p addr.
255  * \returns \c 0 on success.
256  */
257 int
258 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
259 {
260         int res;
261         BEGIN_CRITICAL_SECTION;
262         res = munmap (addr, length);
263         END_CRITICAL_SECTION;
264
265         account_mem (type, -(ssize_t)length);
266
267         return res;
268 }
269
270 /**
271  * mono_file_map:
272  * \param length size of data to map
273  * \param flags protection flags
274  * \param fd file descriptor
275  * \param offset offset in the file
276  * \param ret_handle pointer to storage for returning a handle for the map
277  * Map the area of the file pointed to by the file descriptor \p fd, at offset
278  * \p offset and of size \p length in memory according to the protection flags
279  * \p flags.
280  * \p offset and \p length must be multiples of the page size.
281  * \p ret_handle must point to a void*: this value must be used when unmapping
282  * the memory area using \c mono_file_unmap().
283  */
284 void*
285 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
286 {
287         void *ptr;
288         int mflags = 0;
289         int prot = prot_from_flags (flags);
290         /* translate the flags */
291         if (flags & MONO_MMAP_PRIVATE)
292                 mflags |= MAP_PRIVATE;
293         if (flags & MONO_MMAP_SHARED)
294                 mflags |= MAP_SHARED;
295         if (flags & MONO_MMAP_FIXED)
296                 mflags |= MAP_FIXED;
297         if (flags & MONO_MMAP_32BIT)
298                 mflags |= MAP_32BIT;
299
300         BEGIN_CRITICAL_SECTION;
301         ptr = mmap (0, length, prot, mflags, fd, offset);
302         END_CRITICAL_SECTION;
303         if (ptr == MAP_FAILED)
304                 return NULL;
305         *ret_handle = (void*)length;
306         return ptr;
307 }
308
309 /**
310  * mono_file_unmap:
311  * \param addr memory address returned by mono_file_map ()
312  * \param handle handle of memory map
313  * Remove the memory mapping at the address \p addr.
314  * \p handle must be the value returned in ret_handle by \c mono_file_map().
315  * \returns \c 0 on success.
316  */
317 int
318 mono_file_unmap (void *addr, void *handle)
319 {
320         int res;
321
322         BEGIN_CRITICAL_SECTION;
323         res = munmap (addr, (size_t)handle);
324         END_CRITICAL_SECTION;
325
326         return res;
327 }
328
329 /**
330  * mono_mprotect:
331  * \param addr memory address
332  * \param length size of memory area
333  * \param flags new protection flags
334  * Change the protection for the memory area at \p addr for \p length bytes
335  * to matche the supplied \p flags.
336  * If \p flags includes MON_MMAP_DISCARD the pages are discarded from memory
337  * and the area is cleared to zero.
338  * \p addr must be aligned to the page size.
339  * \p length must be a multiple of the page size.
340  * \returns \c 0 on success.
341  */
342 int
343 mono_mprotect (void *addr, size_t length, int flags)
344 {
345         int prot = prot_from_flags (flags);
346
347         if (flags & MONO_MMAP_DISCARD) {
348                 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
349 #ifdef __linux__
350                 if (madvise (addr, length, MADV_DONTNEED))
351                         memset (addr, 0, length);
352 #else
353                 memset (addr, 0, length);
354 #ifdef HAVE_MADVISE
355                 madvise (addr, length, MADV_DONTNEED);
356                 madvise (addr, length, MADV_FREE);
357 #else
358                 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
359 #endif
360 #endif
361         }
362         return mprotect (addr, length, prot);
363 }
364
365 #else
366
367 /* dummy malloc-based implementation */
368 int
369 mono_pagesize (void)
370 {
371         return 4096;
372 }
373
374 int
375 mono_valloc_granule (void)
376 {
377         return mono_pagesize ();
378 }
379
380 void*
381 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
382 {
383         g_assert (addr == NULL);
384         return mono_valloc_aligned (length, mono_pagesize (), flags, type);
385 }
386
387 void*
388 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
389 {
390         void *res = NULL;
391         if (posix_memalign (&res, alignment, size))
392                 return NULL;
393         return res;
394 }
395
396 #define HAVE_VALLOC_ALIGNED
397
398 int
399 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
400 {
401         g_free (addr);
402         return 0;
403 }
404
405 int
406 mono_mprotect (void *addr, size_t length, int flags)
407 {
408         if (flags & MONO_MMAP_DISCARD) {
409                 memset (addr, 0, length);
410         }
411         return 0;
412 }
413
414 #endif // HAVE_MMAP
415
416 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
417
418 static int use_shared_area;
419
420 static gboolean
421 shared_area_disabled (void)
422 {
423         if (!use_shared_area) {
424                 if (g_hasenv ("MONO_DISABLE_SHARED_AREA"))
425                         use_shared_area = -1;
426                 else
427                         use_shared_area = 1;
428         }
429         return use_shared_area == -1;
430 }
431
432 static int
433 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
434 {
435         int i, j = 0;
436         int num;
437         void *data;
438         gpointer *processes = mono_process_list (&num);
439         for (i = 0; i < num; ++i) {
440                 data = mono_shared_area_for_pid (processes [i]);
441                 if (!data)
442                         continue;
443                 mono_shared_area_unload (data);
444                 if (!cleanup) {
445                         if (j < count)
446                                 array [j++] = processes [i];
447                         else
448                                 break;
449                 }
450         }
451         g_free (processes);
452         return j;
453 }
454
455 static int
456 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
457 {
458         const char *name;
459         int i = 0;
460         int curpid = getpid ();
461         GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
462         if (!dir)
463                 return mono_shared_area_instances_slow (array, count, cleanup);
464         while ((name = g_dir_read_name (dir))) {
465                 int pid;
466                 char *nend;
467                 if (strncmp (name, "mono.", 5))
468                         continue;
469                 pid = strtol (name + 5, &nend, 10);
470                 if (pid <= 0 || nend == name + 5 || *nend)
471                         continue;
472                 if (!cleanup) {
473                         if (i < count)
474                                 array [i++] = GINT_TO_POINTER (pid);
475                         else
476                                 break;
477                 }
478                 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
479                         char buf [128];
480                         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
481                         shm_unlink (buf);
482                 }
483         }
484         g_dir_close (dir);
485         return i;
486 }
487
488 void*
489 mono_shared_area (void)
490 {
491         int fd;
492         int pid = getpid ();
493         /* we should allow the user to configure the size */
494         int size = mono_pagesize ();
495         char buf [128];
496         void *res;
497         SAreaHeader *header;
498
499         if (shared_area_disabled ()) {
500                 if (!malloced_shared_area)
501                         malloced_shared_area = malloc_shared_area (0);
502                 /* get the pid here */
503                 return malloced_shared_area;
504         }
505
506         /* perform cleanup of segments left over from dead processes */
507         mono_shared_area_instances_helper (NULL, 0, TRUE);
508
509         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
510
511         fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
512         if (fd == -1 && errno == EEXIST) {
513                 /* leftover */
514                 shm_unlink (buf);
515                 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
516         }
517         /* in case of failure we try to return a memory area anyway,
518          * even if it means the data can't be read by other processes
519          */
520         if (fd == -1)
521                 return malloc_shared_area (pid);
522         if (ftruncate (fd, size) != 0) {
523                 shm_unlink (buf);
524                 close (fd);
525         }
526         BEGIN_CRITICAL_SECTION;
527         res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
528         END_CRITICAL_SECTION;
529
530         if (res == MAP_FAILED) {
531                 shm_unlink (buf);
532                 close (fd);
533                 return malloc_shared_area (pid);
534         }
535         /* we don't need the file descriptor anymore */
536         close (fd);
537         header = (SAreaHeader *) res;
538         header->size = size;
539         header->pid = pid;
540         header->stats_start = sizeof (SAreaHeader);
541         header->stats_end = sizeof (SAreaHeader);
542
543         mono_atexit (mono_shared_area_remove);
544         return res;
545 }
546
547 void
548 mono_shared_area_remove (void)
549 {
550         char buf [128];
551
552         if (shared_area_disabled ()) {
553                 if (malloced_shared_area)
554                         g_free (malloced_shared_area);
555                 return;
556         }
557
558         g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
559         shm_unlink (buf);
560         if (malloced_shared_area)
561                 g_free (malloced_shared_area);
562 }
563
564 void*
565 mono_shared_area_for_pid (void *pid)
566 {
567         int fd;
568         /* we should allow the user to configure the size */
569         int size = mono_pagesize ();
570         char buf [128];
571         void *res;
572
573         if (shared_area_disabled ())
574                 return NULL;
575
576         g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
577
578         fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
579         if (fd == -1)
580                 return NULL;
581         BEGIN_CRITICAL_SECTION;
582         res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
583         END_CRITICAL_SECTION;
584
585         if (res == MAP_FAILED) {
586                 close (fd);
587                 return NULL;
588         }
589         /* FIXME: validate the area */
590         /* we don't need the file descriptor anymore */
591         close (fd);
592         return res;
593 }
594
595 void
596 mono_shared_area_unload  (void *area)
597 {
598         /* FIXME: currently we load only a page */
599         BEGIN_CRITICAL_SECTION;
600         munmap (area, mono_pagesize ());
601         END_CRITICAL_SECTION;
602 }
603
604 int
605 mono_shared_area_instances (void **array, int count)
606 {
607         return mono_shared_area_instances_helper (array, count, FALSE);
608 }
609 #else
610 void*
611 mono_shared_area (void)
612 {
613         if (!malloced_shared_area)
614                 malloced_shared_area = malloc_shared_area (getpid ());
615         /* get the pid here */
616         return malloced_shared_area;
617 }
618
619 void
620 mono_shared_area_remove (void)
621 {
622         if (malloced_shared_area)
623                 g_free (malloced_shared_area);
624         malloced_shared_area = NULL;
625 }
626
627 void*
628 mono_shared_area_for_pid (void *pid)
629 {
630         return NULL;
631 }
632
633 void
634 mono_shared_area_unload (void *area)
635 {
636 }
637
638 int
639 mono_shared_area_instances (void **array, int count)
640 {
641         return 0;
642 }
643
644 #endif // HAVE_SHM_OPEN
645
646 #endif // HOST_WIN32
647
648 #ifndef HAVE_VALLOC_ALIGNED
649 void*
650 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
651 {
652         /* Allocate twice the memory to be able to put the block on an aligned address */
653         char *mem = (char *) mono_valloc (NULL, size + alignment, flags, type);
654         char *aligned;
655
656         if (!mem)
657                 return NULL;
658
659         aligned = aligned_address (mem, size, alignment);
660
661         if (aligned > mem)
662                 mono_vfree (mem, aligned - mem, type);
663         if (aligned + size < mem + size + alignment)
664                 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size), type);
665
666         return aligned;
667 }
668 #endif
669
670 int
671 mono_pages_not_faulted (void *addr, size_t size)
672 {
673 #ifdef HAVE_MINCORE
674         int i;
675         gint64 count;
676         int pagesize = mono_pagesize ();
677         int npages = (size + pagesize - 1) / pagesize;
678         char *faulted = (char *) g_malloc0 (sizeof (char*) * npages);
679
680         /*
681          * We cast `faulted` to void* because Linux wants an unsigned
682          * char* while BSD wants a char*.
683          */
684 #ifdef __linux__
685         if (mincore (addr, size, (unsigned char *)faulted) != 0) {
686 #else
687         if (mincore (addr, size, (char *)faulted) != 0) {
688 #endif
689                 count = -1;
690         } else {
691                 count = 0;
692                 for (i = 0; i < npages; ++i) {
693                         if (faulted [i] != 0)
694                                 ++count;
695                 }
696         }
697
698         g_free (faulted);
699
700         return count;
701 #else
702         return -1;
703 #endif
704 }