flower_malloc.c
来自「BCAST Implementation for NS2」· C语言 代码 · 共 508 行
C
508 行
/* * Library interposer to collect malloc/calloc/realloc statistics * * To compile use: * gcc -shared -fpic -o flower_malloc.so flower_malloc.c * * To run use (in BASH): * LD_PRELOAD=$cwd/flower_malloc.so <your application here> * * The results will be in ./flower_report.<prog_name>.<pid> for each * process invoked by current application. * * How to use it... * * Start your application as mentioned above, and send it SIGUSR2 whenever you * want to examine the state of the different allocation points in your * program. * * If you see that one of the tracked allocation points growing suspciously, * create a file called flower_limits.cfg, with the same format a the report * file generated by flower_malloc. When a SIGUSR2 signal is sent to * flower_malloc, it checks for such a file, and if found, it will load the * limits specified therein for 'allocs' and 'total size' (the other fields are * ignored). When any tracker exceeds those limits, the program will abort. * For instance, if your flower_report looks like this: * *********************************************** allocs/watermark total size/watermark avg size tracker_id 0/120 0/252000 2100 3bab4 300/300 90000/90000 300 3d55c + 4/5 280000/350000 70000 3d7ac + 3/3 20480/20480 6826 4d3a9 * and you are worried about the allocation tracked by tracker_id 3d55c, you * can edit the columns 'allocs' and/or 'total size' on the file to be: *********************************************** allocs/watermark total size/watermark avg size tracker_id 200/300 80000/90000 300 3d55c + * * and save it as flower_limits.cfg. You must remove the trackers you are not * interested on. The next time you run your program, it will abort when there * are 200 outstanding allocations or 80 kbytes from that point in your * program. If the limits you provide have already been exceeded by the time * you send the SIGUSR2 signal, flower_malloc will abort the next time an * allocation happens from the execution point identified by the tracker. * * * Sending a SIGUSR2 will also force flower_malloc to read your * limits configuration file, so you will not need to restart your application. * * SIGUSR1 can be used to reset all trackers. */#include "config.h"#ifdef HOST_OS_FREEBSD #include <dlfcn.h>#include <memory.h>#include <assert.h>#ifdef APPL_IS_MULTITHREADED#include <thread.h>#endif /* APPL_IS_MULTITHREADED */#include <stdio.h>#include <fcntl.h>#include <signal.h>#include <errno.h>#include <stdlib.h>#include <limits.h>#include <unistd.h>#define BACKTRACE_DEPTH 4#define HASH_TABLE_SIZE 343051 /* prime, max 31 bits */#define ADMIN_DATA_SIZE 2 * sizeof(uint32_t)typedef struct alloc_tracker { uint32_t count; uint32_t max_count; uint32_t last_read_max; uint32_t total_size; uint32_t watermark;} alloc_tracker_t;typedef struct limit_tracker { uint32_t enabled; uint32_t count; uint32_t total_size;} limit_tracker_t;static FILE *output = NULL;static int pid;static char path[64] = "uninitialized";static alloc_tracker_t tracker_table[HASH_TABLE_SIZE];static limit_tracker_t limits_table[HASH_TABLE_SIZE];static int32_t no_tracking;/* prototypes */static uint32_t within_limits (uint32_t trk_idx);static void init_flower_malloc(void);/*************************************************************************** * print_data - dumps the state of the alloc tracking table * */static void print_data(void){ int i; char growing[2] = " "; no_tracking = 1; /* prevent mallocs in fprintf from spoiling our report */ fprintf(output, "***********************************************\n"); fprintf(output, "allocs/watermark total size/watermark"); fprintf(output, " avg size tracker_id [+]\n"); for (i=0; i < HASH_TABLE_SIZE; i++) if (tracker_table[i].watermark) { fprintf(output, "%8u/%-8u %10u/%-10u ", tracker_table[i].count, tracker_table[i].max_count, tracker_table[i].total_size, tracker_table[i].watermark); if (tracker_table[i].count) fprintf(output, "%8u ", tracker_table[i].total_size / tracker_table[i].count); else /* use watermark to determine avg size */ fprintf(output, "%8u ", tracker_table[i].watermark / tracker_table[i].max_count); if (tracker_table[i].max_count > tracker_table[i].last_read_max) { if (tracker_table[i].last_read_max) growing[0] = '+'; tracker_table[i].last_read_max = tracker_table[i].max_count; } else growing[0] = ' '; fprintf(output, "%8x %s\n", i, growing); } fflush(output); /* untentionally leaving file open */ no_tracking = 0;}/*************************************************************************** * read_config - read flower_malloc config file * */static voidread_config(void){ unsigned long trk_idx, count, max_count, total_size, watermark, avg_size; int assig; FILE *config; #define MAX_CFG_LINE_WIDTH 80 char ignored[MAX_CFG_LINE_WIDTH]; config = fopen("./flower_limits.cfg","r"); if (!config) return; no_tracking = 1; /* prevent mallocs in fprintf from spoiling our report */ do { assig = fscanf(config, "%8lu/%8lu %10lu/%10lu %8lu %8lx %*[+ ]\n", &count, &max_count, &total_size, &watermark, &avg_size, &trk_idx); if (assig == 6) { fprintf(output, "Abort if tracker %8lx exceeds %8lu outst " "allocs or %10lu bytes\n", trk_idx, count, total_size); fflush(stdout); limits_table[trk_idx].enabled = 1; limits_table[trk_idx].count = count; limits_table[trk_idx].total_size = total_size; } else { fgets(ignored, MAX_CFG_LINE_WIDTH, config); } } while (assig != EOF); fclose(config); no_tracking = 0;}/*************************************************************************** * sigusr1_handler - handler for the SIGUSR1 signal * * This signal is used to reset the tracker and limits tables */static void sigusr1_handler(int unused){ uint32_t i; unused = unused; for (i=0; i < HASH_TABLE_SIZE; i++) { tracker_table[i].count = 0; tracker_table[i].max_count = 0; tracker_table[i].last_read_max = 0; tracker_table[i].total_size = 0; tracker_table[i].watermark = 0; limits_table[i].enabled = 0; limits_table[i].count = 0; limits_table[i].total_size = 0; } }/*************************************************************************** * sigusr2_handler - handler for the SIGUSR2 signal * * This signal is used to dump the current state of the alloc tracking table to * the output file. */static void sigusr2_handler(int unused){ unused = unused; read_config(); print_data();}/*************************************************************************** * within_limits - check if a tracker is within its configured limits * * Return zero if not */ static uint32_twithin_limits (uint32_t trk_idx){ if (!limits_table[trk_idx].enabled) return 1; return (tracker_table[trk_idx].count <= limits_table[trk_idx].count && tracker_table[trk_idx].total_size <= limits_table[trk_idx].total_size);}/*************************************************************************** * hash - calculate hash value based on backtrace stack * * This will be used to assign the same tracker to all the allocations that are * made from the same point in a program. */ static uint32_thash (void ** addr_array, size_t depth){ uint32_t i; uint32_t hash_value = 0; for (i=0; i<depth; i++) hash_value ^= ((uint32_t) addr_array[i] << (i+1)); hash_value %= HASH_TABLE_SIZE; return hash_value;}/*************************************************************************** * get_backtrace - copy the current stack backtrace onto ret_addr array * * This function uses gcc built-in function __builtin_return_address to fetch * the return addresses from the stack. Since this function only accepts * constant integers as arguments, we must use ugly preprocessor tricks to * allow for configurable backtrace depths. */static void get_backtrace(void ** ret_addr){ ret_addr[0] = __builtin_return_address(1); #if BACKTRACE_DEPTH > 1 ret_addr[1] = __builtin_return_address(2); #if BACKTRACE_DEPTH > 2 ret_addr[2] = __builtin_return_address(3); #if BACKTRACE_DEPTH > 3 ret_addr[3] = __builtin_return_address(4); #if BACKTRACE_DEPTH > 4 ret_addr[4] = __builtin_return_address(5); #if BACKTRACE_DEPTH > 5 ret_addr[5] = __builtin_return_address(6); #if BACKTRACE_DEPTH > 6 ret_addr[6] = __builtin_return_address(7); #if BACKTRACE_DEPTH > 7 ret_addr[7] = __builtin_return_address(8); #if BACKTRACE_DEPTH > 8 ret_addr[8] = __builtin_return_address(9); #if BACKTRACE_DEPTH > 9 ret_addr[9] = __builtin_return_address(10); #if BACKTRACE_DEPTH > 10#error "maximum supported BACKTRACE_DEPTH is 10"#endif /* 1 */#endif /* 2 */#endif /* 3 */#endif /* 4 */#endif /* 5 */#endif /* 6 */#endif /* 7 */#endif /* 8 */#endif /* 9 */#endif /* 10 */}static voidinit_flower_malloc(){ char procbuf[32]; char procname[20]; char prog_name[32]; int fd; static int initialized = 0; if (initialized) return; /* * Get current executable's name using proc(4) interface */ pid = (int)getpid (); (void)sprintf (procbuf, "/proc/%ld/status", (long)pid); if ((fd = open (procbuf, O_RDONLY)) != -1) { if (read (fd, procname, sizeof (procname)) == sizeof (procname)) { strtok (procname, " "); sprintf (prog_name, "%s.%d", procname, pid); } else sprintf (prog_name, "%s.%d", "unknown", pid); } else sprintf (prog_name, "%s.%d", "unknown", pid); sprintf (path, "%s%s", "./flower_report.", prog_name); if (!output) { output = fopen (path, "a"); if (!output) { fprintf(stderr, "unable to write to file %s\n", path); /* invoke original exit(), not the one we implement here */ (*((void (*) (int))dlsym (RTLD_NEXT, "exit"))) (-1); } } fprintf(output, "flower_malloc report for program %s\n\n", prog_name); fflush(output); signal(SIGUSR1, sigusr1_handler); signal(SIGUSR2, sigusr2_handler); initialized = 1; return;} /*************************************************************************** * track_alloc - track a new allocation * * This function updates the allocation tracker for this new allocation, and * marks the new memory block with the tracker index, and the size of the * requested memory block. Those two values are stored in the allocated memory * block, this is why the function requires ptr to point to a fresh memory * block of size req_size + 8 bytes. * * The function returns a pointer to the memory immediately following the marks * which should be returned to the user. * * +---------+---------+---------+---------+---------+---- * | trk_idx | size | (memory block of size req_size) ... * +---------+---------+---------+---------+---------+---- * ^ ^ * | | * ptr returned ptr */static void *track_alloc (void *ptr, size_t req_size){ uint32_t trk_idx; void * ret_addr[BACKTRACE_DEPTH]; if (no_tracking) return ptr; get_backtrace(ret_addr); trk_idx = hash(ret_addr,BACKTRACE_DEPTH); tracker_table[trk_idx].count++; tracker_table[trk_idx].total_size += req_size; if (tracker_table[trk_idx].total_size > tracker_table[trk_idx].watermark) tracker_table[trk_idx].watermark = tracker_table[trk_idx].total_size; if (tracker_table[trk_idx].count > tracker_table[trk_idx].max_count) tracker_table[trk_idx].max_count = tracker_table[trk_idx].count; if (!within_limits(trk_idx)) { fprintf(output,"\nTracker %x exceeding limits - ABORTING\n", trk_idx); print_data(); abort(); } * (uint32_t*) ptr = trk_idx; (uint32_t) ptr += sizeof(uint32_t); * (uint32_t*) ptr = req_size; (uint32_t) ptr += sizeof(uint32_t); return ptr;}/*************************************************************************** * track_free - track a free * * This function updates the allocation tracker for this free operation. * * The function takes the pointer to the memory passed by the user, and returns * the pointer that is to be freed * * +---------+---------+---------+---------+---------+---- * | trk_idx | size | (memory block of size req_size) ... * +---------+---------+---------+---------+---------+---- * ^ ^ * | | * returned ptr ptr */static void *track_free (void *ptr){ uint32_t trk_idx, req_size; if (no_tracking) return ptr; (uint32_t) ptr -= sizeof(uint32_t); req_size = * (uint32_t*) ptr; (uint32_t) ptr -= sizeof(uint32_t); trk_idx = * (uint32_t*) ptr; if (tracker_table[trk_idx].count) tracker_table[trk_idx].count--; if (tracker_table[trk_idx].total_size >= req_size) tracker_table[trk_idx].total_size -= req_size; return ptr;}void exit (int status){ /* * (Re)open the file here since the shell closes all file descriptors * before calling exit() */ output = fopen (path, "a"); if (output) print_data(); (*((void (*) (int))dlsym (RTLD_NEXT, "exit"))) (status); abort(); /* should never get here */}void *malloc (size_t size){ static void *(*func) (size_t); void *ret; if (!func) { func = (void *(*) (size_t))dlsym (RTLD_NEXT, "malloc"); init_flower_malloc(); } ret = func (size + ADMIN_DATA_SIZE); return (track_alloc (ret, size));}#if ORIGINAL_CALLOC_DOES_NOT_CALL_MALLOCvoid *calloc (size_t nelem, size_t elsize){ static void *(*func) (size_t, size_t); void *ret; int i; if (!func) { func = (void *(*) (size_t, size_t))dlsym (RTLD_NEXT, "calloc"); init_flower_malloc(); } ret = func (elsize * nelem + ADMIN_DATA_SIZE, 1); return (track_alloc(ret, elsize * nelem));}#endifvoid *realloc (void *ptr, size_t size){ static void *(*func) (void *, size_t); void *ret; if (!func) { func = (void *(*) (void *, size_t))dlsym (RTLD_NEXT, "realloc"); init_flower_malloc(); } ret = func (track_free(ptr), size + ADMIN_DATA_SIZE); return (track_alloc(ret, size));}voidfree (void *ptr){ static void (*func) (void *); if (!func) { func = (void (*) (void *))dlsym (RTLD_NEXT, "free"); init_flower_malloc(); } func (track_free(ptr)); return;}#endif /* HOST_OS_FREEBSD */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?