links/error.c
2021-08-28 18:37:32 +03:00

992 lines
22 KiB
C

/* error.c
* (c) 2002 Mikulas Patocka
* This file is a part of the Links program, released under GPL.
*/
#include "links.h"
#if DEBUGLEVEL >= 2
#define RED_ZONE 'R'
#endif
#if DEBUGLEVEL >= 3
#define FREE_FILL 0xfe
#define REALLOC_FILL 0xfd
#define ALLOC_FILL 0xfc
#endif
#if DEBUGLEVEL < 0
#define FREE_FILL 0xfe
#endif
#ifdef RED_ZONE
#define RED_ZONE_INC 1
#else
#define RED_ZONE_INC 0
#endif
volatile char dummy_val;
volatile char * volatile dummy_ptr = &dummy_val;
volatile char * volatile x_ptr;
void *do_not_optimize_here(void *p)
{
/* break ANSI aliasing */
x_ptr = p;
*dummy_ptr = 0;
return p;
}
#if defined(USE_WIN32_HEAP)
#include <windows.h>
/*#define NEW_HEAP*/
static HANDLE heap;
#define heap_malloc(s) HeapAlloc(heap, 0, (s))
#define heap_calloc(s) HeapAlloc(heap, HEAP_ZERO_MEMORY, (s))
#define heap_free(p) HeapFree(heap, 0, (p))
#define heap_realloc(p, s) HeapReAlloc(heap, 0, (p), (s))
void init_heap(void)
{
#ifndef NEW_HEAP
heap = GetProcessHeap();
#else
if (!(heap = HeapCreate(0, 0, 0)))
fatal_exit("HeapCreate failed: %x", (unsigned)GetLastError());
#endif
{
ULONG heap_frag = 2;
if (!HeapSetInformation(heap, HeapCompatibilityInformation, &heap_frag, sizeof(heap_frag))) {
/*fatal_exit("HeapSetInformation failed: %x", (unsigned)GetLastError());*/
}
}
}
static void exit_heap(void)
{
#if DEBUGLEVEL >= 1
if (!HeapValidate(heap, 0, NULL))
internal_error("HeapValidate failed: %x", (unsigned)GetLastError());
#endif
#ifdef NEW_HEAP
if (!HeapDestroy(heap))
internal_error("HeapDestroy failed: %x", (unsigned)GetLastError());
#endif
}
#elif defined(OS2_ADVANCED_HEAP) || defined(VMS_ADVANCED_HEAP)
#define SIZE_THRESHOLD 65536
#define REALLOC_PART 32
struct heap_entry {
void *ptr;
size_t len;
};
static struct heap_entry *heap_entries;
unsigned long mem_bigalloc;
unsigned long blocks_bigalloc;
unsigned long blocks_bigalloc_allocated;
static my_uintptr_t heap_mask;
static void *big_calloc(size_t size, int re_al)
{
void *ptr;
my_uintptr_t mask;
size_t padding;
if (blocks_bigalloc == blocks_bigalloc_allocated) {
struct heap_entry *he;
if (blocks_bigalloc_allocated > MAXINT / 2 / sizeof(struct heap_entry) - 1)
return NULL;
he = realloc(heap_entries, (blocks_bigalloc_allocated + 1) * sizeof(struct heap_entry));
if (!he)
return NULL;
heap_entries = he;
blocks_bigalloc_allocated++;
}
padding = page_size;
if (re_al) {
while (padding < size / REALLOC_PART && (size_t)(padding * 2) != 0)
padding *= 2;
}
size = (size + padding - 1) & ~(padding - 1);
if (!size)
return NULL;
ptr = virtual_alloc(size);
if (!ptr)
return NULL;
heap_entries[blocks_bigalloc].ptr = ptr;
heap_entries[blocks_bigalloc].len = size;
blocks_bigalloc++;
mem_bigalloc += size;
mask = (my_uintptr_t)ptr;
mask ^= (mask - 1);
mask >>= 1;
heap_mask &= mask;
/*fprintf(stderr, "mask: %p -> %x -> %x\n", ptr, mask, heap_mask);*/
return ptr;
}
static void *heap_malloc(size_t size)
{
if (size >= SIZE_THRESHOLD) {
void *ptr = big_calloc(size, 0);
if (ptr)
return ptr;
}
#ifdef OPENVMS_64BIT
if (size > 0x77fffff0)
return NULL;
#endif
return malloc(size);
}
static void *heap_calloc(size_t size)
{
if (size >= SIZE_THRESHOLD) {
void *ptr = big_calloc(size, 0);
if (ptr)
return ptr;
}
#ifdef OPENVMS_64BIT
if (size > 0x77fffff0)
return NULL;
#endif
return calloc(1, size);
}
static void heap_free(void *ptr)
{
unsigned i;
if (!((my_uintptr_t)ptr & heap_mask)) {
for (i = 0; i < blocks_bigalloc; i++) {
if (heap_entries[i].ptr == ptr) {
virtual_free(ptr, heap_entries[i].len);
mem_bigalloc -= heap_entries[i].len;
blocks_bigalloc--;
heap_entries[i] = heap_entries[blocks_bigalloc];
return;
}
}
}
free(ptr);
}
static void *heap_realloc(void *ptr, size_t size)
{
void *new_ptr;
if (!((my_uintptr_t)ptr & heap_mask)) {
unsigned i;
for (i = 0; i < blocks_bigalloc; i++) {
if (heap_entries[i].ptr == ptr) {
if (size <= heap_entries[i].len)
return ptr;
new_ptr = big_calloc(size, 1);
if (!new_ptr)
new_ptr = heap_malloc(size);
if (!new_ptr)
return NULL;
memcpy(new_ptr, ptr, heap_entries[i].len);
heap_free(ptr);
return new_ptr;
}
}
}
#ifdef OPENVMS_64BIT
if (size > 0x77fffff0)
return NULL;
#endif
if (size >= SIZE_THRESHOLD) {
new_ptr = big_calloc(size, 1);
if (new_ptr) {
#ifdef HAVE__MSIZE
size_t to_copy = _msize(ptr);
if (to_copy > size) to_copy = size;
memcpy(new_ptr, ptr, to_copy);
#else
ptr = realloc(ptr, size);
if (!ptr) {
heap_free(new_ptr);
return NULL;
}
memcpy(new_ptr, ptr, size);
#endif
free(ptr);
return new_ptr;
}
}
return realloc(ptr, size);
}
void init_heap(void)
{
mem_bigalloc = 0;
blocks_bigalloc = 0;
blocks_bigalloc_allocated = 0;
heap_entries = NULL;
heap_mask = -1;
}
static void exit_heap(void)
{
free(heap_entries);
heap_entries = NULL;
}
#else
#define heap_malloc malloc
#define heap_realloc realloc
#define heap_free free
#ifdef HAVE_CALLOC
#define heap_calloc(x) calloc(1, (x))
#else
static inline void *heap_calloc(size_t x)
{
void *p;
if ((p = heap_malloc(x))) memset(p, 0, x);
return p;
}
#endif
void init_heap(void)
{
}
#define exit_heap() do { } while (0)
#endif
#ifdef LEAK_DEBUG
my_uintptr_t mem_amount = 0;
my_uintptr_t mem_blocks = 0;
#define ALLOC_MAGIC 0xa110c
#define ALLOC_FREE_MAGIC 0xf4ee
#define ALLOC_REALLOC_MAGIC 0x4ea110c
#ifndef LEAK_DEBUG_LIST
struct alloc_header {
int magic;
size_t size;
};
#else
struct alloc_header {
list_entry_1st
size_t size;
unsigned char *file;
unsigned char *comment;
list_entry_last
int line;
int magic;
};
static struct list_head memory_list = { &memory_list, &memory_list };
#endif
#define L_D_S ((sizeof(struct alloc_header) + 15) & ~15)
unsigned alloc_overhead = L_D_S + RED_ZONE_INC;
#endif
static inline void force_dump(void)
{
#if defined(DOS)
fprintf(stderr, "\n"ANSI_SET_BOLD"Exiting"ANSI_CLEAR_BOLD"\n");
fflush(stdout);
fflush(stderr);
exit(RET_INTERNAL);
#else
int rs;
fprintf(stderr, "\n"ANSI_SET_BOLD"Forcing core dump"ANSI_CLEAR_BOLD"\n");
fflush(stdout);
fflush(stderr);
EINTRLOOP(rs, raise(SIGSEGV));
#endif
}
void check_memory_leaks(void)
{
#if defined(LEAK_DEBUG) && !defined(NO_IE)
if (mem_amount || mem_blocks) {
char rb[OS_REPORT_ERROR_BUFFER];
char *rp = rb;
fatal_tty_exit();
fprintf(stderr, "\n"ANSI_SET_BOLD"Memory leak by %lu bytes (%lu blocks)"ANSI_CLEAR_BOLD"\n", (unsigned long)mem_amount, (unsigned long)mem_blocks);
snprintf(rp, sizeof rb - (rp - rb), "Memory leak by %lu bytes (%lu blocks)", (unsigned long)mem_amount, (unsigned long)mem_blocks);
rp = strchr(rp, 0);
#ifdef LEAK_DEBUG_LIST
fprintf(stderr, "\nList of blocks: ");
snprintf(rp, sizeof rb - (rp - rb), ":\r\n");
rp = strchr(rp, 0);
{
int r = 0;
struct alloc_header *ah;
struct list_head *lah;
foreach(struct alloc_header, ah, lah, memory_list) {
fprintf(stderr, "%s%p:%lu @ %s:%d", r ? ", ": "", (unsigned char *)ah + L_D_S, (unsigned long)ah->size, ah->file, ah->line);
snprintf(rp, sizeof rb - (rp - rb), "%s%p:%lu @ %s:%d", r ? ", ": "", (unsigned char *)ah + L_D_S, (unsigned long)ah->size, ah->file, ah->line);
rp = strchr(rp, 0);
if (ah->comment) {
fprintf(stderr, ":\"%s\"", ah->comment);
snprintf(rb, sizeof rb - (rp - rb), ":\"%s\"", ah->comment);
rp = strchr(rp, 0);
}
r = 1;
}
fprintf(stderr, "\n");
}
#endif
os_report_error("Memory leak", rb, NULL);
force_dump();
}
#endif
exit_heap();
}
static void er(int b, const char *m, va_list l)
{
#ifdef HAVE_VPRINTF
vfprintf(stderr, cast_const_char m, l);
#else
fprintf(stderr, "%s", m);
#endif
if (b) fprintf(stderr, ANSI_BELL);
fprintf(stderr, "\n");
fflush(stderr);
portable_sleep(1000);
}
void error(const char *m, ...)
{
va_list l;
va_start(l, m);
os_report_error_va("Error", m, l);
va_end(l);
va_start(l, m);
fprintf(stderr, "\n");
er(1, m, l);
va_end(l);
}
void fatal_exit(const char *m, ...)
{
va_list l;
va_start(l, m);
os_report_error_va("Fatal error", m, l);
va_end(l);
fatal_tty_exit();
va_start(l, m);
fprintf(stderr, "\n");
er(1, m, l);
va_end(l);
fflush(stdout);
fflush(stderr);
exit(RET_FATAL);
}
int errline;
unsigned char *errfile;
static unsigned char errbuf[4096];
void int_error(const char *m, ...)
{
#ifdef NO_IE
return;
#else
va_list l;
sprintf(cast_char errbuf, "Internal error at %s:%d\r\n%s", errfile, errline, m);
va_start(l, m);
os_report_error_va("Internal error", cast_const_char errbuf, l);
va_end(l);
fatal_tty_exit();
sprintf(cast_char errbuf, "\n"ANSI_SET_BOLD"INTERNAL ERROR"ANSI_CLEAR_BOLD" at %s:%d: %s", errfile, errline, m);
va_start(l, m);
er(1, cast_char errbuf, l);
va_end(l);
force_dump();
exit(RET_INTERNAL);
#endif
}
void debug_msg(const char *m, ...)
{
va_list l;
va_start(l, m);
sprintf(cast_char errbuf, "\nDEBUG MESSAGE at %s:%d: %s", errfile, errline, m);
er(0, cast_char errbuf, l);
va_end(l);
}
#ifdef LEAK_DEBUG
void *debug_mem_alloc(unsigned char *file, int line, size_t size, int mayfail)
{
void *p;
#ifdef LEAK_DEBUG
struct alloc_header *ah;
#endif
dos_poll_break();
debug_test_free(file, line);
if (!size) return DUMMY;
if (size > MAX_SIZE_T - L_D_S - RED_ZONE_INC) {
if (mayfail) return NULL;
overalloc_at(file, line);
}
size += L_D_S;
retry:
#ifdef LEAK_DEBUG
mem_amount += size - L_D_S;
mem_blocks++;
#endif
if (!(p = heap_malloc(size + RED_ZONE_INC))) {
#ifdef LEAK_DEBUG
mem_amount -= size - L_D_S;
mem_blocks--;
#endif
if (out_of_memory_fl(0, !mayfail ? cast_uchar "malloc" : NULL, size + RED_ZONE_INC, file, line)) goto retry;
return NULL;
}
#ifdef RED_ZONE
*((unsigned char *)p + size + RED_ZONE_INC - 1) = RED_ZONE;
#endif
#ifdef LEAK_DEBUG
ah = p;
p = (unsigned char *)p + L_D_S;
ah->size = size - L_D_S;
ah->magic = ALLOC_MAGIC;
#ifdef LEAK_DEBUG_LIST
ah->file = file;
ah->line = line;
ah->comment = NULL;
add_to_list(memory_list, ah);
#endif
#endif
#ifdef ALLOC_FILL
memset(p, ALLOC_FILL, size - L_D_S);
#endif
return p;
}
void *debug_mem_calloc(unsigned char *file, int line, size_t size, int mayfail)
{
void *p;
#ifdef LEAK_DEBUG
struct alloc_header *ah;
#endif
dos_poll_break();
debug_test_free(file, line);
if (!size) return DUMMY;
if (size > MAX_SIZE_T - L_D_S - RED_ZONE_INC) {
if (mayfail) return NULL;
overalloc_at(file, line);
}
size += L_D_S;
retry:
#ifdef LEAK_DEBUG
mem_amount += size - L_D_S;
mem_blocks++;
#endif
if (!(p = heap_calloc(size + RED_ZONE_INC))) {
#ifdef LEAK_DEBUG
mem_amount -= size - L_D_S;
mem_blocks--;
#endif
if (out_of_memory_fl(0, !mayfail ? cast_uchar "calloc" : NULL, size + RED_ZONE_INC, file, line)) goto retry;
return NULL;
}
#ifdef RED_ZONE
*((unsigned char *)p + size + RED_ZONE_INC - 1) = RED_ZONE;
#endif
#ifdef LEAK_DEBUG
ah = p;
p = (unsigned char *)p + L_D_S;
ah->size = size - L_D_S;
ah->magic = ALLOC_MAGIC;
#ifdef LEAK_DEBUG_LIST
ah->file = file;
ah->line = line;
ah->comment = NULL;
add_to_list(memory_list, ah);
#endif
#endif
return p;
}
void debug_mem_free(unsigned char *file, int line, void *p)
{
#ifdef LEAK_DEBUG
struct alloc_header *ah;
#endif
dos_poll_break();
if (p == DUMMY) return;
if (!p) {
errfile = file, errline = line, int_error("mem_free(NULL)");
return;
}
#ifdef LEAK_DEBUG
p = (unsigned char *)p - L_D_S;
ah = p;
if (ah->magic != ALLOC_MAGIC) {
errfile = file, errline = line, int_error("mem_free: magic doesn't match: %08x", ah->magic);
return;
}
#ifdef FREE_FILL
memset((unsigned char *)p + L_D_S, FREE_FILL, ah->size);
#endif
ah->magic = ALLOC_FREE_MAGIC;
#ifdef LEAK_DEBUG_LIST
del_from_list(ah);
#endif
mem_amount -= ah->size;
mem_blocks--;
#endif
#ifdef RED_ZONE
if (*((unsigned char *)p + L_D_S + ah->size + RED_ZONE_INC - 1) != RED_ZONE) {
errfile = file, errline = line, int_error("mem_free: red zone damaged: %02x (block allocated at %s:%d%s%s)", *((unsigned char *)p + L_D_S + ah->size + RED_ZONE_INC - 1),
#ifdef LEAK_DEBUG_LIST
ah->file, ah->line, ah->comment ? ":" : "", ah->comment ? ah->comment : (unsigned char *)"");
#else
"-", 0, "-");
#endif
return;
}
#endif
#ifdef LEAK_DEBUG_LIST
if (ah->comment) heap_free(ah->comment);
#endif
heap_free(p);
}
void *debug_mem_realloc(unsigned char *file, int line, void *p, size_t size, int mayfail)
{
#ifdef LEAK_DEBUG
struct alloc_header *ah;
#endif
void *np;
if (p == DUMMY) return debug_mem_alloc(file, line, size, mayfail);
dos_poll_break();
debug_test_free(file, line);
if (!p) {
errfile = file, errline = line, int_error("mem_realloc(NULL, %lu)", (unsigned long)size);
return NULL;
}
if (!size) {
debug_mem_free(file, line, p);
return DUMMY;
}
if (size > MAX_SIZE_T - L_D_S - RED_ZONE_INC) {
if (mayfail) return NULL;
overalloc_at(file, line);
}
#ifdef LEAK_DEBUG
p = (unsigned char *)p - L_D_S;
ah = p;
if (ah->magic != ALLOC_MAGIC) {
errfile = file, errline = line, int_error("mem_realloc: magic doesn't match: %08x", ah->magic);
return NULL;
}
ah->magic = ALLOC_REALLOC_MAGIC;
#ifdef REALLOC_FILL
if (!mayfail && size < (size_t)ah->size) memset((unsigned char *)p + L_D_S + size, REALLOC_FILL, ah->size - size);
#endif
#endif
#ifdef RED_ZONE
if (*((unsigned char *)p + L_D_S + ah->size + RED_ZONE_INC - 1) != RED_ZONE) {
errfile = file, errline = line, int_error("mem_realloc: red zone damaged: %02x (block allocated at %s:%d%s%s)", *((unsigned char *)p + L_D_S + ah->size + RED_ZONE_INC - 1),
#ifdef LEAK_DEBUG_LIST
ah->file, ah->line, ah->comment ? ":" : "", ah->comment ? ah->comment : (unsigned char *)"");
#else
"-", 0, "-");
#endif
return (unsigned char *)p + L_D_S;
}
#endif
retry:
if (!(np = heap_realloc(p, size + L_D_S + RED_ZONE_INC))) {
if (out_of_memory_fl(0, !mayfail ? cast_uchar "realloc" : NULL, size + L_D_S + RED_ZONE_INC, file, line)) goto retry;
ah->magic = ALLOC_MAGIC;
return NULL;
}
p = np;
#ifdef RED_ZONE
*((unsigned char *)p + size + L_D_S + RED_ZONE_INC - 1) = RED_ZONE;
#endif
#ifdef LEAK_DEBUG
ah = p;
/* OpenVMS Alpha C miscompiles this: mem_amount += size - ah->size; */
mem_amount += size;
mem_amount -= ah->size;
ah->size = size;
ah->magic = ALLOC_MAGIC;
#ifdef LEAK_DEBUG_LIST
fix_list_after_realloc(ah);
#endif
#endif
return (unsigned char *)p + L_D_S;
}
void set_mem_comment(void *p, unsigned char *c, int l)
{
#ifdef LEAK_DEBUG_LIST
struct alloc_header *ah = (struct alloc_header *)((unsigned char *)p - L_D_S);
if (ah->comment) heap_free(ah->comment);
if ((ah->comment = heap_malloc(l + 1))) memcpy(ah->comment, c, l), ah->comment[l] = 0;
#endif
}
#ifdef JS
unsigned char *get_mem_comment(void *p)
{
#ifdef LEAK_DEBUG_LIST
/* perm je prase: return ((struct alloc_header*)((unsigned char*)((void*)((unsigned char*)p-sizeof(int))) - L_D_S))->comment;*/
struct alloc_header *ah = (struct alloc_header *)((unsigned char *)p - L_D_S);
if (!ah->comment) return cast_uchar "";
else return ah->comment;
#else
return cast_uchar "";
#endif
}
#endif
#else
void *mem_alloc_(size_t size, int mayfail)
{
void *p;
dos_poll_break();
debug_test_free(NULL, 0);
if (!size) return DUMMY;
if (size > MAX_SIZE_T) {
if (mayfail) return NULL;
overalloc();
}
retry:
if (!(p = heap_malloc(size))) {
if (out_of_memory_fl(0, !mayfail ? cast_uchar "malloc" : NULL, size, NULL, 0)) goto retry;
return NULL;
}
return p;
}
void *mem_calloc_(size_t size, int mayfail)
{
void *p;
dos_poll_break();
debug_test_free(NULL, 0);
if (!size) return DUMMY;
if (size > MAX_SIZE_T) {
if (mayfail) return NULL;
overalloc();
}
retry:
if (!(p = heap_calloc(size))) {
if (out_of_memory_fl(0, !mayfail ? cast_uchar "calloc" : NULL, size, NULL, 0)) goto retry;
return NULL;
}
return p;
}
void mem_free(void *p)
{
dos_poll_break();
if (p == DUMMY) return;
if (!p) {
internal_error("mem_free(NULL)");
return;
}
heap_free(p);
}
void *mem_realloc_(void *p, size_t size, int mayfail)
{
void *np;
if (p == DUMMY) return mem_alloc_(size, mayfail);
dos_poll_break();
debug_test_free(NULL, 0);
if (!p) {
internal_error("mem_realloc(NULL, %lu)", (unsigned long)size);
return NULL;
}
if (!size) {
mem_free(p);
return DUMMY;
}
if (size > MAX_SIZE_T) {
if (mayfail) return NULL;
overalloc();
}
retry:
if (!(np = heap_realloc(p, size))) {
if (out_of_memory_fl(0, !mayfail ? cast_uchar "realloc" : NULL, size, NULL, 0)) goto retry;
return NULL;
}
return np;
}
#endif
#if !(defined(LEAK_DEBUG) && defined(LEAK_DEBUG_LIST))
unsigned char *memacpy(const unsigned char *src, size_t len)
{
unsigned char *m;
if (!(len + 1)) overalloc();
m = (unsigned char *)mem_alloc(len + 1);
if (len)
memcpy(m, src, len);
m[len] = 0;
return m;
}
unsigned char *stracpy(const unsigned char *src)
{
return src ? memacpy(src, src != DUMMY ? strlen(cast_const_char src) : 0) : NULL;
}
#else
unsigned char *debug_memacpy(unsigned char *f, int l, const unsigned char *src, size_t len)
{
unsigned char *m;
m = (unsigned char *)debug_mem_alloc(f, l, len + 1, 0);
if (len)
memcpy(m, src, len);
m[len] = 0;
return m;
}
unsigned char *debug_stracpy(unsigned char *f, int l, const unsigned char *src)
{
return src ? (unsigned char *)debug_memacpy(f, l, src, src != DUMMY ? strlen(cast_const_char src) : 0L) : NULL;
}
#endif
#if defined(LEAK_DEBUG) && defined(LEAK_DEBUG_LIST)
struct memory_entry {
struct alloc_header *ah;
my_uintptr_t cumulative_size;
my_uintptr_t n_blocks;
};
LIBC_CALLBACK static int memory_sort_file_line(const void *l1_, const void *l2_)
{
const struct memory_entry *l1 = (const struct memory_entry *)l1_;
const struct memory_entry *l2 = (const struct memory_entry *)l2_;
int c;
c = strcmp(cast_const_char l1->ah->file, cast_const_char l2->ah->file);
if (c)
return c;
if (l1->ah->line != l2->ah->line)
return l1->ah->line - l2->ah->line;
return 0;
}
LIBC_CALLBACK static int memory_sort_cumulative_size(const void *l1_, const void *l2_)
{
const struct memory_entry *l1 = (const struct memory_entry *)l1_;
const struct memory_entry *l2 = (const struct memory_entry *)l2_;
int c;
if (l1->cumulative_size < l2->cumulative_size)
return 1;
if (l1->cumulative_size > l2->cumulative_size)
return -1;
if (l1->n_blocks < l2->n_blocks)
return 1;
if (l1->n_blocks > l2->n_blocks)
return -1;
c = strcmp(cast_const_char l1->ah->file, cast_const_char l2->ah->file);
if (c)
return c;
if (l1->ah->line != l2->ah->line)
return l1->ah->line - l2->ah->line;
return 0;
}
static int num_digits(my_uintptr_t num)
{
int n = 0;
do {
n++;
num /= 10;
} while (num);
return n;
}
unsigned char *get_top_memory(int mode, unsigned n)
{
struct memory_entry *entries;
struct alloc_header *ah;
struct list_head *lah;
my_uintptr_t offs, offs2, mr, ln;
unsigned char *r;
int rl;
int nd = 0; /* against warning */
entries = malloc(sizeof(struct memory_entry) * mem_blocks);
if (!entries)
return stracpy(cast_uchar "Not enough memory");
offs = 0;
foreach(struct alloc_header, ah, lah, memory_list) {
entries[offs].ah = ah;
entries[offs].cumulative_size = ah->size;
entries[offs].n_blocks = 1;
offs++;
}
if (offs) qsort(entries, offs, sizeof(struct memory_entry), (int (*)(const void *, const void *))memory_sort_file_line);
if (mode == GTM_MOST_ALLOCATED) {
offs2 = 0;
for (mr = 0; mr < offs; mr++) {
entries[offs2] = entries[mr];
while (mr + 1 < offs &&
!strcmp(cast_const_char entries[mr].ah->file, cast_const_char entries[mr + 1].ah->file) &&
entries[mr].ah->line == entries[mr + 1].ah->line) {
mr++;
entries[offs2].cumulative_size += entries[mr].ah->size;
entries[offs2].n_blocks++;
}
offs2++;
}
} else {
offs2 = offs;
}
if (offs2) qsort(entries, offs2, sizeof(struct memory_entry), (int (*)(const void *, const void *))memory_sort_cumulative_size);
r = init_str();
rl = 0;
if (offs2) {
nd = num_digits(entries[0].cumulative_size);
}
for (mr = 0, ln = 0; mr < offs2 && ln < n; mr++, ln++) {
int l1 = rl;
int d1;
unsigned char *g, *f = entries[mr].ah->file;
while ((g = cast_uchar strchr(cast_const_char f, '/'))) f = g + 1;
add_to_str(&r, &rl, f);
add_chr_to_str(&r, &rl, ':');
add_num_to_str(&r, &rl, entries[mr].ah->line);
add_chr_to_str(&r, &rl, ' ');
d1 = num_digits(entries[mr].cumulative_size);
while (rl < l1 + 24 + (nd - d1))
add_chr_to_str(&r, &rl, ' ');
add_num_to_str(&r, &rl, entries[mr].cumulative_size);
if (mode == GTM_MOST_ALLOCATED) {
add_to_str(&r, &rl, cast_uchar " / ");
add_num_to_str(&r, &rl, entries[mr].n_blocks);
} else if (mode == GTM_LARGEST_BLOCKS) {
my_uintptr_t mq;
for (mq = mr + 1; mq < offs2; mq++) {
if (strcmp(cast_const_char entries[mr].ah->file, cast_const_char entries[mq].ah->file))
break;
if (entries[mr].ah->line != entries[mq].ah->line)
break;
if (entries[mr].cumulative_size != entries[mq].cumulative_size)
break;
}
if (mq > mr + 1) {
add_to_str(&r, &rl, cast_uchar " x ");
add_num_to_str(&r, &rl, mq - mr);
}
mr = mq - 1;
}
add_chr_to_str(&r, &rl, '\n');
}
if (rl) {
rl--;
r[rl] = 0;
}
free(entries);
return r;
}
#endif
#ifdef OOPS
struct prot {
list_entry_1st
sigjmp_buf buf;
list_entry_last
};
static struct list_head prot = { &prot, &prot };
static void fault(void *dummy)
{
struct prot *p;
/*fprintf(stderr, "FAULT: %d !\n", (int)(unsigned long)dummy);*/
if (list_empty(prot)) {
fatal_tty_exit();
exit(0);
}
p = list_struct(prot.next, struct prot);
del_from_list(p);
longjmp(p->buf, 1);
}
sigjmp_buf *new_stack_frame(void)
{
static int handled = 0;
struct prot *new;
if (!handled) {
#ifdef SIGILL
install_signal_handler(SIGILL, fault, (void *)SIGILL, 1);
#endif
#ifdef SIGABRT
install_signal_handler(SIGABRT, fault, (void *)SIGABRT, 1);
#endif
#ifdef SIGFPE
install_signal_handler(SIGFPE, fault, (void *)SIGFPE, 1);
#endif
#ifdef SIGSEGV
install_signal_handler(SIGSEGV, fault, (void *)SIGSEGV, 1);
#endif
#ifdef SIGBUS
install_signal_handler(SIGBUS, fault, (void *)SIGBUS, 1);
#endif
handled = 1;
}
if (!(new = mem_alloc(sizeof(struct prot)))) return NULL;
add_to_list(prot, new);
return &new->buf;
}
void xpr(void)
{
if (!list_empty(prot)) {
struct prot *next = list_struct(prot.next, struct prot);
del_from_list(next);
mem_free(next);
}
}
void nopr(void)
{
free_list(struct prot, prot);
}
#endif