Give a better panic if the allocation debugger is used uninitialised
[ghc-hetmet.git] / rts / RtsUtils.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 1998-2004
4  *
5  * General utility functions used in the RTS.
6  *
7  * ---------------------------------------------------------------------------*/
8
9 #include "PosixSource.h"
10
11 #include "Rts.h"
12 #include "RtsAPI.h"
13 #include "RtsFlags.h"
14 #include "RtsUtils.h"
15 #include "Ticky.h"
16
17 #ifdef HAVE_TIME_H
18 #include <time.h>
19 #endif
20
21 #ifdef HAVE_FCNTL_H
22 #include <fcntl.h>
23 #endif
24
25 #ifdef HAVE_GETTIMEOFDAY
26 #include <sys/time.h>
27 #endif
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33
34 #ifdef HAVE_SIGNAL_H
35 #include <signal.h>
36 #endif
37
38 #if defined(THREADED_RTS) && defined(openbsd_HOST_OS) && defined(HAVE_PTHREAD_H)
39 #include <pthread.h>
40 #endif
41
42
43 #if defined(_WIN32)
44 #include <windows.h>
45 #endif
46
47 /* -----------------------------------------------------------------------------
48    Debugging allocator
49    -------------------------------------------------------------------------- */
50
51 #if defined(DEBUG)
52
53 typedef struct Allocated_ {
54     void *addr;
55     size_t len;
56     struct Allocated_ *next;
57 } Allocated;
58
59 static Allocated *allocs = NULL;
60
61 #ifdef THREADED_RTS
62 static Mutex allocator_mutex;
63 #endif
64
65 void
66 initAllocator(void)
67 {
68     Allocated *a;
69     size_t alloc_size;
70
71 #ifdef THREADED_RTS
72     initMutex(&allocator_mutex);
73 #endif
74     alloc_size = sizeof(Allocated);
75     if ((a = (Allocated *) malloc(alloc_size)) == NULL) {
76       /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
77       MallocFailHook((W_) alloc_size, "initialising debugging allocator");
78       stg_exit(EXIT_INTERNAL_ERROR);
79     }
80     a->addr = NULL;
81     a->len = 0;
82     a->next = NULL;
83     allocs = a;
84 }
85
86 void
87 shutdownAllocator(void)
88 {
89 #ifdef THREADED_RTS
90     closeMutex(&allocator_mutex);
91 #endif
92 }
93
94 static void addAllocation(void *addr, size_t len) {
95     Allocated *a;
96     size_t alloc_size;
97
98     if (allocs == NULL) {
99         barf("addAllocation: allocator debugger not initialised");
100     }
101     alloc_size = sizeof(Allocated);
102     if ((a = (Allocated *) malloc(alloc_size)) == NULL) {
103       /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
104       MallocFailHook((W_) alloc_size, "creating info for debugging allocator");
105       stg_exit(EXIT_INTERNAL_ERROR);
106     }
107     a->addr = addr;
108     a->len = len;
109     ACQUIRE_LOCK(&allocator_mutex);
110     a->next = allocs->next;
111     allocs->next = a;
112     RELEASE_LOCK(&allocator_mutex);
113 }
114
115 static void removeAllocation(void *addr) {
116     Allocated *prev, *a;
117
118     if (allocs == NULL) {
119         barf("addAllocation: allocator debugger not initialised");
120     }
121     if (addr == NULL) {
122         barf("Freeing NULL!");
123     }
124
125     ACQUIRE_LOCK(&allocator_mutex);
126     prev = allocs;
127     a = prev->next;
128     while (a != NULL) {
129         if (a->addr == addr) {
130             prev->next = a->next;
131             memset(addr, 0xaa, a->len);
132             free(a);
133             RELEASE_LOCK(&allocator_mutex);
134             return;
135         }
136         prev = a;
137         a = a->next;
138     }
139     barf("Freeing non-allocated memory at %p", addr);
140 }
141 #endif
142
143 /* -----------------------------------------------------------------------------
144    Result-checking malloc wrappers.
145    -------------------------------------------------------------------------- */
146
147 void *
148 stgMallocBytes (int n, char *msg)
149 {
150     char *space;
151     size_t n2;
152
153     n2 = (size_t) n;
154     if ((space = (char *) malloc(n2)) == NULL) {
155       /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
156       MallocFailHook((W_) n, msg); /*msg*/
157       stg_exit(EXIT_INTERNAL_ERROR);
158     }
159 #if defined(DEBUG)
160     addAllocation(space, n2);
161 #endif
162     return space;
163 }
164
165 void *
166 stgReallocBytes (void *p, int n, char *msg)
167 {
168     char *space;
169     size_t n2;
170
171     n2 = (size_t) n;
172     if ((space = (char *) realloc(p, (size_t) n2)) == NULL) {
173       /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
174       MallocFailHook((W_) n, msg); /*msg*/
175       stg_exit(EXIT_INTERNAL_ERROR);
176     }
177 #if defined(DEBUG)
178     removeAllocation(p);
179     addAllocation(space, n2);
180 #endif
181     return space;
182 }
183
184 void *
185 stgCallocBytes (int n, int m, char *msg)
186 {
187     char *space;
188
189     if ((space = (char *) calloc((size_t) n, (size_t) m)) == NULL) {
190       /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
191       MallocFailHook((W_) n*m, msg); /*msg*/
192       stg_exit(EXIT_INTERNAL_ERROR);
193     }
194 #if defined(DEBUG)
195     addAllocation(space, (size_t) n * (size_t) m);
196 #endif
197     return space;
198 }
199
200 /* To simplify changing the underlying allocator used
201  * by stgMallocBytes(), provide stgFree() as well.
202  */
203 void
204 stgFree(void* p)
205 {
206 #if defined(DEBUG)
207   removeAllocation(p);
208 #endif
209   free(p);
210 }
211
212 /* -----------------------------------------------------------------------------
213    Stack overflow
214    
215    Not sure if this belongs here.
216    -------------------------------------------------------------------------- */
217
218 void
219 stackOverflow(void)
220 {
221   StackOverflowHook(RtsFlags.GcFlags.maxStkSize * sizeof(W_));
222
223 #if defined(TICKY_TICKY)
224   if (RtsFlags.TickyFlags.showTickyStats) PrintTickyInfo();
225 #endif
226 }
227
228 void
229 heapOverflow(void)
230 {
231   /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
232   OutOfHeapHook(0/*unknown request size*/, 
233                 RtsFlags.GcFlags.maxHeapSize * BLOCK_SIZE);
234   
235 #if defined(TICKY_TICKY)
236   if (RtsFlags.TickyFlags.showTickyStats) PrintTickyInfo();
237 #endif
238
239   stg_exit(EXIT_HEAPOVERFLOW);
240 }
241
242 /* -----------------------------------------------------------------------------
243    Out-of-line strlen.
244
245    Used in addr2Integer because the C compiler on x86 chokes on
246    strlen, trying to inline it with not enough registers available.
247    -------------------------------------------------------------------------- */
248
249 nat stg_strlen(char *s)
250 {
251    char *p = s;
252
253    while (*p) p++;
254    return p-s;
255 }
256
257
258 /* -----------------------------------------------------------------------------
259    genSym stuff, used by GHC itself for its splitting unique supply.
260
261    ToDo: put this somewhere sensible.
262    -------------------------------------------------------------------------  */
263
264 static I_ __GenSymCounter = 0;
265
266 I_
267 genSymZh(void)
268 {
269     return(__GenSymCounter++);
270 }
271 I_
272 resetGenSymZh(void) /* it's your funeral */
273 {
274     __GenSymCounter=0;
275     return(__GenSymCounter);
276 }
277
278 /* -----------------------------------------------------------------------------
279    Get the current time as a string.  Used in profiling reports.
280    -------------------------------------------------------------------------- */
281
282 #if defined(PROFILING) || defined(DEBUG) || defined(PAR) || defined(GRAN)
283 char *
284 time_str(void)
285 {
286     static time_t now = 0;
287     static char nowstr[26];
288
289     if (now == 0) {
290         time(&now);
291 #if HAVE_CTIME_R
292         ctime_r(&now, nowstr);
293 #else
294         strcpy(nowstr, ctime(&now));
295 #endif
296         memmove(nowstr+16,nowstr+19,7);
297         nowstr[21] = '\0';  // removes the \n
298     }
299     return nowstr;
300 }
301 #endif
302
303 /* -----------------------------------------------------------------------------
304  * Reset a file handle to blocking mode.  We do this for the standard
305  * file descriptors before exiting, because the shell doesn't always
306  * clean up for us.
307  * -------------------------------------------------------------------------- */
308
309 #if !defined(mingw32_HOST_OS)
310 void
311 resetNonBlockingFd(int fd)
312 {
313   long fd_flags;
314
315   /* clear the non-blocking flag on this file descriptor */
316   fd_flags = fcntl(fd, F_GETFL);
317   if (fd_flags & O_NONBLOCK) {
318     fcntl(fd, F_SETFL, fd_flags & ~O_NONBLOCK);
319   }
320 }
321
322 void
323 setNonBlockingFd(int fd)
324 {
325   long fd_flags;
326
327   /* clear the non-blocking flag on this file descriptor */
328   fd_flags = fcntl(fd, F_GETFL);
329   if (!(fd_flags & O_NONBLOCK)) {
330     fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK);
331   }
332 }
333 #else
334 /* Stub defns -- async / non-blocking IO is not done 
335  * via O_NONBLOCK and select() under Win32. 
336  */
337 void resetNonBlockingFd(int fd STG_UNUSED) {}
338 void setNonBlockingFd(int fd STG_UNUSED) {}
339 #endif
340
341 #ifdef PAR
342 static ullong startTime = 0;
343
344 /* used in a parallel setup */
345 ullong
346 msTime(void)
347 {
348 # if defined(HAVE_GETCLOCK) && !defined(alpha_HOST_ARCH) && !defined(hppa1_1_HOST_ARCH)
349     struct timespec tv;
350
351     if (getclock(TIMEOFDAY, &tv) != 0) {
352         fflush(stdout);
353         fprintf(stderr, "Clock failed\n");
354         stg_exit(EXIT_FAILURE);
355     }
356     return tv.tv_sec * LL(1000) + tv.tv_nsec / LL(1000000) - startTime;
357 # elif HAVE_GETTIMEOFDAY && !defined(alpha_HOST_ARCH)
358     struct timeval tv;
359  
360     if (gettimeofday(&tv, NULL) != 0) {
361         fflush(stdout);
362         fprintf(stderr, "Clock failed\n");
363         stg_exit(EXIT_FAILURE);
364     }
365     return tv.tv_sec * LL(1000) + tv.tv_usec / LL(1000) - startTime;
366 # else
367     time_t t;
368     if ((t = time(NULL)) == (time_t) -1) {
369         fflush(stdout);
370         fprintf(stderr, "Clock failed\n");
371         stg_exit(EXIT_FAILURE);
372     }
373     return t * LL(1000) - startTime;
374 # endif
375 }
376 #endif /* PAR */
377
378 /* -----------------------------------------------------------------------------
379    Print large numbers, with punctuation.
380    -------------------------------------------------------------------------- */
381
382 char *
383 ullong_format_string(ullong x, char *s, rtsBool with_commas)
384 {
385     if (x < (ullong)1000) 
386         sprintf(s, "%lu", (lnat)x);
387     else if (x < (ullong)1000000)
388         sprintf(s, (with_commas) ? "%lu,%3.3lu" : "%lu%3.3lu",
389                 (lnat)((x)/(ullong)1000),
390                 (lnat)((x)%(ullong)1000));
391     else if (x < (ullong)1000000000)
392         sprintf(s, (with_commas) ? "%lu,%3.3lu,%3.3lu" :  "%lu%3.3lu%3.3lu",
393                 (lnat)((x)/(ullong)1000000),
394                 (lnat)((x)/(ullong)1000%(ullong)1000),
395                 (lnat)((x)%(ullong)1000));
396     else
397         sprintf(s, (with_commas) ? "%lu,%3.3lu,%3.3lu,%3.3lu" : "%lu%3.3lu%3.3lu%3.3lu",
398                 (lnat)((x)/(ullong)1000000000),
399                 (lnat)((x)/(ullong)1000000%(ullong)1000),
400                 (lnat)((x)/(ullong)1000%(ullong)1000), 
401                 (lnat)((x)%(ullong)1000));
402     return s;
403 }
404
405
406 // Can be used as a breakpoint to set on every heap check failure.
407 #ifdef DEBUG
408 void
409 heapCheckFail( void )
410 {
411 }
412 #endif
413
414 /* 
415  * It seems that pthreads and signals interact oddly in OpenBSD & FreeBSD
416  * pthreads (and possibly others). When linking with -lpthreads, we
417  * have to use pthread_kill to send blockable signals. So use that
418  * when we have a threaded rts. So System.Posix.Signals will call
419  * genericRaise(), rather than raise(3).
420  */
421 int genericRaise(int sig) {
422 #if defined(THREADED_RTS) && (defined(openbsd_HOST_OS) || defined(freebsd_HOST_OS))
423         return pthread_kill(pthread_self(), sig);
424 #else
425         return raise(sig);
426 #endif
427 }