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