aboutsummaryrefslogtreecommitdiffstats
path: root/erts/etc/vxworks/reclaim.c
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/etc/vxworks/reclaim.c
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/etc/vxworks/reclaim.c')
-rw-r--r--erts/etc/vxworks/reclaim.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/erts/etc/vxworks/reclaim.c b/erts/etc/vxworks/reclaim.c
new file mode 100644
index 0000000000..d8676b3750
--- /dev/null
+++ b/erts/etc/vxworks/reclaim.c
@@ -0,0 +1,551 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vxWorks.h>
+#include <version.h>
+#include <string.h>
+#include <types.h>
+#include <sigLib.h>
+#include <ioLib.h>
+#include <iosLib.h>
+#include <fioLib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <symLib.h>
+#include <sysLib.h>
+#include <sysSymTbl.h>
+#include <loadLib.h>
+#include <taskLib.h>
+#include <taskVarLib.h>
+#include <taskHookLib.h>
+#include <tickLib.h>
+#include <time.h>
+#include <rngLib.h>
+#include <semLib.h>
+#include <selectLib.h>
+#include <sockLib.h>
+#include <a_out.h>
+#include <wdLib.h>
+#include <timers.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdarg.h>
+
+#include <stdio.h>
+#include <math.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+#define RECLAIM_NO_ALIAS /* No #defines for open/malloc/fopen etc */
+#include "reclaim.h"
+#include "reclaim_private.h"
+
+#undef open
+#undef creat
+#undef socket
+#undef accept
+#undef close
+#undef fopen
+#undef fdopen
+#undef freopen
+#undef fclose
+/* XXX Should do opendir/closedir too... */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#undef cfree
+
+#ifdef _ARCH_PPC
+#define MAX_FILES_SYM_NAME "maxFiles"
+#else
+#define MAX_FILES_SYM_NAME "_maxFiles"
+#endif
+
+
+/*
+ * Use another free() function upon task deletion?
+ * Note! When changing free function, the new free function will have to
+ * be able to cope with ALL previously used malloced areas, that is
+ * it has to be able to find out how things are malloced
+ * to free them in the right way!
+ */
+static FreeFunction reclaim_free_function = NULL;
+
+/* delete hook handling (see below) */
+static int hook_added = 0; /* Initated at first reclaim_init, an extra
+ non MT-safe check that we only get
+ initialized once */
+
+/* Forward... */
+static void save_reclaim(WIND_TCB *tcbp);
+
+struct mall_data {
+ struct mall_data *next;
+ char *self;
+};
+
+struct task_data {
+ FUNCPTR version; /* To recognize when we have reloaded */
+ int max_files; /* It may change... */
+ struct fd_set open_fds;
+ struct mall_data *mall_data;
+ FUNCPTR delete_hook;
+ caddr_t hook_data;
+ FILE *open_fps[1]; /* Will be max_files long */
+} *task_data = NULL;
+
+static int max_files = 50; /* default configAll.h */
+
+int reclaim_max_files(void)
+{
+ return max_files;
+}
+
+#ifdef DEBUG
+#define check_hook() \
+((task_data != NULL || \
+ fdprintf(2,"check_hook() TID = 0x%08x, Called from line %d\n", \
+ (unsigned int)taskIdSelf(),\
+ __LINE__)) && \
+(task_data != NULL || \
+ (taskVarAdd(0, (int *)&task_data) == OK && \
+ (task_data = (struct task_data *)\
+ calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
+ (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
+ (task_data->max_files = max_files) != 0 && \
+ fdprintf(2,"taskVar Added for 0x%08x\n",(unsigned int)taskIdSelf()))))
+#else
+#define check_hook() \
+(task_data != NULL || \
+ (taskVarAdd(0, (int *)&task_data) == OK && \
+ (task_data = (struct task_data *)\
+ calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
+ (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
+ (task_data->max_files = max_files) != 0))
+#endif
+
+/*
+ * Global initialization of the reclaim data structures, mainly
+ * the max_files variable. This HAS to be called by some task before
+ * the first task that utilizes this exit's, preferrably before any
+ * task makes the first use of this library.
+ */
+STATUS reclaim_init(void)
+{
+ int *mp;
+ SYM_TYPE type;
+ struct task_data *tdp;
+ int i;
+
+ if (!hook_added) {
+ /* race condition */
+ ++hook_added;
+ /* Try to find the maxFiles value */
+ if (symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
+ &type,
+ N_EXT | N_BSS, N_EXT | N_BSS) == OK ||
+ symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
+ &type,
+ N_EXT | N_DATA, N_EXT | N_DATA) == OK) {
+
+#ifdef DEBUG
+ fdprintf(2, "Found maxFiles=%d\n", *mp);
+#endif
+ if (*mp <= FD_SETSIZE)
+ max_files = *mp;
+ else
+ max_files = FD_SETSIZE;
+ }
+ if (task_data != NULL && task_data->max_files != max_files) {
+ /* fix our own iff we have one */
+ if ((tdp = (struct task_data *)
+ realloc(task_data, sizeof(struct task_data) +
+ max_files*sizeof(FILE *))) != NULL) {
+ task_data = tdp;
+ for (i = task_data->max_files; i < max_files; i++)
+ task_data->open_fps[i] = NULL;
+ task_data->max_files = max_files;
+ }
+ }
+ /* Make sure taskVariables are deleted AFTER our hook is run. */
+ taskVarInit();
+ if(taskDeleteHookAdd((FUNCPTR)save_reclaim) != OK) {
+ fprintf(stderr,
+ "Panic: taskDeleteHook cannot be added for reclaim.\n");
+ return ERROR;
+ }
+ return OK;
+ } else
+ return ERROR;
+}
+
+/* N.B.!! We do *not* execute in the context of the dying task here,
+ but rather that of tExcTask - we do get a pointer to the task's
+ TCB though - this pointer is in fact also the task's ID. */
+static void save_reclaim(WIND_TCB *tcbp)
+{
+ int i, var, oldfd;
+ struct task_data *tdp;
+ struct mall_data *mdp, *mdnextp;
+
+ if ((var = taskVarGet((int)tcbp, (int *)&task_data)) != ERROR &&
+ var != 0) {
+ tdp = (struct task_data *)var;
+ if (tdp->version == (FUNCPTR)save_reclaim) { /* Only handle our own */
+#ifdef DEBUG
+ fdprintf(2, "Reclaiming for task id 0x%x:\nFiles: ", (int)tcbp);
+#endif
+ /* Ugh! VxWorks doesn't even flush stdout/err - we need to
+ get at those (which are task-private of course, i.e. we
+ can't just do fflush(stdout) here) - we could be really
+ pedantic and try to redefine stdout/err (which "are"
+ function calls) too, snarfing the values when they are
+ used - but besides the overhead this is problematic since
+ they are actually #defines already... We'll peek in the
+ TCB instead (no documentation of course). And of course,
+ we must also meddle with the *file descriptor* indirections,
+ or we'll just flush out on tExcTask's descriptors... */
+ for (i = 1; i <= 2; i++) {
+ if (tcbp->taskStdFp[i] != NULL) {
+#ifdef DEBUG
+ fdprintf(2, "fflush(%s) ", i == 1 ? "stdout" : "stderr");
+#endif
+ oldfd = ioTaskStdGet(0, i);
+ ioTaskStdSet(0, i, tcbp->taskStd[i]);
+ fflush(tcbp->taskStdFp[i]);
+ ioTaskStdSet(0, i, oldfd);
+ }
+ }
+ for (i = 3; i < tdp->max_files; i++) {
+ if (FD_ISSET(i, &tdp->open_fds)) {
+#ifdef DEBUG
+ fdprintf(2, "close(%d) ", i);
+#endif
+ (void) close(i);
+ }
+ if (tdp->open_fps[i] != NULL) {
+#ifdef DEBUG
+ fdprintf(2, "fclose(%0x%x) ", (int)tdp->open_fps[i]);
+#endif
+ (void) fclose(tdp->open_fps[i]);
+ }
+ }
+ i = 0;
+ mdp = tdp->mall_data;
+ while (mdp != NULL) {
+ mdnextp = mdp->next;
+ if(reclaim_free_function != NULL)
+ (*reclaim_free_function)(mdp->self);
+ else
+ free(mdp->self);
+ i++;
+ mdp = mdnextp;
+ }
+#ifdef DEBUG
+ fdprintf(2, "\nFreeing memory: total %d mallocs\n", i);
+#endif
+
+ if (tdp->delete_hook != NULL) {
+#ifdef DEBUG
+ fdprintf(2, "Calling delete hook at 0x%08x\n", tdp->delete_hook);
+#endif
+ (*tdp->delete_hook)(tdp->hook_data);
+#ifdef DEBUG
+ fdprintf(2, "Called delete hook at 0x%08x\n", tdp->delete_hook);
+#endif
+ }
+#ifdef DEBUG
+ fdprintf(2, "Freeing own mem at 0x%08x\n", tdp);
+#endif
+ (void) free((char *)tdp);
+#ifdef DEBUG
+ fdprintf(2, "Freed own mem at 0x%08x, done (0x%08x)\n**********\n", tdp,
+ taskIdSelf());
+ checkStack(0);
+#endif
+ }
+ }
+#ifdef DEBUG
+ else
+ fdprintf(2, "No task data found for id 0x%x, var = %d\n", (int)tcbp, var);
+#endif
+}
+
+/*
+ * This sets another free function to be used by the task deletion hook.
+ * The free function HAS to be able to free ANY type of dynamically allocated
+ * memory that can be in the task data list of allocated memory, that is
+ * also memory that's allocated before the free function was set.
+ * A "user-supplied" free function is GLOBAL to the system!!!
+ * A race condition is present, a task delete hook may be running when this
+ * function is called, that may not be especially funny...
+ */
+void set_reclaim_free_function(FreeFunction f){
+ reclaim_free_function = f;
+}
+
+void save_delete_hook(FUNCPTR func, caddr_t parm)
+{
+ if (check_hook()) {
+ task_data->delete_hook = func;
+ task_data->hook_data = parm;
+ }
+}
+
+/*
+ * plain_malloc is only used by spawn_start; plain_free by call_proc;
+ * save_fd is used by both.
+ */
+void *plain_malloc(size_t size){
+ return(malloc(size));
+}
+
+void *plain_realloc(void *ptr, size_t size){
+ return(realloc(ptr, size));
+}
+
+void plain_free(void *ptr){
+ free(ptr);
+}
+
+void save_fd(int fd){
+ if (fd >= 0 && check_hook() && fd < task_data->max_files)
+ FD_SET(fd, &task_data->open_fds);
+}
+
+int save_open(char *path, int flags, /*mode_t mode*/ ...){
+ int fd;
+ mode_t mode = 0;
+ if(flags & O_CREAT){
+ va_list pvar;
+ va_start(pvar,flags);
+#ifdef __GNUC__
+#warning save_open() gives three known alignment warnings.
+#endif
+ mode = va_arg(pvar, mode_t);
+ va_end(pvar);
+ }
+ if ((fd = open(path, flags, mode)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_creat(char *path, int mode){
+ int fd;
+ if ((fd = creat(path, mode)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_socket(int domain, int type, int protocol){
+ int fd;
+ if ((fd = socket(domain, type, protocol)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_accept(int s, struct sockaddr *addr, int *addrlen){
+ int fd;
+ if ((fd = accept(s, addr, addrlen)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_close(int fd){
+ if (fd >= 0 && fd <= FD_SETSIZE && check_hook())
+ FD_CLR(fd, &task_data->open_fds);
+ return(close(fd));
+}
+
+/* The dealing with FILE *'s below isn't strictly correct, we assume
+ that one never has several pointing to the same fd - in the unlikely
+ event that one does, all but the last one opened is forgotten */
+FILE *save_fopen(const char *filename, char *type){
+ FILE *fp;
+
+ if ((fp = fopen(filename, type)) != NULL &&
+ check_hook() && fileno(fp) < task_data->max_files)
+ task_data->open_fps[fileno(fp)] = fp;
+ return(fp);
+}
+
+FILE *save_fdopen(int fd, char *type){
+ FILE *fp;
+
+ if ((fp = fdopen(fd, type)) != NULL &&
+ check_hook() && fileno(fp) < task_data->max_files) {
+ task_data->open_fps[fileno(fp)] = fp;
+ FD_CLR(fd, &task_data->open_fds);
+ }
+ return(fp);
+}
+
+FILE *save_freopen(char *filename, char *type, FILE *stream){
+ FILE *fp;
+
+ if (check_hook()) {
+ if(fileno(stream) < task_data->max_files &&
+ task_data->open_fps[fileno(stream)] == stream)
+ task_data->open_fps[fileno(stream)] = NULL;
+ if ((fp = freopen(filename, type, stream)) != NULL &&
+ fileno(fp) < task_data->max_files)
+ task_data->open_fps[fileno(fp)] = fp;
+ } else
+ fp = freopen(filename, type, stream);
+ return(fp);
+}
+
+int save_fclose(FILE *stream){
+ if (check_hook() && fileno(stream) < task_data->max_files &&
+ task_data->open_fps[fileno(stream)] == stream)
+ task_data->open_fps[fileno(stream)] = NULL;
+ return(fclose(stream));
+}
+
+/* We link all malloc'ed segments by adding a couple of pointers
+ at the *end* - that way we can return the address malloc gave
+ (need to make sure we align those pointers) */
+
+/*
+ #define MALL_MARGIN 32
+ #define save_mall_size(size) save_mall_size1((size) + 2 * MALL_MARGIN)
+ #define save_mall_size1(size) \
+ (((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
+
+ #define save_mall_enq(ptr, mdp) save_mall_enq1((ptr), (mdp) - MALL_MARGIN)
+ #define save_mall_enq1(ptr, mdp) \
+ (((struct mall_data *)(mdp))->self = (ptr), \
+ ((struct mall_data *)(mdp))->next = task_data->mall_data, \
+ task_data->mall_data = (struct mall_data *)(mdp))
+*/
+#define save_mall_size(size) \
+(((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
+#define save_mall_enq(ptr, mdp) \
+(((struct mall_data *)(mdp))->self = (ptr), \
+ ((struct mall_data *)(mdp))->next = task_data->mall_data, \
+ task_data->mall_data = (struct mall_data *)(mdp))
+
+
+#define save_mall_deq(ptr) { \
+ struct mall_data *mdp = task_data->mall_data, \
+ **prevnext = &task_data->mall_data; \
+ while (mdp != NULL && mdp->self != (ptr)) { \
+ prevnext = &mdp->next; \
+ mdp = mdp->next; \
+ } \
+ if (mdp != NULL) *prevnext = mdp->next; \
+}
+
+void *save_malloc2(size_t size, MallocFunction mf){
+ unsigned msize = save_mall_size(size);
+ char *ptr;
+
+ if ((ptr = (*mf)(msize + sizeof(struct mall_data))) != NULL &&
+ check_hook())
+ save_mall_enq((void *) ptr, (void *) (ptr + msize));
+ return((void *) ptr);
+}
+
+void *save_malloc(size_t size){
+ return save_malloc2(size, &malloc);
+}
+
+void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf){
+ unsigned msize = save_mall_size(nelem * elsize);
+ char *ptr;
+
+ if ((ptr = (*cf)(1, msize + sizeof(struct mall_data))) != NULL &&
+ check_hook())
+ save_mall_enq((void *) ptr, (void *) (ptr + msize));
+ return((void *) ptr);
+}
+
+void *save_calloc(size_t nelem, size_t elsize){
+ return save_calloc2(nelem,elsize,&calloc);
+}
+
+void *save_realloc2(void *optr, size_t size, ReallocFunction rf){
+ unsigned msize = save_mall_size(size);
+ char *ptr;
+
+ /* First we must dequeue the old save block, after that
+ we try to realloc, if that succeeds we enqueue the new
+ block, if it fails we have to enqueue the old one anew
+ so we must deduce the size of that old block first. */
+
+ struct mall_data *mdp0 = task_data->mall_data,
+ **prevnext0 = &task_data->mall_data;
+ while (mdp0 != NULL && mdp0->self != (((char *) optr))) {
+ prevnext0 = &mdp0->next;
+ mdp0 = mdp0->next;
+ }
+ /* mdp0 == NULL (can) mean that the block that is realloced
+ have been malloced with an (for example) ordinary malloc
+ (that is not a save_malloc). This is handled like: no dequeing
+ is done of that block, the new block is enqueued */
+ if (mdp0 != NULL)
+ save_mall_deq(((char *) optr));
+
+ if ((ptr = (*rf)(optr, msize + sizeof(struct mall_data))) != NULL &&
+ check_hook())
+ save_mall_enq((void *) ptr, (void *) (ptr + msize));
+ else if (mdp0 != NULL)
+ /* re-enqueue the old block that has just been dequeued */
+ save_mall_enq(((char *) optr), mdp0);
+
+ return((void *) ptr);
+}
+
+void *save_realloc(void *optr, size_t size){
+ return save_realloc2(optr,size,&realloc);
+}
+
+void save_free2(void *ptr, FreeFunction ff)
+{
+ if (check_hook())
+ save_mall_deq(((char *) ptr));
+ (*ff)(ptr);
+}
+
+void save_free(void *ptr){
+ save_free2(ptr,&free);
+}
+
+void save_cfree2(void *ptr, CfreeFunction cf)
+{
+ if (check_hook())
+ save_mall_deq(((char *)ptr));
+ (*cf)(ptr);
+}
+
+void save_cfree(void *ptr){
+ save_cfree2(ptr,&cfree);
+}
+