#include "std.h" #include "lpc_incl.h" #include "file.h" #include "md.h" #ifdef DEBUGMALLOC_EXTENSIONS #include "comm.h" #include "lex.h" #include "simul_efun.h" #include "call_out.h" #include "mapping.h" #if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL) #include "socket_efuns.h" #endif #endif #include "master.h" #ifdef PACKAGE_PARSER #include "packages/parser.h" #endif /* note: do not use MALLOC() etc. in this module. Unbridled recursion will occur. (use malloc() etc. instead) This module introduces quite a lot of overhead but it can be useful for tracking down memory leaks or for catching the freeing on non-malloc'd data. This module could easily be extended to allow the malloced memory chunks to be tagged with a string label. */ #ifdef DEBUGMALLOC #define LEFT_MAGIC(node) ((node)->magic) #define RIGHT_MAGIC_ADDR(node) ((unsigned char *)(node) + sizeof(md_node_t) + (node)->size) #define STORE_RIGHT_MAGIC(node) \ *(RIGHT_MAGIC_ADDR(node)) = (char)(MD_MAGIC >> 24) & 0xff; \ *(RIGHT_MAGIC_ADDR(node)+1) = (char)(MD_MAGIC >> 16) & 0xff; \ *(RIGHT_MAGIC_ADDR(node)+2) = (char)(MD_MAGIC >> 8) & 0xff; \ *(RIGHT_MAGIC_ADDR(node)+3) = (char)MD_MAGIC & 0xff #define FETCH_RIGHT_MAGIC(l, node) \ l = (*(RIGHT_MAGIC_ADDR(node)) << 24) + \ (*(RIGHT_MAGIC_ADDR(node) + 1) << 16) + \ (*(RIGHT_MAGIC_ADDR(node) + 2) << 8) + \ (*(RIGHT_MAGIC_ADDR(node) + 3)) int totals[MAX_CATEGORY]; int blocks[MAX_CATEGORY]; static char *sources[] = { "*", "temporary blocks", "permanent blocks", "compiler blocks", "data blocks", "miscellaneous blocks", "<#6>", "<#7>", "<#8>", "<#9>", "<#10>", "program blocks", "call_out blocks", "interactives", "ed blocks", "<#15>", "include list", "permanent identifiers", "identifier hash table", "reserved block", "mudlib stats", "objects", "object table", "config table", "simul_efuns", "sentences", "string table", "free swap blocks", "uids", "object names", "predefines", "line numbers", "compiler local blocks", "compiled program", "users", "debugmalloc overhead", "heart_beat list", "parser", "input_to", "sockets", "strings", "malloc strings", "shared strings", "function pointers", "arrays", "mappings", "mapping nodes", "mapping tables", "buffers", "classes" }; int malloc_mask = 121; static md_node_t **table; unsigned int total_malloced = 0L; unsigned int hiwater = 0L; #ifdef DEBUGMALLOC_EXTENSIONS void check_all_blocks (int); outbuffer_t out; void MDmemcheck(); void MDmemcheck() { check_all_blocks(2); if (out.buffer) fatal("Internal memory check failed: %s\n", out.buffer); } #endif void MDinit() { int j; table = (md_node_t **) calloc(MD_TABLE_SIZE, sizeof(md_node_t *)); for (j = 0; j < MAX_CATEGORY; j++) { totals[j] = 0; } } void MDmalloc (md_node_t * node, int size, int tag, char * desc) { unsigned long h; static int count = 0; if (!size) { debug_message("md: debugmalloc: attempted to allocate zero bytes\n"); #ifdef DEBUG abort(); #endif return; } total_malloced += size; if (total_malloced > hiwater) { hiwater = total_malloced; } h = MD_HASH(node); node->size = size; node->next = table[h]; #ifdef CHECK_MEMORY LEFT_MAGIC(node) = MD_MAGIC; STORE_RIGHT_MAGIC(node); #endif #ifdef DEBUGMALLOC_EXTENSIONS if ((tag & 0xff) < MAX_CATEGORY) { totals[tag & 0xff] += size; blocks[tag & 0xff]++; } if (((tag >> 8) & 0xff) < MAX_CATEGORY) { totals[(tag >> 8) & 0xff] += size; blocks[(tag >> 8) & 0xff]++; } node->tag = tag; node->id = count++; node->desc = desc ? desc : "default"; if (malloc_mask == node->tag) { debug_message("MDmalloc: %5d, [%-25s], %8lx:(%d)\n", node->tag, node->desc, PTR(node), node->size); } #endif table[h] = node; } #ifdef DEBUGMALLOC_EXTENSIONS void set_tag (const void * ptr, int tag) { md_node_t *node = PTR_TO_NODET(ptr); if ((node->tag & 0xff) < MAX_CATEGORY) { totals[node->tag & 0xff] -= node->size; blocks[node->tag & 0xff]--; } if (((node->tag >> 8) & 0xff) < MAX_CATEGORY) { totals[(node->tag >> 8) & 0xff] -= node->size; blocks[(node->tag >> 8) & 0xff]--; } node->tag = tag; if ((node->tag & 0xff) < MAX_CATEGORY) { totals[node->tag & 0xff] += node->size; blocks[node->tag & 0xff]++; } if (((node->tag >> 8) & 0xff) < MAX_CATEGORY) { totals[(node->tag >> 8) & 0xff] += node->size; blocks[(node->tag >> 8) & 0xff]++; } } #endif int MDfree (void * ptr) { unsigned long h; int tmp; md_node_t *entry, **oentry; h = MD_HASH(ptr); oentry = &table[h]; for (entry = *oentry; entry; oentry = &entry->next, entry = *oentry) { if (entry == ptr) { *oentry = entry->next; total_malloced -= entry->size; break; } } if (entry) { #ifdef CHECK_MEMORY if (LEFT_MAGIC(entry) != MD_MAGIC) { debug_message("MDfree: left side of entry corrupt: %s %04x at %lx\n", entry->desc, entry->tag, entry); } FETCH_RIGHT_MAGIC(tmp, entry); if (tmp != MD_MAGIC) { debug_message("MDfree: right side of entry corrupt: %s %04x at %lx\n", entry->desc, entry->tag, entry); } #endif #ifdef DEBUGMALLOC_EXTENSIONS if ((entry->tag & 0xff) < MAX_CATEGORY) { totals[entry->tag & 0xff] -= entry->size; blocks[entry->tag & 0xff]--; } if (((entry->tag >> 8) & 0xff) < MAX_CATEGORY) { totals[(entry->tag >> 8) & 0xff] -= entry->size; blocks[(entry->tag >> 8) & 0xff]--; } if (malloc_mask == entry->tag) { debug_message("MDfree: %5d, [%-25s], %8lx:(%d)\n", entry->tag, entry->desc, (unsigned long) PTR(entry), entry->size); } #endif } else { debug_message("md: debugmalloc: attempted to free non-malloc'd pointer %08lx\n", (unsigned long) ptr); #ifdef DEBUG abort(); #endif return 0; } return 1; } #ifdef DEBUGMALLOC_EXTENSIONS char *dump_debugmalloc (char * tfn, int mask) { int j, total = 0, chunks = 0, total2 = 0; const char *fn; md_node_t *entry; FILE *fp; outbuf_zero(&out); fn = check_valid_path(tfn, current_object, "debugmalloc", 1); if (!fn) { error("Invalid path '%s' for writing.\n", tfn); } fp = fopen(fn, "w"); if (!fp) { error("Unable to open %s for writing.\n", fn); } for (j = 0; j < MD_TABLE_SIZE; j++) { for (entry = table[j]; entry; entry = entry->next) { if (!mask || (entry->tag == mask)) { fprintf(fp, "%-30s: sz %7d: id %6d: tag %08x, a %8lx\n", entry->desc, entry->size, entry->id, entry->tag, (unsigned long) PTR(entry)); total += entry->size; chunks++; } } } fprintf(fp, "total = %8d\n", total); fprintf(fp, "# chunks = %8d\n", chunks); fprintf(fp, "ave. bytes per chunk = %7.2f\n\n", (float) total / chunks); fprintf(fp, "categories:\n\n"); for (j = 0; j < MAX_CATEGORY; j++) { fprintf(fp, "%4d: %10d\n", j, totals[j]); total2 += totals[j]; } fprintf(fp, "\ntotal = %11d\n", total2); fclose(fp); outbuf_addv(&out, "total = %8d\n", total); outbuf_addv(&out, "# chunks = %8d\n", chunks); if (chunks) { outbuf_addv(&out, "ave. bytes per chunk = %7.2f\n", (float) total / chunks); } outbuf_fix(&out); return out.buffer; } #endif /* DEBUGMALLOC_EXTENSIONS */ void set_malloc_mask (int mask) { malloc_mask = mask; } #ifdef DEBUGMALLOC_EXTENSIONS static void mark_object (object_t * ob) { #ifndef NO_ADD_ACTION sentence_t *sent; #endif int i; if (ob->prog) ob->prog->extra_ref++; if (ob->obname) { DO_MARK(ob->obname, TAG_OBJ_NAME); } if (ob->replaced_program) EXTRA_REF(BLOCK(ob->replaced_program))++; #ifdef PRIVS if (ob->privs) EXTRA_REF(BLOCK(ob->privs))++; #endif #ifndef NO_ADD_ACTION if (ob->living_name) EXTRA_REF(BLOCK(ob->living_name))++; sent = ob->sent; while (sent) { DO_MARK(sent, TAG_SENTENCE); if (sent->flags & V_FUNCTION) sent->function.f->hdr.extra_ref++; else { if (sent->function.s) EXTRA_REF(BLOCK(sent->function.s))++; } if (sent->verb) EXTRA_REF(BLOCK(sent->verb))++; sent = sent->next; } #endif #ifdef PACKAGE_PARSER if (ob->pinfo) parser_mark(ob->pinfo); #endif if (ob->prog) for (i = 0; i < ob->prog->num_variables_total; i++) mark_svalue(&ob->variables[i]); else outbuf_addv(&out, "can't mark variables; %s is swapped.\n", ob->obname); } void mark_svalue (svalue_t * sv) { switch (sv->type) { case T_OBJECT: sv->u.ob->extra_ref++; break; case T_ARRAY: sv->u.arr->extra_ref++; break; case T_CLASS: sv->u.arr->extra_ref++; break; case T_MAPPING: sv->u.map->extra_ref++; break; case T_FUNCTION: sv->u.fp->hdr.extra_ref++; break; #ifndef NO_BUFFER_TYPE case T_BUFFER: sv->u.buf->extra_ref++; break; #endif case T_STRING: switch (sv->subtype) { case STRING_MALLOC: MSTR_EXTRA_REF(sv->u.string)++; break; case STRING_SHARED: EXTRA_REF(BLOCK(sv->u.string))++; break; } } } static void mark_funp (funptr_t* fp) { if (fp->hdr.args) fp->hdr.args->extra_ref++; if (fp->hdr.owner) fp->hdr.owner->extra_ref++; switch (fp->hdr.type) { case FP_LOCAL | FP_NOT_BINDABLE: if (fp->hdr.owner) fp->hdr.owner->prog->extra_func_ref++; break; case FP_FUNCTIONAL: case FP_FUNCTIONAL | FP_NOT_BINDABLE: fp->f.functional.prog->extra_func_ref++; break; } } static void mark_sentence (sentence_t * sent) { if (sent->flags & V_FUNCTION) { if (sent->function.f) sent->function.f->hdr.extra_ref++; } else { if (sent->function.s) EXTRA_REF(BLOCK(sent->function.s))++; } #ifndef NO_ADD_ACTION if (sent->verb) EXTRA_REF(BLOCK(sent->verb))++; #endif } static int print_depth = 0; static void md_print_array (array_t * vec) { int i; outbuf_add(&out, "({ "); for (i=0; i < vec->size; i++) { switch (vec->item[i].type) { case T_INVALID: outbuf_add(&out, "INVALID"); break; case T_NUMBER: outbuf_addv(&out, "%ld", vec->item[i].u.number); break; case T_REAL: outbuf_addv(&out, "%f", vec->item[i].u.real); break; case T_STRING: outbuf_addv(&out, "\"%s\"", vec->item[i].u.string); break; case T_ARRAY: if (print_depth < 2) { print_depth++; md_print_array(vec->item[i].u.arr); } else { outbuf_add(&out, "({ ... })"); } break; case T_CLASS: outbuf_add(&out, ""); break; #ifndef NO_BUFFER_TYPE case T_BUFFER: outbuf_add(&out, ""); break; #endif case T_FUNCTION: outbuf_add(&out, ""); break; case T_MAPPING: outbuf_add(&out, ""); break; case T_OBJECT: outbuf_addv(&out, "OBJ(%s)", vec->item[i].u.ob->obname); break; } if (i != vec->size - 1) outbuf_add(&out, ", "); } outbuf_add(&out, " })\n"); print_depth--; } static void mark_config (void) { int i; for (i = 0; i < NUM_CONFIG_STRS; i++) { DO_MARK(config_str[i], TAG_STRING); } } #if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL) void mark_sockets (void) { int i; char *s; for (i = 0; i < max_lpc_socks; i++) { if (lpc_socks[i].flags & S_READ_FP) { lpc_socks[i].read_callback.f->hdr.extra_ref++; } else if ((s = lpc_socks[i].read_callback.s)) { EXTRA_REF(BLOCK(s))++; } if (lpc_socks[i].flags & S_WRITE_FP) { lpc_socks[i].write_callback.f->hdr.extra_ref++; } else if ((s = lpc_socks[i].write_callback.s)) { EXTRA_REF(BLOCK(s))++; } if (lpc_socks[i].flags & S_CLOSE_FP) { lpc_socks[i].close_callback.f->hdr.extra_ref++; } else if ((s = lpc_socks[i].close_callback.s)) { EXTRA_REF(BLOCK(s))++; } } } #endif #ifdef STRING_STATS static int base_overhead = 0; /* Compute the correct values of allocd_strings, allocd_bytes, and * bytes_distinct_strings based on blocks that are actually allocated. */ void compute_string_totals (int * asp, int * abp, int * bp) { int hsh; md_node_t *entry; malloc_block_t *msbl; block_t *ssbl; *asp = 0; *abp = 0; *bp = 0; for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) { for (entry = table[hsh]; entry; entry = entry->next) { if (entry->tag == TAG_MALLOC_STRING) { msbl = NODET_TO_PTR(entry, malloc_block_t*); *bp += msbl->size + 1; *asp += msbl->ref; *abp += msbl->ref * (msbl->size + 1); } if (entry->tag == TAG_SHARED_STRING) { ssbl = NODET_TO_PTR(entry, block_t*); *bp += ssbl->size + 1; *asp += ssbl->refs; *abp += ssbl->refs * (ssbl->size + 1); } } } } /* * Verify string statistics. out can be zero, in which case any errors * are printed to stdout and abort() is called. Otherwise the error messages * are added to the outbuffer. */ void check_string_stats (outbuffer_t * out) { int overhead = blocks[TAG_SHARED_STRING & 0xff] * sizeof(block_t) + blocks[TAG_MALLOC_STRING & 0xff] * sizeof(malloc_block_t); int num = blocks[TAG_SHARED_STRING & 0xff] + blocks[TAG_MALLOC_STRING & 0xff]; int bytes, as, ab; compute_string_totals(&as, &ab, &bytes); if (!base_overhead) base_overhead = overhead_bytes - overhead; overhead += base_overhead; if (num != num_distinct_strings) { if (out) { outbuf_addv(out, "WARNING: num_distinct_strings is: %i should be: %i\n", num_distinct_strings, num); } else { printf("WARNING: num_distinct_strings is: %i should be: %i\n", num_distinct_strings, num); abort(); } } if (overhead != overhead_bytes) { if (out) { outbuf_addv(out, "WARNING: overhead_bytes is: %i should be: %i\n", overhead_bytes, overhead); } else { printf("WARNING: overhead_bytes is: %i should be: %i\n", overhead_bytes, overhead); abort(); } } if (bytes != bytes_distinct_strings) { if (out) { outbuf_addv(out, "WARNING: bytes_distinct_strings is: %i should be: %i\n", bytes_distinct_strings, bytes - (overhead - base_overhead)); } else { printf("WARNING: bytes_distinct_strings is: %i should be: %i\n", bytes_distinct_strings, bytes - (overhead - base_overhead)); abort(); } } if (allocd_strings != as) { if (out) { outbuf_addv(out, "WARNING: allocd_strings is: %i should be: %i\n", allocd_strings, as); } else { printf("WARNING: allocd_strings is: %i should be: %i\n", allocd_strings, as); abort(); } } if (allocd_bytes != ab) { if (out) { outbuf_addv(out, "WARNING: allocd_bytes is: %i should be: %i\n", allocd_bytes, ab); } else { printf("WARNING: allocd_bytes is: %i should be: %i\n", allocd_bytes, ab); abort(); } } } #endif /* currently: 1 - debug, 2 - suppress leak checks */ void check_all_blocks (int flag) { int i, j, hsh; int tmp; md_node_t *entry; object_t *ob; array_t *vec; mapping_t *map; #ifndef NO_BUFFER_TYPE buffer_t *buf; #endif funptr_t *fp; mapping_node_t *node; program_t *prog; sentence_t *sent; char *ptr; block_t *ssbl; malloc_block_t *msbl; extern svalue_t apply_ret_value; #if 0 int num = 0, total = 0; #endif outbuf_zero(&out); if (!(flag & 2)) outbuf_add(&out, "Performing memory tests ...\n"); for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) { for (entry = table[hsh]; entry; entry = entry->next) { entry->tag &= ~TAG_MARKED; #ifdef CHECK_MEMORY if (LEFT_MAGIC(entry) != MD_MAGIC) { outbuf_addv(&out, "WARNING: left side of entry corrupt: %s %08lx at %lx\n", entry->desc, entry->tag, entry); } FETCH_RIGHT_MAGIC(tmp, entry); if (tmp != MD_MAGIC) { outbuf_addv(&out, "WARNING: right side of entry corrupt: %s %08lx at %lx\n", entry->desc, entry->tag, entry); } #endif switch (entry->tag & 0xff00) { case TAG_TEMPORARY: if (!(flag & 2)) outbuf_addv(&out, "WARNING: Found temporary block: %s %04x\n", entry->desc, entry->tag); break; case TAG_COMPILER: if (!(flag & 2)) outbuf_addv(&out, "Found compiler block: %s %04x\n", entry->desc, entry->tag); break; case TAG_MISC: outbuf_addv(&out, "Found miscellaneous block: %s %04x\n", entry->desc, entry->tag); break; } switch (entry->tag) { case TAG_OBJECT: ob = NODET_TO_PTR(entry, object_t *); ob->extra_ref = 0; break; case TAG_PROGRAM: prog = NODET_TO_PTR(entry, program_t *); prog->extra_ref = 0; prog->extra_func_ref = 0; break; case TAG_MALLOC_STRING: { char *str; msbl = NODET_TO_PTR(entry, malloc_block_t *); /* don't give an error for the return value we are constructing :) */ if (msbl == MSTR_BLOCK(out.buffer)) break; str = (char *)(msbl + 1); msbl->extra_ref = 0; if (msbl->size != USHRT_MAX && msbl->size != strlen(str)) { outbuf_addv(&out, "Malloc'ed string length is incorrect: %s %04x '%s': is: %i should be: %i\n", entry->desc, entry->tag, str, msbl->size, strlen(str)); } break; } case TAG_SHARED_STRING: ssbl = NODET_TO_PTR(entry, block_t *); EXTRA_REF(ssbl) = 0; break; case TAG_ARRAY: vec = NODET_TO_PTR(entry, array_t *); vec->extra_ref = 0; break; case TAG_CLASS: vec = NODET_TO_PTR(entry, array_t *); vec->extra_ref = 0; break; case TAG_MAPPING: map = NODET_TO_PTR(entry, mapping_t *); map->extra_ref = 0; break; case TAG_FUNP: fp = NODET_TO_PTR(entry, funptr_t *); fp->hdr.extra_ref = 0; break; #ifndef NO_BUFFER_TYPE case TAG_BUFFER: buf = NODET_TO_PTR(entry, buffer_t *); buf->extra_ref = 0; break; #endif } } } if (!(flag & 2)) { /* the easy ones to find */ if (blocks[TAG_SIMULS & 0xff] > 3) outbuf_add(&out, "WARNING: more than three simul_efun tables allocated.\n"); if (blocks[TAG_INC_LIST & 0xff] > 1) outbuf_add(&out, "WARNING: more than one include list allocated.\n"); if (blocks[TAG_IDENT_TABLE & 0xff] > 1) outbuf_add(&out, "WARNING: more than one identifier hash table allocated.\n"); if (blocks[TAG_RESERVED & 0xff] > 1) outbuf_add(&out, "WARNING: more than one reserved block allocated.\n"); if (blocks[TAG_OBJ_TBL & 0xff] > 1) outbuf_add(&out, "WARNING: more than object table allocated.\n"); if (blocks[TAG_CONFIG & 0xff] > 1) outbuf_add(&out, "WARNING: more than config file table allocated.\n"); if (blocks[TAG_STR_TBL & 0xff] > 1) outbuf_add(&out, "WARNING: more than string table allocated.\n"); if (totals[TAG_CALL_OUT & 0xff] != print_call_out_usage(&out, -1)) outbuf_add(&out, "WARNING: wrong number of call_out blocks allocated.\n"); if (blocks[TAG_LOCALS & 0xff] > 3) outbuf_add(&out, "WARNING: more than 3 local blocks allocated.\n"); if (blocks[TAG_SENTENCE & 0xff] != tot_alloc_sentence) outbuf_addv(&out, "WARNING: tot_alloc_sentence is: %i should be: %i\n", tot_alloc_sentence, blocks[TAG_SENTENCE & 0xff]); if (blocks[TAG_OBJECT & 0xff] != tot_alloc_object) outbuf_addv(&out, "WARNING: tot_alloc_object is: %i should be: %i\n", tot_alloc_object, blocks[TAG_OBJECT & 0xff]); if (blocks[TAG_PROGRAM & 0xff] != total_num_prog_blocks) outbuf_addv(&out, "WARNING: total_num_prog_blocks is: %i should be: %i\n", total_num_prog_blocks, blocks[TAG_PROGRAM & 0xff]); #ifdef ARRAY_STATS if (blocks[TAG_ARRAY & 0xff] != num_arrays) outbuf_addv(&out, "WARNING: num_arrays is: %i should be: %i\n", num_arrays, blocks[TAG_ARRAY & 0xff]); if (totals[TAG_ARRAY & 0xff] != total_array_size) outbuf_addv(&out, "WARNING: total_array_size is: %i should be: %i\n", total_array_size, totals[TAG_ARRAY & 0xff]); #endif #ifdef CLASS_STATS if (blocks[TAG_CLASS & 0xff] != num_classes) outbuf_addv(&out, "WARNING: num_classes is: %i should be: %i\n", num_classes, blocks[TAG_CLASS & 0xff]); if (totals[TAG_CLASS & 0xff] != total_class_size) outbuf_addv(&out, "WARNING: total_class_size is: %i should be: %i\n", total_class_size, totals[TAG_CLASS & 0xff]); #endif if (blocks[TAG_MAPPING & 0xff] != num_mappings) outbuf_addv(&out, "WARNING: num_mappings is: %i should be: %i\n", num_mappings, blocks[TAG_MAPPING & 0xff]); if (blocks[TAG_MAP_TBL & 0xff] != num_mappings) outbuf_addv(&out, "WARNING: %i tables for %i mappings\n", blocks[TAG_MAP_TBL & 0xff], num_mappings); if (blocks[TAG_INTERACTIVE & 0xff] != num_user) outbuf_addv(&out, "WATNING: num_user is: %i should be: %i\n", num_user, blocks[TAG_INTERACTIVE & 0xff]); #ifdef STRING_STATS check_string_stats(&out); #endif #ifdef PACKAGE_EXTERNAL for (i = 0; i < NUM_EXTERNAL_CMDS; i++) { if (external_cmd[i]) { DO_MARK(external_cmd[i], TAG_STRING); } } #endif /* now do a mark and sweep check to see what should be alloc'd */ for (i = 0; i < max_users; i++) if (all_users[i]) { DO_MARK(all_users[i], TAG_INTERACTIVE); all_users[i]->ob->extra_ref++; if (all_users[i]->input_to) { all_users[i]->input_to->ob->extra_ref++; DO_MARK(all_users[i]->input_to, TAG_SENTENCE); mark_sentence(all_users[i]->input_to); if (all_users[i]->num_carry) { for (j = 0; j < all_users[i]->num_carry; j++) mark_svalue(all_users[i]->carryover + j); } } #ifdef PACKAGE_COMPRESS if(all_users[i]->compressed_stream) DO_MARK(all_users[i]->compressed_stream, TAG_INTERACTIVE); #endif #ifndef NO_ADD_ACTION if (all_users[i]->iflags & NOTIFY_FAIL_FUNC) all_users[i]->default_err_message.f->hdr.extra_ref++; else if (all_users[i]->default_err_message.s) EXTRA_REF(BLOCK(all_users[i]->default_err_message.s))++; #endif } if (*(DEFAULT_FAIL_MESSAGE)) { char buf[8192]; strcpy(buf, DEFAULT_FAIL_MESSAGE); strcat(buf, "\n"); EXTRA_REF(BLOCK(findstring(buf)))++; } #ifdef PACKAGE_UIDS mark_all_uid_nodes(); #endif #ifdef PACKAGE_MUDLIB_STATS mark_mudlib_stats(); #endif #if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL) mark_sockets(); #endif #ifdef PACKAGE_PARSER parser_mark_verbs(); #endif mark_file_sv(); mark_all_defines(); mark_free_sentences(); mark_iptable(); mark_stack(); mark_command_giver_stack(); mark_call_outs(); mark_simuls(); mark_apply_low_cache(); mark_mapping_node_blocks(); mark_config(); mark_svalue(&apply_ret_value); if (master_ob) master_ob->extra_ref++; if (simul_efun_ob) simul_efun_ob->extra_ref++; for (ob = obj_list; ob; ob = ob->next_all) { ob->extra_ref++; } /* objects on obj_list_destruct still have a ref too */ for (ob = obj_list_destruct; ob; ob = ob->next_all) { ob->extra_ref++; } for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) { for (entry = table[hsh]; entry; entry = entry->next) { switch (entry->tag & ~TAG_MARKED) { case TAG_IDENT_TABLE: { ident_hash_elem_t *hptr, *first; ident_hash_elem_t **table; int size; table = NODET_TO_PTR(entry, ident_hash_elem_t **); size = (entry->size / 3) / sizeof(ident_hash_elem_t *); for (i = 0; i < size; i++) { first = table[i]; if (first) { hptr = first; do { if (hptr->token & (IHE_SIMUL | IHE_EFUN)) { DO_MARK(hptr, TAG_PERM_IDENT); } hptr = hptr->next; } while (hptr != first); } } break; } case TAG_FUNP: fp = NODET_TO_PTR(entry, funptr_t *); mark_funp(fp); break; case TAG_ARRAY: vec = NODET_TO_PTR(entry, array_t *); if (entry->size != sizeof(array_t) + sizeof(svalue_t[1]) * (vec->size - 1)) outbuf_addv(&out, "array size doesn't match block size: %s %04x\n", entry->desc, entry->tag); for (i = 0; i < vec->size; i++) mark_svalue(&vec->item[i]); break; case TAG_CLASS: vec = NODET_TO_PTR(entry, array_t *); if (vec->size && entry->size != sizeof(array_t) + sizeof(svalue_t[1]) * (vec->size - 1)) outbuf_addv(&out, "class size doesn't match block size: %s %04x\n", entry->desc, entry->tag); for (i = 0; i < vec->size; i++) mark_svalue(&vec->item[i]); break; case TAG_MAPPING: map = NODET_TO_PTR(entry, mapping_t *); DO_MARK(map->table, TAG_MAP_TBL); i = map->table_size; do { for (node = map->table[i]; node; node = node->next) { mark_svalue(node->values); mark_svalue(node->values + 1); } } while (i--); break; case TAG_OBJECT: ob = NODET_TO_PTR(entry, object_t *); mark_object(ob); { object_t *tmp = obj_list; while (tmp && tmp != ob) tmp = tmp->next_all; if (!tmp) { tmp = obj_list_destruct; while (tmp && tmp != ob) tmp = tmp->next_all; } if (!tmp) { tmp = obj_list_dangling; while (tmp && tmp != ob) tmp = tmp->next_all; if (tmp) outbuf_addv(&out, "WARNING: %s is dangling.\n", ob->obname); } if (!tmp) outbuf_addv(&out, "WARNING: %s not in object list.\n", ob->obname); } break; case TAG_LPC_OBJECT: ob = NODET_TO_PTR(entry, object_t *); if (ob->obname) { DO_MARK(ob->obname, TAG_OBJ_NAME); } break; case TAG_PROGRAM: prog = NODET_TO_PTR(entry, program_t *); if (prog->line_info) { DO_MARK(prog->file_info, TAG_LINENUMBERS); } for (i = 0; i < prog->num_inherited; i++) prog->inherit[i].prog->extra_ref++; for (i = 0; i < prog->num_functions_defined; i++) if (prog->function_table[i].funcname) EXTRA_REF(BLOCK(prog->function_table[i].funcname))++; for (i = 0; i < prog->num_strings; i++) EXTRA_REF(BLOCK(prog->strings[i]))++; for (i = 0; i < prog->num_variables_defined; i++) EXTRA_REF(BLOCK(prog->variable_table[i]))++; EXTRA_REF(BLOCK(prog->filename))++; } } } /* now check */ for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) { for (entry = table[hsh]; entry; entry = entry->next) { switch (entry->tag) { case TAG_MUDLIB_STATS: outbuf_addv(&out, "WARNING: Found orphan mudlib stat block: %s %04x\n", entry->desc, entry->tag); break; case TAG_PROGRAM: prog = NODET_TO_PTR(entry, program_t *); if (prog->ref != prog->extra_ref) outbuf_addv(&out, "Bad ref count for program %s, is %d - should be %d\n", prog->filename, prog->ref, prog->extra_ref); if (prog->func_ref != prog->extra_func_ref) outbuf_addv(&out, "Bad function ref count for program %s, is %d - should be %d\n", prog->filename, prog->func_ref, prog->extra_func_ref); break; case TAG_OBJECT: ob = NODET_TO_PTR(entry, object_t *); if (ob->ref != ob->extra_ref) outbuf_addv(&out, "Bad ref count for object %s, is %d - should be %d\n", ob->obname, ob->ref, ob->extra_ref); break; case TAG_ARRAY: vec = NODET_TO_PTR(entry, array_t *); if (vec->ref != vec->extra_ref) { outbuf_addv(&out, "Bad ref count for array, is %d - should be %d\n", vec->ref, vec->extra_ref); print_depth = 0; md_print_array(vec); } break; case TAG_CLASS: vec = NODET_TO_PTR(entry, array_t *); if (vec->ref != vec->extra_ref) outbuf_addv(&out, "Bad ref count for class, is %d - should be %d\n", vec->ref, vec->extra_ref); break; case TAG_MAPPING: map = NODET_TO_PTR(entry, mapping_t *); if (map->ref != map->extra_ref) outbuf_addv(&out, "Bad ref count for mapping, is %d - should be %d\n", map->ref, map->extra_ref); break; case TAG_FUNP: fp = NODET_TO_PTR(entry, funptr_t *); if (fp->hdr.ref != fp->hdr.extra_ref) outbuf_addv(&out, "Bad ref count for function pointer (owned by %s), is %d - should be %d\n", (fp->hdr.owner ? fp->hdr.owner->obname : "(null)"), fp->hdr.ref, fp->hdr.extra_ref); break; #ifndef NO_BUFFER_TYPE case TAG_BUFFER: buf = NODET_TO_PTR(entry, buffer_t *); if (buf->ref != buf->extra_ref) outbuf_addv(&out, "Bad ref count for buffer, is %d - should be %d\n", buf->ref, buf->extra_ref); break; #endif case TAG_PREDEFINES: outbuf_addv(&out, "WARNING: Found orphan predefine: %s %04x\n", entry->desc, entry->tag); break; case TAG_LINENUMBERS: outbuf_addv(&out, "WARNING: Found orphan line number block: %s %04x\n", entry->desc, entry->tag); break; case TAG_OBJ_NAME: outbuf_addv(&out, "WARNING: Found orphan object name: %s %04x\n", entry->desc, entry->tag); break; case TAG_INTERACTIVE: outbuf_addv(&out, "WARNING: Found orphan interactive: %s %04x\n", entry->desc, entry->tag); break; case TAG_UID: outbuf_addv(&out, "WARNING: Found orphan uid node: %s %04x\n", entry->desc, entry->tag); break; case TAG_SENTENCE: sent = NODET_TO_PTR(entry, sentence_t *); outbuf_addv(&out, "WARNING: Found orphan sentence: %s:%s - %s %04x\n", sent->ob->obname, sent->function, entry->desc, entry->tag); break; case TAG_PERM_IDENT: outbuf_addv(&out, "WARNING: Found orphan permanent identifier: %s %04x\n", entry->desc, entry->tag); break; case TAG_STRING: ptr = NODET_TO_PTR(entry, char *); outbuf_addv(&out, "WARNING: Found orphan malloc'ed string: \"%s\" - %s %04x\n", ptr, entry->desc, entry->tag); break; case TAG_MALLOC_STRING: msbl = NODET_TO_PTR(entry, malloc_block_t *); /* don't give an error for the return value we are constructing :) */ if (msbl == MSTR_BLOCK(out.buffer)) break; if (msbl->ref != msbl->extra_ref) outbuf_addv(&out, "Bad ref count for malloc string \"%s\" %s %04x, is %d - should be %d\n", (char *)(msbl + 1), entry->desc, entry->tag, msbl->ref, msbl->extra_ref); break; case TAG_SHARED_STRING: ssbl = NODET_TO_PTR(entry, block_t *); if (REFS(ssbl) != EXTRA_REF(ssbl)) outbuf_addv(&out, "Bad ref count for shared string \"%s\", is %d - should be %d\n", STRING(ssbl), REFS(ssbl), EXTRA_REF(ssbl)); break; case TAG_ED: outbuf_addv(&out, "Found allocated ed block: %s %04x\n", entry->desc, entry->tag); break; case TAG_MAP_TBL: outbuf_addv(&out, "WARNING: Found orphan mapping table: %s %04x\n", entry->desc, entry->tag); break; case TAG_MAP_NODE_BLOCK: outbuf_addv(&out, "WARNING: Found orphan mapping node block: %s %04x\n", entry->desc, entry->tag); break; } entry->tag &= ~TAG_MARKED; } } } if (flag & 1) { outbuf_add(&out, "\n\n"); outbuf_add(&out, " source blks total\n"); outbuf_add(&out, "------------------------------ ------ --------\n"); for (i = 1; i < MAX_CATEGORY; i++) { if (totals[i]) outbuf_addv(&out, "%-30s %6d %8d\n", sources[i], blocks[i], totals[i]); if (i == 5) outbuf_add(&out, "\n"); } } if (!(flag & 2)) outbuf_push(&out); else { FREE_MSTR(out.buffer); push_number(0); } } #endif /* DEBUGMALLOC_EXTENSIONS */ #endif /* DEBUGMALLOC */