📄 dmalloc.c
字号:
/* * program that handles the dmalloc variables. * * Copyright 2000 by Gray Watson * * This file is part of the dmalloc package. * * Permission to use, copy, modify, and distribute this software for * any purpose and without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies, and that the name of Gray Watson not be used in advertising * or publicity pertaining to distribution of the document or software * without specific, written prior permission. * * Gray Watson makes no representations about the suitability of the * software described herein for any purpose. It is provided "as is" * without express or implied warranty. * * The author may be contacted via http://dmalloc.com/ * * $Id: dmalloc.c,v 1.114 2004/07/10 03:49:52 gray Exp $ *//* * This is the dmalloc program which is designed to enable the user to * easily set the environmental variables that control the dmalloc * library capabilities. *//* * NOTE: all standard-output from this program is designed to be run * through a shell evaluation command by default. Any messages for * the user should therefore be send to stderr. */#include <stdio.h> /* for stderr */#define DMALLOC_DISABLE#include "dmalloc_argv.h" /* for argument processing */#if HAVE_STRING_H# include <string.h>#endif#if HAVE_STDLIB_H# include <stdlib.h>#endif#include "conf.h"#include "dmalloc.h"#include "compat.h"#include "debug_tok.h"#include "debug_tok.h"#include "env.h"#include "error_val.h"#include "dmalloc_loc.h"#include "version.h"#define HOME_ENVIRON "HOME" /* home directory */#define SHELL_ENVIRON "SHELL" /* for the type of shell */#define DEFAULT_CONFIG ".dmallocrc" /* default config file */#define TOKENIZE_EQUALS " \t=" /* for tag lines */#define TOKENIZE_CHARS " \t," /* for tag lines */#define DEBUG_ARG 'd' /* debug argument */#define INTERVAL_ARG 'i' /* interval argument */#define THREAD_LOCK_ON_ARG 'o' /* lock-on argument */#define LIMIT_ARG 'M' /* memory-limit argument */#define LINE_WIDTH 75 /* num debug toks per line */#define FILE_NOT_FOUND 1#define FILE_FOUND 2#define TOKEN_FOUND 3/* * default flag information */typedef struct { char *de_string; /* default name */ long de_flags; /* default settings */} default_t;#define RUNTIME_FLAGS (DEBUG_LOG_STATS | DEBUG_LOG_NONFREE | \ DEBUG_LOG_BAD_SPACE | \ DEBUG_CHECK_FENCE | \ DEBUG_CATCH_NULL)#define LOW_FLAGS (RUNTIME_FLAGS | \ DEBUG_LOG_ELAPSED_TIME | \ DEBUG_CHECK_SHUTDOWN | \ DEBUG_FREE_BLANK | DEBUG_ERROR_ABORT | \ DEBUG_ALLOC_BLANK)#define MEDIUM_FLAGS (LOW_FLAGS | \ DEBUG_CHECK_HEAP | \ DEBUG_REALLOC_COPY)#define HIGH_FLAGS (MEDIUM_FLAGS | \ DEBUG_CHECK_BLANK | DEBUG_CHECK_FUNCS)#define ALL_FLAGS (HIGH_FLAGS | \ DEBUG_LOG_TRANS | DEBUG_LOG_ADMIN | \ DEBUG_NEVER_REUSE)/* NOTE: print-messages is not in this list because it is special */static default_t defaults[] = { { "none", 0 }, { "runtime", RUNTIME_FLAGS }, { "run", RUNTIME_FLAGS }, { "low", LOW_FLAGS }, { "med", MEDIUM_FLAGS }, { "medium", MEDIUM_FLAGS }, { "high", HIGH_FLAGS }, { "all", ALL_FLAGS }, { NULL }};/* argument variables */static int bourne_b = 0; /* set bourne shell output */static int cshell_b = 0; /* set c-shell output */static int gdb_b = 0; /* set gdb output */static int rcshell_b = 0; /* set rc shell output */static char *address = NULL; /* for ADDRESS */static int clear_b = 0; /* clear variables */static int debug = 0; /* for DEBUG */static int errno_to_print = 0; /* to print the error string */static int help_b = 0; /* print help message */static char *inpath = NULL; /* for config-file path */static unsigned long interval = 0; /* for setting INTERVAL */static int thread_lock_on = 0; /* for setting LOCK_ON */static int keep_b = 0; /* keep settings override -r */static int list_tags_b = 0; /* list rc tags */static int debug_tokens_b = 0; /* list debug tokens */static char *logpath = NULL; /* for LOGFILE setting */static int long_tokens_b = 0; /* long-tok output */static argv_array_t minus; /* tokens to remove */static unsigned long limit_arg = 0; /* memory limit */static int make_changes_b = 1; /* make no changes to env */static argv_array_t plus; /* tokens to add */static int remove_auto_b = 0; /* auto-remove settings */static char *start_file = NULL; /* for START settings */static unsigned long start_iter = 0; /* for START settings */static unsigned long start_size = 0; /* for START settings */static int usage_b = 0; /* usage messages */static int verbose_b = 0; /* verbose flag */static int very_verbose_b = 0; /* very-verbose flag */static char *tag = NULL; /* maybe a tag argument */static argv_t args[] = { { 'b', "bourne-shell", ARGV_BOOL_INT, &bourne_b, NULL, "set output for bourne shells" }, { ARGV_OR }, { 'C', "c-shell", ARGV_BOOL_INT, &cshell_b, NULL, "set output for C-type shells" }, { ARGV_OR }, { 'g', "gdb", ARGV_BOOL_INT, &gdb_b, NULL, "set output for gdb parsing" }, { ARGV_OR }, { 'R', "rc-shell", ARGV_BOOL_INT, &rcshell_b, NULL, "set output for rc shell" }, { 'L', "long-tokens", ARGV_BOOL_INT, &long_tokens_b, NULL, "output long-tokens not 0x..." }, { 'a', "address", ARGV_CHAR_P, &address, "address:#", "stop when malloc sees address" }, { 'c', "clear", ARGV_BOOL_INT, &clear_b, NULL, "clear all variables not set" }, { DEBUG_ARG, "debug-mask", ARGV_HEX, &debug, "value", "hex flag to set debug mask" }, { 'D', "debug-tokens", ARGV_BOOL_INT, &debug_tokens_b, NULL, "list debug tokens" }, { 'e', "errno", ARGV_INT, &errno_to_print, "errno", "print error string for errno" }, { 'f', "file", ARGV_CHAR_P, &inpath, "path", "config if not $HOME/.dmallocrc" }, { 'h', "help", ARGV_BOOL_INT, &help_b, NULL, "print help message" }, { INTERVAL_ARG, "interval", ARGV_U_LONG, &interval, "value", "check heap every number times" }, { 'k', "keep", ARGV_BOOL_INT, &keep_b, NULL, "keep settings (override -r)" }, { 'l', "logfile", ARGV_CHAR_P, &logpath, "path", "file to log messages to" }, { 'm', "minus", ARGV_CHAR_P | ARGV_FLAG_ARRAY, &minus, "token(s)", "del tokens from current debug" }, { LIMIT_ARG, "memory-limit", ARGV_U_SIZE, &limit_arg, "value", "limit allocations to this amount" }, { 'n', "no-changes", ARGV_BOOL_NEG, &make_changes_b, NULL, "make no changes to the env" }, { THREAD_LOCK_ON_ARG, "lock-on", ARGV_INT, &thread_lock_on, "number", "number of times to not lock" }, { 'p', "plus", ARGV_CHAR_P | ARGV_FLAG_ARRAY, &plus, "token(s)", "add tokens to current debug" }, { 'r', "remove", ARGV_BOOL_INT, &remove_auto_b, NULL, "remove other settings if tag" }, { 's', "start-file", ARGV_CHAR_P, &start_file, "file:line", "check heap after this location" }, { ARGV_OR }, { 'S', "start-iter", ARGV_U_LONG, &start_iter, "number", "check heap after this iteration" }, { ARGV_OR }, { '\0', "start-size", ARGV_U_SIZE, &start_size, "size", "check heap after this mem size" }, { 't', "list-tags", ARGV_BOOL_INT, &list_tags_b, NULL, "list tags in rc file" }, { 'u', "usage", ARGV_BOOL_INT, &usage_b, NULL, "print usage messages" }, { 'v', "verbose", ARGV_BOOL_INT, &verbose_b, NULL, "turn on verbose output" }, { 'V', "very-verbose", ARGV_BOOL_INT, &very_verbose_b, NULL, "turn on very-verbose output" }, { ARGV_MAYBE, NULL, ARGV_CHAR_P, &tag, "tag", "debug token to find in rc" }, { ARGV_LAST }};/* * list of bourne shells */static char *sh_shells[] = { "sh", "ash", "bash", "ksh", "zsh", NULL };/* * try a check out the shell env variable to see what form of shell * commands we should output */static void choose_shell(void){ const char *shell, *shell_p; int shell_c; shell = getenv(SHELL_ENVIRON); if (shell == NULL) { /* oh well, we just guess on c-shell */ cshell_b = 1; return; } shell_p = strrchr(shell, '/'); if (shell_p == NULL) { shell_p = shell; } else { shell_p++; } /* look for the rc shell specifically */ if (strcmp("rc", shell_p) == 0) { rcshell_b = 1; return; } /* look for the shells which are bourne-shell compatible */ for (shell_c = 0; sh_shells[shell_c] != NULL; shell_c++) { if (strcmp(sh_shells[shell_c], shell_p) == 0) { bourne_b = 1; return; } } /* otherwise set to c-shell */ cshell_b = 1;}/* * dump the current flags set in the debug variable VAL */static void dump_debug(const unsigned long val){ attr_t *attr_p; char *str; unsigned long work = val; int col_c = 0, len; for (attr_p = attributes; attr_p->at_string != NULL; attr_p++) { /* the below test for work == 0 is necessary to handle the 'none' token */ if ((work == 0 && attr_p->at_value == 0) || (attr_p->at_value != 0 && BIT_IS_SET(work, attr_p->at_value))) { BIT_CLEAR(work, attr_p->at_value); if (col_c == 0) { (void)fprintf(stderr, " "); col_c += 3; } if (very_verbose_b) { (void)fprintf(stderr, "%s -- %s (%#lx)\n", attr_p->at_string, attr_p->at_desc, attr_p->at_value); col_c = 0; } else { str = attr_p->at_string; len = strlen(str); if (col_c + len + 2 > LINE_WIDTH) { (void)fprintf(stderr, "\n"); (void)fprintf(stderr, " "); col_c = 3; } (void)fprintf(stderr, "%s", str); col_c += len; /* if we've got more to go then print the , */ if (work != 0) { (void)fprintf(stderr, ", "); col_c += 2; } } if (work == 0) { break; } } } if (col_c != 0) { (void)fprintf(stderr, "\n"); } if (work != 0) { (void)fprintf(stderr, "%s: warning, unknown debug flag(s): %#lx\n", argv_program, work); }}/* * translate TOK into its proper value which is returned */static long token_to_value(const char *tok){ attr_t *attr_p; /* find the matching attribute string */ for (attr_p = attributes; attr_p->at_string != NULL; attr_p++) { if (strcmp(tok, attr_p->at_string) == 0) { break; } } if (attr_p->at_string == NULL) { (void)fprintf(stderr, "%s: unknown token '%s'\n", argv_program, tok); return 0; } /* if we have a 0 value and not none then this is a disabled token */ if (attr_p->at_value == 0 && strcmp(tok, "none") != 0) { (void)fprintf(stderr, "%s: token '%s' has been disabled: %s\n", argv_program, tok, attr_p->at_desc); return 0; } return attr_p->at_value;}/* * Read in the next token from INFILE. It passes back the returned * debug value in DEBUG_P. Passes back the matching TOKEN of * TOKEN_SIZE. Returns 1 if there was a next else 0. */static int read_next_token(FILE *infile, long *debug_p, char *token, const int token_size){ int cont_b = 0, found_b = 0; long new_debug = 0; char buf[1024], *tok_p, *buf_p; while (fgets(buf, sizeof(buf), infile) != NULL) { /* ignore comments and empty lines */ if (buf[0] == '#' || buf[0] == '\n') { continue; } /* chop off the ending \n */ buf_p = strrchr(buf, '\n'); SET_POINTER(buf_p, '\0'); buf_p = buf; /* if we're not continuing then we need to process a tag */ if (! cont_b) { /* get the first token on the line */ tok_p = strsep(&buf_p, TOKENIZE_EQUALS); if (tok_p == NULL) { continue; } if (*tok_p == '\0') { (void)fprintf(stderr, "Invalid start of line: %s\n", buf_p); continue; } /* save the token */ if (token != NULL) { (void)strncpy(token, tok_p, token_size); token[token_size - 1] = '\0'; } found_b = 1; } cont_b = 0; while (1) { /* get the next token */ tok_p = strsep(&buf_p, TOKENIZE_CHARS); if (tok_p == NULL) { break; } if (*tok_p == '\0') { continue; } /* do we have a continuation character? */ if (strcmp(tok_p, "\\") == 0) { cont_b = 1; break; } new_debug |= token_to_value(tok_p); } /* are we done? */ if (! cont_b) { break; } } SET_POINTER(debug_p, new_debug); if (found_b) { return 1; } else { return 0; }}/* * Read in a rc file from PATH and process it looking for the * specified DEBUG_VALUE or TAG_FIND token. It passes back the * returned debug value in DEBUG_P. Passes back the matching TOKEN of * TOKEN_SIZE. * * Returns FILE_NOT_FOUND, FILE_FOUND, or TOKEN_FOUND. */static int read_rc_file(const char *path, const long debug_value, const char *tag_find, long *debug_p, char *token, const int token_size){ FILE *infile; int found_b = 0; char next_token[64]; long new_debug; /* open the path */ infile = fopen(path, "r"); if (infile == NULL) { return FILE_NOT_FOUND; } /* run through the tokens, looking for a match */ while (read_next_token(infile, &new_debug, next_token, sizeof(next_token)) == 1) { /* are we looking for a tag? */ if (tag_find != NULL && strcmp(tag_find, next_token) == 0) { found_b = 1; break; } /* are we looking for a debug-value? */ if (debug_value > 0 && debug_value == new_debug) { found_b = 1; break; } } (void)fclose(infile); SET_POINTER(debug_p, new_debug); if (token != NULL) { (void)loc_snprintf(token, token_size, "config file token: %s", next_token); } if (found_b) { return TOKEN_FOUND; } else { return FILE_FOUND; }}/* * Process the user configuration looking for the TAG_FIND. If it is * null then look for DEBUG_VALUE in the file and copy the token found * into TOKEN of TOKEN_SIZE. Routine returns the new debug value * matching tag. */static long find_tag(const long debug_value, const char *tag_find, char *token, const int token_size){ char path[1024], *path_p; default_t *def_p; const char *home_p; int ret; long new_debug = 0; /* do we need to have a home variable? */ if (inpath == NULL) { /* first we try to read the RC file from the current directory */ ret = read_rc_file(DEFAULT_CONFIG, debug_value, tag_find, &new_debug, token, token_size); /* did we find the correct value in the file? */ if (ret == TOKEN_FOUND) { return new_debug; } /* if we did not find the file, check the home directory */ if (ret == FILE_FOUND) { path_p = DEFAULT_CONFIG; } else { /* find our home directory */ home_p = getenv(HOME_ENVIRON); if (home_p == NULL) { (void)fprintf(stderr, "%s: could not find variable '%s'\n", argv_program, HOME_ENVIRON); exit(1); } (void)loc_snprintf(path, sizeof(path), "%s/%s", home_p, DEFAULT_CONFIG); /* read in the file from our home directory */ ret = read_rc_file(path, debug_value, tag_find, &new_debug, token, token_size); /* did we find the correct value in the file? */ if (ret == TOKEN_FOUND) { return new_debug; } if (ret == FILE_FOUND) { path_p = path;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -