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