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