[runtime] Add arch size InterlockedAdd
[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         if (saved_pagesize)
167                 return saved_pagesize;
168         saved_pagesize = getpagesize ();
169         return saved_pagesize;
170 }
171
172 int
173 mono_valloc_granule (void)
174 {
175         return mono_pagesize ();
176 }
177
178 static int
179 prot_from_flags (int flags)
180 {
181         int prot = PROT_NONE;
182         /* translate the protection bits */
183         if (flags & MONO_MMAP_READ)
184                 prot |= PROT_READ;
185         if (flags & MONO_MMAP_WRITE)
186                 prot |= PROT_WRITE;
187         if (flags & MONO_MMAP_EXEC)
188                 prot |= PROT_EXEC;
189         return prot;
190 }
191
192 /**
193  * mono_valloc:
194  * \param addr memory address
195  * \param length memory area size
196  * \param flags protection flags
197  * Allocates \p length bytes of virtual memory with the \p flags
198  * protection. \p addr can be a preferred memory address or a
199  * mandatory one if MONO_MMAP_FIXED is set in \p flags.
200  * \p addr must be pagesize aligned and can be NULL.
201  * \p length must be a multiple of pagesize.
202  * \returns NULL on failure, the address of the memory area otherwise
203  */
204 void*
205 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
206 {
207         void *ptr;
208         int mflags = 0;
209         int prot = prot_from_flags (flags);
210
211         if (!mono_valloc_can_alloc (length))
212                 return NULL;
213
214         /* translate the flags */
215         if (flags & MONO_MMAP_FIXED)
216                 mflags |= MAP_FIXED;
217         if (flags & MONO_MMAP_32BIT)
218                 mflags |= MAP_32BIT;
219
220         mflags |= MAP_ANONYMOUS;
221         mflags |= MAP_PRIVATE;
222
223         BEGIN_CRITICAL_SECTION;
224         ptr = mmap (addr, length, prot, mflags, -1, 0);
225         if (ptr == MAP_FAILED) {
226                 int fd = open ("/dev/zero", O_RDONLY);
227                 if (fd != -1) {
228                         ptr = mmap (addr, length, prot, mflags, fd, 0);
229                         close (fd);
230                 }
231         }
232         END_CRITICAL_SECTION;
233
234         if (ptr == MAP_FAILED)
235                 return NULL;
236
237         account_mem (type, (ssize_t)length);
238
239         return ptr;
240 }
241
242 /**
243  * mono_vfree:
244  * \param addr memory address returned by mono_valloc ()
245  * \param length size of memory area
246  * Remove the memory mapping at the address \p addr.
247  * \returns \c 0 on success.
248  */
249 int
250 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
251 {
252         int res;
253         BEGIN_CRITICAL_SECTION;
254         res = munmap (addr, length);
255         END_CRITICAL_SECTION;
256
257         account_mem (type, -(ssize_t)length);
258
259         return res;
260 }
261
262 /**
263  * mono_file_map:
264  * \param length size of data to map
265  * \param flags protection flags
266  * \param fd file descriptor
267  * \param offset offset in the file
268  * \param ret_handle pointer to storage for returning a handle for the map
269  * Map the area of the file pointed to by the file descriptor \p fd, at offset
270  * \p offset and of size \p length in memory according to the protection flags
271  * \p flags.
272  * \p offset and \p length must be multiples of the page size.
273  * \p ret_handle must point to a void*: this value must be used when unmapping
274  * the memory area using \c mono_file_unmap().
275  */
276 void*
277 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
278 {
279         void *ptr;
280         int mflags = 0;
281         int prot = prot_from_flags (flags);
282         /* translate the flags */
283         if (flags & MONO_MMAP_PRIVATE)
284                 mflags |= MAP_PRIVATE;
285         if (flags & MONO_MMAP_SHARED)
286                 mflags |= MAP_SHARED;
287         if (flags & MONO_MMAP_FIXED)
288                 mflags |= MAP_FIXED;
289         if (flags & MONO_MMAP_32BIT)
290                 mflags |= MAP_32BIT;
291
292         BEGIN_CRITICAL_SECTION;
293         ptr = mmap (0, length, prot, mflags, fd, offset);
294         END_CRITICAL_SECTION;
295         if (ptr == MAP_FAILED)
296                 return NULL;
297         *ret_handle = (void*)length;
298         return ptr;
299 }
300
301 /**
302  * mono_file_unmap:
303  * \param addr memory address returned by mono_file_map ()
304  * \param handle handle of memory map
305  * Remove the memory mapping at the address \p addr.
306  * \p handle must be the value returned in ret_handle by \c mono_file_map().
307  * \returns \c 0 on success.
308  */
309 int
310 mono_file_unmap (void *addr, void *handle)
311 {
312         int res;
313
314         BEGIN_CRITICAL_SECTION;
315         res = munmap (addr, (size_t)handle);
316         END_CRITICAL_SECTION;
317
318         return res;
319 }
320
321 /**
322  * mono_mprotect:
323  * \param addr memory address
324  * \param length size of memory area
325  * \param flags new protection flags
326  * Change the protection for the memory area at \p addr for \p length bytes
327  * to matche the supplied \p flags.
328  * If \p flags includes MON_MMAP_DISCARD the pages are discarded from memory
329  * and the area is cleared to zero.
330  * \p addr must be aligned to the page size.
331  * \p length must be a multiple of the page size.
332  * \returns \c 0 on success.
333  */
334 #if defined(__native_client__)
335 int
336 mono_mprotect (void *addr, size_t length, int flags)
337 {
338         int prot = prot_from_flags (flags);
339         void *new_addr;
340
341         if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
342
343         new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
344         if (new_addr == addr) return 0;
345         return -1;
346 }
347 #else
348 int
349 mono_mprotect (void *addr, size_t length, int flags)
350 {
351         int prot = prot_from_flags (flags);
352
353         if (flags & MONO_MMAP_DISCARD) {
354                 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
355 #ifdef __linux__
356                 if (madvise (addr, length, MADV_DONTNEED))
357                         memset (addr, 0, length);
358 #else
359                 memset (addr, 0, length);
360 #ifdef HAVE_MADVISE
361                 madvise (addr, length, MADV_DONTNEED);
362                 madvise (addr, length, MADV_FREE);
363 #else
364                 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
365 #endif
366 #endif
367         }
368         return mprotect (addr, length, prot);
369 }
370 #endif // __native_client__
371
372 #else
373
374 /* dummy malloc-based implementation */
375 int
376 mono_pagesize (void)
377 {
378         return 4096;
379 }
380
381 int
382 mono_valloc_granule (void)
383 {
384         return mono_pagesize ();
385 }
386
387 void*
388 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
389 {
390         return g_malloc (length);
391 }
392
393 void*
394 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
395 {
396         g_assert_not_reached ();
397 }
398
399 #define HAVE_VALLOC_ALIGNED
400
401 int
402 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
403 {
404         g_free (addr);
405         return 0;
406 }
407
408 int
409 mono_mprotect (void *addr, size_t length, int flags)
410 {
411         if (flags & MONO_MMAP_DISCARD) {
412                 memset (addr, 0, length);
413         }
414         return 0;
415 }
416
417 #endif // HAVE_MMAP
418
419 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
420
421 static int use_shared_area;
422
423 static gboolean
424 shared_area_disabled (void)
425 {
426         if (!use_shared_area) {
427                 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
428                         use_shared_area = -1;
429                 else
430                         use_shared_area = 1;
431         }
432         return use_shared_area == -1;
433 }
434
435 static int
436 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
437 {
438         int i, j = 0;
439         int num;
440         void *data;
441         gpointer *processes = mono_process_list (&num);
442         for (i = 0; i < num; ++i) {
443                 data = mono_shared_area_for_pid (processes [i]);
444                 if (!data)
445                         continue;
446                 mono_shared_area_unload (data);
447                 if (!cleanup) {
448                         if (j < count)
449                                 array [j++] = processes [i];
450                         else
451                                 break;
452                 }
453         }
454         g_free (processes);
455         return j;
456 }
457
458 static int
459 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
460 {
461         const char *name;
462         int i = 0;
463         int curpid = getpid ();
464         GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
465         if (!dir)
466                 return mono_shared_area_instances_slow (array, count, cleanup);
467         while ((name = g_dir_read_name (dir))) {
468                 int pid;
469                 char *nend;
470                 if (strncmp (name, "mono.", 5))
471                         continue;
472                 pid = strtol (name + 5, &nend, 10);
473                 if (pid <= 0 || nend == name + 5 || *nend)
474                         continue;
475                 if (!cleanup) {
476                         if (i < count)
477                                 array [i++] = GINT_TO_POINTER (pid);
478                         else
479                                 break;
480                 }
481                 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
482                         char buf [128];
483                         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
484                         shm_unlink (buf);
485                 }
486         }
487         g_dir_close (dir);
488         return i;
489 }
490
491 void*
492 mono_shared_area (void)
493 {
494         int fd;
495         int pid = getpid ();
496         /* we should allow the user to configure the size */
497         int size = mono_pagesize ();
498         char buf [128];
499         void *res;
500         SAreaHeader *header;
501
502         if (shared_area_disabled ()) {
503                 if (!malloced_shared_area)
504                         malloced_shared_area = malloc_shared_area (0);
505                 /* get the pid here */
506                 return malloced_shared_area;
507         }
508
509         /* perform cleanup of segments left over from dead processes */
510         mono_shared_area_instances_helper (NULL, 0, TRUE);
511
512         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
513
514         fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
515         if (fd == -1 && errno == EEXIST) {
516                 /* leftover */
517                 shm_unlink (buf);
518                 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
519         }
520         /* in case of failure we try to return a memory area anyway,
521          * even if it means the data can't be read by other processes
522          */
523         if (fd == -1)
524                 return malloc_shared_area (pid);
525         if (ftruncate (fd, size) != 0) {
526                 shm_unlink (buf);
527                 close (fd);
528         }
529         BEGIN_CRITICAL_SECTION;
530         res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
531         END_CRITICAL_SECTION;
532
533         if (res == MAP_FAILED) {
534                 shm_unlink (buf);
535                 close (fd);
536                 return malloc_shared_area (pid);
537         }
538         /* we don't need the file descriptor anymore */
539         close (fd);
540         header = (SAreaHeader *) res;
541         header->size = size;
542         header->pid = pid;
543         header->stats_start = sizeof (SAreaHeader);
544         header->stats_end = sizeof (SAreaHeader);
545
546         mono_atexit (mono_shared_area_remove);
547         return res;
548 }
549
550 void
551 mono_shared_area_remove (void)
552 {
553         char buf [128];
554
555         if (shared_area_disabled ()) {
556                 if (malloced_shared_area)
557                         g_free (malloced_shared_area);
558                 return;
559         }
560
561         g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
562         shm_unlink (buf);
563         if (malloced_shared_area)
564                 g_free (malloced_shared_area);
565 }
566
567 void*
568 mono_shared_area_for_pid (void *pid)
569 {
570         int fd;
571         /* we should allow the user to configure the size */
572         int size = mono_pagesize ();
573         char buf [128];
574         void *res;
575
576         if (shared_area_disabled ())
577                 return NULL;
578
579         g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
580
581         fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
582         if (fd == -1)
583                 return NULL;
584         BEGIN_CRITICAL_SECTION;
585         res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
586         END_CRITICAL_SECTION;
587
588         if (res == MAP_FAILED) {
589                 close (fd);
590                 return NULL;
591         }
592         /* FIXME: validate the area */
593         /* we don't need the file descriptor anymore */
594         close (fd);
595         return res;
596 }
597
598 void
599 mono_shared_area_unload  (void *area)
600 {
601         /* FIXME: currently we load only a page */
602         BEGIN_CRITICAL_SECTION;
603         munmap (area, mono_pagesize ());
604         END_CRITICAL_SECTION;
605 }
606
607 int
608 mono_shared_area_instances (void **array, int count)
609 {
610         return mono_shared_area_instances_helper (array, count, FALSE);
611 }
612 #else
613 void*
614 mono_shared_area (void)
615 {
616         if (!malloced_shared_area)
617                 malloced_shared_area = malloc_shared_area (getpid ());
618         /* get the pid here */
619         return malloced_shared_area;
620 }
621
622 void
623 mono_shared_area_remove (void)
624 {
625         if (malloced_shared_area)
626                 g_free (malloced_shared_area);
627         malloced_shared_area = NULL;
628 }
629
630 void*
631 mono_shared_area_for_pid (void *pid)
632 {
633         return NULL;
634 }
635
636 void
637 mono_shared_area_unload (void *area)
638 {
639 }
640
641 int
642 mono_shared_area_instances (void **array, int count)
643 {
644         return 0;
645 }
646
647 #endif // HAVE_SHM_OPEN
648
649 #endif // HOST_WIN32
650
651 #ifndef HAVE_VALLOC_ALIGNED
652 void*
653 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
654 {
655         /* Allocate twice the memory to be able to put the block on an aligned address */
656         char *mem = (char *) mono_valloc (NULL, size + alignment, flags, type);
657         char *aligned;
658
659         if (!mem)
660                 return NULL;
661
662         aligned = aligned_address (mem, size, alignment);
663
664         if (aligned > mem)
665                 mono_vfree (mem, aligned - mem, type);
666         if (aligned + size < mem + size + alignment)
667                 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size), type);
668
669         return aligned;
670 }
671 #endif
672
673 int
674 mono_pages_not_faulted (void *addr, size_t size)
675 {
676 #ifdef HAVE_MINCORE
677         int i;
678         gint64 count;
679         int pagesize = mono_pagesize ();
680         int npages = (size + pagesize - 1) / pagesize;
681         char *faulted = (char *) g_malloc0 (sizeof (char*) * npages);
682
683         /*
684          * We cast `faulted` to void* because Linux wants an unsigned
685          * char* while BSD wants a char*.
686          */
687 #ifdef __linux__
688         if (mincore (addr, size, (unsigned char *)faulted) != 0) {
689 #else
690         if (mincore (addr, size, (char *)faulted) != 0) {
691 #endif
692                 count = -1;
693         } else {
694                 count = 0;
695                 for (i = 0; i < npages; ++i) {
696                         if (faulted [i] != 0)
697                                 ++count;
698                 }
699         }
700
701         g_free (faulted);
702
703         return count;
704 #else
705         return -1;
706 #endif
707 }