| /*****************************************************************************\ |
| * xmalloc.c - enhanced malloc routines |
| * Started with Jim Garlick's xmalloc and tied into slurm log facility. |
| * Also added ability to print file, line, and function of caller. |
| * $Id$ |
| ***************************************************************************** |
| * Copyright (C) 2002 The Regents of the University of California. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Jim Garlick <garlick1@llnl.gov> and |
| * Mark Grondona <mgrondona@llnl.gov> |
| * UCRL-CODE-226842. |
| * |
| * This file is part of SLURM, a resource management program. |
| * For details, see <http://www.llnl.gov/linux/slurm/>. |
| * |
| * SLURM is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with SLURM; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #if HAVE_CONFIG_H |
| # include "config.h" |
| #endif |
| |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <limits.h> /* for INT_MAX */ |
| |
| #include "src/common/xmalloc.h" |
| #include "src/common/log.h" |
| #include "src/common/macros.h" |
| |
| #if HAVE_UNSAFE_MALLOC |
| # include <pthread.h> |
| static pthread_mutex_t malloc_lock = PTHREAD_MUTEX_INITIALIZER; |
| # define MALLOC_LOCK() pthread_mutex_lock(&malloc_lock) |
| # define MALLOC_UNLOCK() pthread_mutex_unlock(&malloc_lock) |
| #else |
| # define MALLOC_LOCK() |
| # define MALLOC_UNLOCK() |
| #endif |
| |
| |
| #if NDEBUG |
| # define xmalloc_assert(expr) ((void) (0)) |
| #else |
| static void malloc_assert_failed(char *, const char *, int, |
| const char *, const char *); |
| # define xmalloc_assert(expr) _STMT_START { \ |
| (expr) ? ((void)(0)) : \ |
| malloc_assert_failed(__STRING(expr), file, line, func, \ |
| __CURRENT_FUNC__); \ |
| } _STMT_END |
| #endif /* NDEBUG */ |
| |
| |
| /* |
| * "Safe" version of malloc(). |
| * size (IN) number of bytes to malloc |
| * RETURN pointer to allocate heap space |
| */ |
| void *slurm_xmalloc(size_t size, const char *file, int line, const char *func) |
| { |
| void *new; |
| int *p; |
| |
| |
| xmalloc_assert(size >= 0 && size <= INT_MAX); |
| MALLOC_LOCK(); |
| p = (int *)malloc(size + 2*sizeof(int)); |
| MALLOC_UNLOCK(); |
| if (!p) { |
| /* don't call log functions here, we're probably OOM |
| */ |
| fprintf(log_fp(), "%s:%d: %s: xmalloc(%d) failed\n", |
| file, line, func, (int)size); |
| exit(1); |
| } |
| p[0] = XMALLOC_MAGIC; /* add "secret" magic cookie */ |
| p[1] = (int)size; /* store size in buffer */ |
| |
| new = &p[2]; |
| memset(new, 0, size); |
| return new; |
| } |
| |
| /* |
| * same as above, except return NULL on malloc failure instead of exiting |
| */ |
| void *slurm_try_xmalloc(size_t size, const char *file, int line, |
| const char *func) |
| { |
| void *new; |
| int *p; |
| |
| xmalloc_assert(size >= 0 && size <= INT_MAX); |
| MALLOC_LOCK(); |
| p = (int *)malloc(size + 2*sizeof(int)); |
| MALLOC_UNLOCK(); |
| if (!p) { |
| return NULL; |
| } |
| p[0] = XMALLOC_MAGIC; /* add "secret" magic cookie */ |
| p[1] = (int)size; /* store size in buffer */ |
| |
| new = &p[2]; |
| memset(new, 0, size); |
| return new; |
| } |
| |
| /* |
| * "Safe" version of realloc(). Args are different: pass in a pointer to |
| * the object to be realloced instead of the object itself. |
| * item (IN/OUT) double-pointer to allocated space |
| * newsize (IN) requested size |
| */ |
| void * slurm_xrealloc(void **item, size_t newsize, |
| const char *file, int line, const char *func) |
| { |
| int *p = NULL; |
| |
| /* xmalloc_assert(*item != NULL, file, line, func); */ |
| xmalloc_assert(newsize >= 0 && (int)newsize <= INT_MAX); |
| |
| if (*item != NULL) { |
| int old_size; |
| p = (int *)*item - 2; |
| |
| /* magic cookie still there? */ |
| xmalloc_assert(p[0] == XMALLOC_MAGIC); |
| old_size = p[1]; |
| |
| MALLOC_LOCK(); |
| p = (int *)realloc(p, newsize + 2*sizeof(int)); |
| MALLOC_UNLOCK(); |
| |
| if (p == NULL) |
| goto error; |
| |
| if (old_size < newsize) { |
| char *p_new = (char *)(&p[2]) + old_size; |
| memset(p_new, 0, (int)(newsize-old_size)); |
| } |
| xmalloc_assert(p[0] == XMALLOC_MAGIC); |
| |
| } else { |
| /* Initalize new memory */ |
| MALLOC_LOCK(); |
| p = (int *)malloc(newsize + 2*sizeof(int)); |
| MALLOC_UNLOCK(); |
| |
| if (p == NULL) |
| goto error; |
| |
| memset(&p[2], 0, newsize); |
| p[0] = XMALLOC_MAGIC; |
| } |
| |
| p[1] = (int)newsize; |
| *item = &p[2]; |
| return *item; |
| |
| error: |
| fprintf(log_fp(), "%s:%d: %s: xrealloc(%d) failed\n", |
| file, line, func, (int)newsize); |
| abort(); |
| } |
| |
| /* |
| * same as above, but return <= 0 on malloc() failure instead of aborting. |
| * `*item' will be unchanged. |
| */ |
| int slurm_try_xrealloc(void **item, size_t newsize, |
| const char *file, int line, const char *func) |
| { |
| int *p = NULL; |
| |
| /* xmalloc_assert(*item != NULL, file, line, func); */ |
| xmalloc_assert(newsize >= 0 && (int)newsize <= INT_MAX); |
| |
| if (*item != NULL) { |
| int old_size; |
| p = (int *)*item - 2; |
| |
| /* magic cookie still there? */ |
| xmalloc_assert(p[0] == XMALLOC_MAGIC); |
| old_size = p[1]; |
| |
| MALLOC_LOCK(); |
| p = (int *)realloc(p, newsize + 2*sizeof(int)); |
| MALLOC_UNLOCK(); |
| |
| if (p == NULL) |
| return 0; |
| |
| if (old_size < newsize) { |
| char *p_new = (char *)(&p[2]) + old_size; |
| memset(p_new, 0, (int)(newsize-old_size)); |
| } |
| xmalloc_assert(p[0] == XMALLOC_MAGIC); |
| |
| } else { |
| /* Initalize new memory */ |
| MALLOC_LOCK(); |
| p = (int *)malloc(newsize + 2*sizeof(int)); |
| MALLOC_UNLOCK(); |
| |
| if (p == NULL) |
| return 0; |
| |
| memset(&p[2], 0, newsize); |
| p[0] = XMALLOC_MAGIC; |
| } |
| |
| p[1] = newsize; |
| *item = &p[2]; |
| return 1; |
| } |
| |
| |
| /* |
| * Return the size of a buffer. |
| * item (IN) pointer to allocated space |
| */ |
| int slurm_xsize(void *item, const char *file, int line, const char *func) |
| { |
| int *p = (int *)item - 2; |
| xmalloc_assert(item != NULL); |
| xmalloc_assert(p[0] == XMALLOC_MAGIC); |
| return p[1]; |
| } |
| |
| /* |
| * Free which takes a pointer to object to free, which it turns into a null |
| * object. |
| * item (IN/OUT) double-pointer to allocated space |
| */ |
| void slurm_xfree(void **item, const char *file, int line, const char *func) |
| { |
| if (*item != NULL) { |
| int *p = (int *)*item - 2; |
| /* magic cookie still there? */ |
| xmalloc_assert(p[0] == XMALLOC_MAGIC); |
| p[0] = 0; /* make sure xfree isn't called twice */ |
| MALLOC_LOCK(); |
| free(p); |
| MALLOC_UNLOCK(); |
| *item = NULL; |
| } |
| } |
| |
| #ifndef NDEBUG |
| static void malloc_assert_failed(char *expr, const char *file, |
| int line, const char *caller, const char *func) |
| { |
| error("%s() Error: from %s:%d: %s(): Assertion (%s) failed", |
| func, file, line, caller, expr); |
| abort(); |
| } |
| #endif |