diff --git a/Makefile b/Makefile index 8c7d6e9..d0ecac3 100644 --- a/Makefile +++ b/Makefile @@ -21,25 +21,25 @@ LUA_LIBS = -llua5.2 all: luamemprofiler.so luamemprofiler.so: graphic.o lmp_struct.o vmemory.o lmp.o luamemprofiler.o - $(CC) graphic.o lmp_struct.o vmemory.o lmp.o luamemprofiler.o -o luamemprofiler.so $(CFLAGS) $(SDL_LIBS) $(LUA_LIBS) + cd src && $(CC) graphic.o lmp_struct.o vmemory.o lmp.o luamemprofiler.o -o luamemprofiler.so $(CFLAGS) $(SDL_LIBS) $(LUA_LIBS) && mv luamemprofiler.so ../ -luamemprofiler.o: luamemprofiler.c lmp.h - $(CC) -c luamemprofiler.c $(CFLAGS) $(LUA_CFLAGS) +luamemprofiler.o: + cd src && $(CC) -c luamemprofiler.c $(CFLAGS) $(LUA_CFLAGS) -lmp.o: lmp.c lmp.h lmp_struct.h vmemory.h - $(CC) -c lmp.c $(CFLAGS) $(LUA_CFLAGS) +lmp.o: + cd src && $(CC) -c lmp.c $(CFLAGS) $(LUA_CFLAGS) -lmp_struct.o: lmp_struct.c lmp_struct.h - $(CC) -c lmp_struct.c $(CFLAGS) $(LUA_CFLAGS) +lmp_struct.o: + cd src && $(CC) -c lmp_struct.c $(CFLAGS) $(LUA_CFLAGS) -vmemory.o: vmemory.c lmp.h vmemory.h graphic.h - $(CC) -c vmemory.c $(CFLAGS) $(LUA_CFLAGS) +vmemory.o: + cd src && $(CC) -c vmemory.c $(CFLAGS) $(LUA_CFLAGS) -graphic.o: gsdl.c graphic.h - $(CC) -c gsdl.c -o graphic.o $(CFLAGS) $(SDL_CFLAGS) +graphic.o: + cd src && $(CC) -c gsdl.c -o graphic.o $(CFLAGS) $(SDL_CFLAGS) clean: - rm *.o + rm src/*.o test: ./run.sh diff --git a/scripts/calc_screen.lua b/scripts/calc_screen.lua new file mode 100644 index 0000000..f8502da --- /dev/null +++ b/scripts/calc_screen.lua @@ -0,0 +1,52 @@ +local bytes_per_pixel = {4, 8, 12, 16, 32} + +function num_of_pixels(w, h) + return w * (h/2) +end + +function num_of_bytes(nop, bpp) + return bpp * nop +end + +function resolution(bpp, memused) + local side = math.sqrt(memused * 1000000 * 2 / bpp / (4*3))+ 1; + local w = side * 4; + local h = side * 3; + return w, h +end + +local op = 1 +while op do +print("Digite 1 para numero de bytes, 2 para resolução, 3 para bytes per pixel") +op = tonumber(io.read()) + +if op == 1 then + print("Entre com a largura:") + local w = tonumber(io.read()) + + print("Entre com a altura:") + local h = tonumber(io.read()) + + for v=4,32,4 do + local nop = num_of_pixels(w,h) + local nob = num_of_bytes(nop, v) + print(string.format("%d bytes %d x %d | %d | %d", v, w, h, nop, nob)) + end +elseif op == 2 then + print("Entre com a memoria maxima usada:") + local mmu = tonumber(io.read()) + + for v=4,32,4 do + local w, h = resolution(v, mmu) + print(string.format("%d bytes %d x %d", v, w, h)) + end +elseif op == 3 then + print("Entre com a memoria maxima usada:") + local mmu = tonumber(io.read()) + + local nop = num_of_pixels(800, 600) + print(string.format("%d bytes %d x %d", mmu*1000000/nop, 800, 600)) +else + op = nil +end +end diff --git a/scripts/gdb.sh b/scripts/gdb.sh new file mode 100755 index 0000000..afbd121 --- /dev/null +++ b/scripts/gdb.sh @@ -0,0 +1 @@ +LD_PRELOAD=/lib/libpthread.so.0 gdb --args lua5.2 $@ diff --git a/scripts/gen_test_out.sh b/scripts/gen_test_out.sh new file mode 100755 index 0000000..b0c2975 --- /dev/null +++ b/scripts/gen_test_out.sh @@ -0,0 +1,25 @@ +# generates test output based on your machine. +# You should run this file, before starting to work on the luamemprofiler code. +# + +TEST=cicle +lua tests/$TEST.lua > tests/out/$TEST.txt + +TEST=dupfinalizer +lua tests/$TEST.lua > tests/out/$TEST.txt + +TEST=eval +lua tests/$TEST.lua > tests/out/$TEST.txt + +TEST=function +lua tests/$TEST.lua > tests/out/$TEST.txt + +TEST=string +lua tests/$TEST.lua > tests/out/$TEST.txt + +TEST=table +lua tests/$TEST.lua > tests/out/$TEST.txt + +TEST=wfc +lua tests/$TEST.lua > tests/out/$TEST.txt + diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100755 index 0000000..1e049b1 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,21 @@ +############################################################################# +# Author: Pablo Musa # +# Creation Date: mar 27 2011 # +# Last Modification: aug 09 2011 # +# # +# Script for automated test # +############################################################################# + +TESTS=tests +OUT=$TESTS/out + +for i in $TESTS/*.lua +do + i=`basename $i .lua` +# echo "lua5.2 $TESTS/$i.lua > tmp.txt" + lua5.2 $TESTS/$i.lua > tmp.txt + echo "diff tmp.txt $OUT/$i.txt" + diff $OUT/$i.txt tmp.txt +done + +rm tmp.txt diff --git a/src/graphic.h b/src/graphic.h new file mode 100644 index 0000000..d61c822 --- /dev/null +++ b/src/graphic.h @@ -0,0 +1,145 @@ +/* +** +** Author: Pablo Musa +** Creation Date: mar 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** This is the header file of the drawing module. There is no graphic tool +** attached to our implementation. One can choose any graphic library to +** implement the graphic module. But one must implement all functions of +** this file and respect their contracts. +** If you are going to implement the graphic module using another graphic +** tool, please pay attention to the functions definitions. +** +*/ + + +#ifndef LMP_GRAPHIC_H +#define LMP_GRAPHIC_H + + +#define LMP_EVENT_EMPTY 0 +#define LMP_EVENT_KEY 1 +#define LMP_EVENT_MOUSE 2 +#define LEFT_BUTTON 1 +#define RIGHT_BUTTON 3 + +#define FONT_SIZE 16 + +#define mkColor(r,g,b) ((r) | ((g) << 8) | ((b) << 16)) + +#define getRed(c) ((c) & 0xFF) +#define getGreen(c) (((c) & 0xFF00) >> 8) +#define getBlue(c) (((c) & 0xFF0000) >> 16) + +#define DARKBLUE mkColor(0, 0, 139) +#define DARKRED mkColor(139, 0, 0) +#define DARKGREEN mkColor(0, 100, 0) +#define DARKORANGE mkColor(255, 140, 0) +#define DARKMAGENTA mkColor(139, 0, 139) +#define DIMGRAY mkColor(105, 105, 105) +#define LTWHITE mkColor(240, 240, 240) +#define LTGRAY mkColor(211, 211, 211) +#define WHITE mkColor(255, 255, 255) +#define BLACK mkColor(0, 0, 0) +#define RED mkColor(255, 0, 0) + + +struct kevent { + int key; +}; + +struct mevent { + int x, y, b; +}; + +union LMP_event { + struct kevent kevent; + struct mevent mevent; +}; +typedef union LMP_event LMP_Event; + +/* +** There are some available colors predefined in the beginning of this file. +** Use the macros: get(Red, Green, Blue) to map from Color to R, G or B. +*/ +typedef long Color; + +typedef void Screen; +typedef void Font; + +/* +** Create a new window with the specified width, height, icon and title. +** This function must load a base font if the graphic tool does not have +** one by default. This font will be used by other functions. +** Returns the screen pointer. +*/ +Screen *gr_newscreen (int width,int height,const char *icon,const char *title); + +/* +** Destroy the window referenced by the screen pointer. +** This function must unload the font if any font was loaded in gr_newscreen. +*/ +void gr_destroyscreen (Screen *screen); + +/* +** Draw a line in the window from (x0,y0) to (x1, y1). +*/ +void gr_drawline (Screen *screen, int x0 , int y0 , int x1, int y1 ); + +/* +** Draw an horizontal block from (x0,y) to x1 with blockheight. +*/ +void gr_drawblock (Screen *screen, int x0 , int x1 , int y, int blockheight); + +/* +** Write the specified text beginning at (x,y). +*/ +void gr_drawtext (Screen *screen, const char *text, int x, int y); + +/* +** Paint all window with the specified color. +** See Color typedef (beginning of this file) for more details about Color. +*/ +void gr_drawbackground (Screen *screen, Color color); + +/* +** Set the specified color for any following draw. +** See Color typedef (beginning of this file) for more details about Color. +*/ +void gr_setdrawcolor(Screen *screen, Color color); + +/* +** Set the specified color for any following text. +** See Color typedef (beginning of this file) for more details about Color. +*/ +void gr_settextcolor (Screen *screen, Color color); + +/* +** Return the width of one text character in pixels. +*/ +int gr_gettextwidth(Screen *screen); + +/* +** Return the height of one text character in pixels. +*/ +int gr_gettextheight(Screen *screen); + +/* +** Search for a keyboard or mouse event until queue is empty. +** Return an int containing the event type (mouse, keyboard or empty). +** This function removes all events from the top of the queue that are not +** keydown or mousebuttondown events. +*/ +int gr_getevent(Screen *screen, LMP_Event *event); + +/* +** Wait until a keyboard or mouse event occur. +** Returns an int containing the event type (mouse, keyboard or empty). +** This function discards all events from the top of the queue that are not +** keydown or mousebuttondown events. +*/ +int gr_waitevent(Screen *screen, LMP_Event *event); + +#endif diff --git a/src/gsdl.c b/src/gsdl.c new file mode 100644 index 0000000..37206bf --- /dev/null +++ b/src/gsdl.c @@ -0,0 +1,195 @@ +/* +** +** Author: Pablo Musa +** Creation Date: mar 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** This file contains the implementation of the graphic module (graphic.h) +** using the SDL graphic toolkit and the SDL_ttf library. +** See graphic.h for more details about the functions +*/ + +#include +#include +#include +#include "graphic.h" + +/* GLOBAL VARIABLES */ +static TTF_Font *font; +static SDL_Colour fontcolor = {0,0,0}; +static int gr_textwidth; +static int gr_textheight; + +/* STATIC FUNCTIONS */ +static TTF_Font* loadfont(char* file, int ptsize); + +/* GLOBAL FUNCTIONS */ +Screen *gr_newscreen(int width,int height,const char *icon,const char *title) { + SDL_Window *screen; + SDL_Renderer *renderer; + + Uint32 vmode = SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_RESIZABLE; + /*SDL_WINDOW_OPENGL;*/ + + if (SDL_Init(SDL_INIT_VIDEO) == -1) + return NULL; + + screen = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, vmode); + if(!screen) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + SDL_SetWindowIcon(screen, SDL_LoadBMP(icon)); + + renderer = SDL_CreateRenderer(screen, -1, 0); + if(!renderer) { + printf("Unable to set renderer: %s\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + + if (TTF_Init() == -1) { + printf("Unable to initialize SDL_ttf(fonts): %s \n", TTF_GetError()); + exit(EXIT_FAILURE); + } + + font = loadfont("FreeMono.ttf", FONT_SIZE); + + return (Screen*) renderer; +} + +void gr_destroyscreen (Screen *screen) { + TTF_Quit(); + SDL_Quit(); +} + +/* DRAW FUNCTIONS */ +void gr_drawline (Screen *screen, int x0, int y0, int x1, int y1) { + SDL_Renderer *sdl_screen = (SDL_Renderer*) screen; + SDL_RenderDrawLine(sdl_screen, x0, y0, x1, y1); + SDL_RenderPresent(sdl_screen); +} + +void gr_drawblock (Screen *screen, int x0 , int x1 , int y, int blockheight) { + SDL_Renderer *sdl_screen = (SDL_Renderer*) screen; + SDL_Rect rect; + rect.x = x0; + rect.y = y; + rect.w = x1 - x0 + 1; /* +1 -> draw x0 AND x1 */ + rect.h = blockheight; + SDL_RenderFillRect(sdl_screen, &rect); + SDL_RenderPresent(sdl_screen); +} + +void gr_drawtext(Screen *screen, const char *text, int x, int y) { + SDL_Renderer *sdl_screen = (SDL_Renderer*) screen; + SDL_Surface *tsurface; + SDL_Texture *ttexture; + SDL_Rect rect; + rect.x = x; + rect.y = y; + rect.w = strlen(text) * gr_textwidth; + rect.h = gr_textheight; + + tsurface = TTF_RenderText_Solid(font, text, fontcolor); + if(!tsurface) { + printf("Unable to create text surface: %s \n", TTF_GetError()); + exit(EXIT_FAILURE); + } + + ttexture = SDL_CreateTextureFromSurface(sdl_screen, tsurface); + SDL_FreeSurface(tsurface); + if(!ttexture) { + printf("Unable to create text texture: %s \n", SDL_GetError()); + exit(EXIT_FAILURE); + } + + SDL_RenderCopy(sdl_screen, ttexture, NULL, &rect); + SDL_RenderPresent(sdl_screen); +} + +void gr_drawbackground(Screen *screen, Color clr) { + SDL_Renderer *sdl_screen = (SDL_Renderer*) screen; + SDL_SetRenderDrawColor(sdl_screen, getRed(clr), getGreen(clr), getBlue(clr), SDL_ALPHA_OPAQUE); + SDL_RenderClear(sdl_screen); + SDL_RenderPresent(sdl_screen); +} + +/* SET FUNCTIONS */ +void gr_setdrawcolor(Screen *screen, Color clr) { + SDL_Renderer *sdl_screen = (SDL_Renderer*) screen; + SDL_SetRenderDrawColor(sdl_screen, getRed(clr), getGreen(clr), getBlue(clr), SDL_ALPHA_OPAQUE); +} + +void gr_settextcolor (Screen *screen, Color clr) { + fontcolor.r = getRed(clr); + fontcolor.g = getGreen(clr); + fontcolor.b = getBlue(clr); +} + +/* GET FUNCTIONS */ +int gr_gettextwidth(Screen *screen) { + return gr_textwidth; +} + +int gr_gettextheight(Screen *screen) { + return gr_textheight; +} + +/* EVENT FUNCTIONS */ +int gr_getevent(Screen *screen, LMP_Event *event) { + int res; + SDL_Event sdlevent; + + /* search for a keyboard or mouse event until list is empty */ + res = SDL_PollEvent(&sdlevent); + while (res == 1) { + if (sdlevent.type == SDL_KEYDOWN) { + event->kevent.key = sdlevent.key.keysym.sym; + return LMP_EVENT_KEY; + } else if (sdlevent.type == SDL_MOUSEBUTTONDOWN) { + event->mevent.x = sdlevent.button.x; + event->mevent.y = sdlevent.button.y; + return LMP_EVENT_MOUSE; + } + res = SDL_PollEvent(&sdlevent); + } + + /* no more events in the list */ + return LMP_EVENT_EMPTY; +} + +int gr_waitevent(Screen *screen, LMP_Event *event) { + int res; + SDL_Event sdlevent; + + /* wait until a keyboard or mouse event occur. discard other events */ + res = SDL_WaitEvent(&sdlevent); + while (res == 1) { + if (sdlevent.type == SDL_KEYDOWN) { + event->kevent.key = sdlevent.key.keysym.sym; + return LMP_EVENT_KEY; + } else if (sdlevent.type == SDL_MOUSEBUTTONDOWN) { + event->mevent.b = sdlevent.button.button; /* 1 - Left | 3 - Right */ + event->mevent.x = sdlevent.button.x; + event->mevent.y = sdlevent.button.y; + return LMP_EVENT_MOUSE; + } + res = SDL_WaitEvent(&sdlevent); + } + + printf("Unable to wait for an event!\n"); + exit(EXIT_FAILURE); +} + +/* STATIC FUNCTIONS */ +static TTF_Font* loadfont(char* file, int ptsize) { + TTF_Font *tmpfont = TTF_OpenFont(file, ptsize); + if (tmpfont == NULL){ + printf("Unable to load font: %s %s \n", file, TTF_GetError()); + exit(EXIT_FAILURE); + } + TTF_SizeText(tmpfont, "0", &gr_textwidth, &gr_textheight); + return tmpfont; +} + diff --git a/src/lmp.c b/src/lmp.c new file mode 100644 index 0000000..08d2be8 --- /dev/null +++ b/src/lmp.c @@ -0,0 +1,229 @@ +/* +** +** Author: Pablo Musa +** Creation Date: may 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** See lmp.h for module overview +** +*/ + +#include +#include +#include +#include +#include + +#include "lmp.h" +#include "vmemory.h" +#include "lmp_struct.h" + +#define LMP_FREE 0 +#define LMP_MALLOC 1 +#define LMP_REALLOC 2 + + +/* STATIC VARIABLES */ +/* ac = allocation counter */ +static int ac_string, ac_function, ac_userdata, ac_thread, ac_table, ac_other; + +static long nallocs, alloc_size; +static long nreallocs, realloc_size; +static long nfrees, free_size; +static long memoryuse, maxmemoryuse; +static int Laddress; +static int Maddress = 0; +static int usegraphics; + +/* STATIC FUNCTIONS */ +static void *lmp_malloc(size_t nsize, size_t osize); +static void *lmp_free(void *ptr); +static void *lmp_realloc(void *ptr, size_t nsize); +static void initcounters(); +static void updatecounters(int alloctype, size_t size, size_t luatype); +static void generatereport(); + +/* PUBLIC FUNCTIONS */ +void lmp_start(int lowestaddress, float memused, int usegraphic) { + initcounters(); + st_newhash(usegraphic); + usegraphics = usegraphic; + Laddress = lowestaddress; /* save lowest address to calc mem needed */ + if (usegraphics) + vm_start(lowestaddress, memused); +} + +void lmp_stop() { + generatereport(); + + /* erase counters and blocks */ + initcounters(); + st_destroyhash(); + if (usegraphics) + vm_stop(); +} + +/* allocation function used by Lua when luamemprofiler is used */ +void *lmp_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + (void) ud; + (void) osize; + + if (nsize == 0) { /* calls our malloc, free or realloc functions */ + return lmp_free(ptr); + } else if (ptr == NULL) { + return lmp_malloc(nsize, osize); /* osize is the lua_type */ + } else { + return lmp_realloc(ptr, nsize); + } +} + +/* STATIC FUNCTIONS */ + +/* does normal malloc and then alloc and update other structures */ +static void *lmp_malloc(size_t nsize, size_t luatype) { + void *ptr = malloc(nsize); /* normal malloc */ + lmp_Block *new = (lmp_Block *) malloc (sizeof(lmp_Block)); + + st_initblock(new, ptr, nsize, luatype); + st_insertblock(new); + + updatecounters(LMP_MALLOC, nsize, luatype); + if ((uintptr_t) ptr > Maddress) /* save max address to calc mem needed */ + Maddress = (uintptr_t) ptr; + + if (usegraphics) /* if graphics enabled call function to handle */ + vm_newmemop(LMP_VM_MALLOC, ptr, luatype, nsize); + + return ptr; +} + +/* free and update other structures and then does normal free */ +static void *lmp_free(void *ptr) { + lmp_Block *block = st_removeblock(ptr); + if (block != NULL) { + int size = st_getsize(block); + updatecounters(LMP_FREE, size, 0); + if (usegraphics) { /* if graphics enabled call function to handle */ + vm_newmemop(LMP_VM_FREE, ptr, LUA_TFREE, size); + } + free(block); + } + free(ptr); + return NULL; +} + +/* +** does normal realloc, assumes realloc always change object address (wich is +** not true, but is simplier to program and costless) and updates block +** information. then, if usegraphics, verify if realloc is enlarging or +** shrinking and call vm_newop with the correct values. Optimise drawing if +** realloc uses same object address. Finally, update counters. +*/ + +static void *lmp_realloc(void *ptr, size_t nsize) { + lmp_Block *block; + size_t osize; + void *p = realloc(ptr, nsize); + if (p == NULL) return NULL; + + block = st_removeblock(ptr); /* realloc usually changes memory address */ + if (block != NULL) { + osize = st_getsize(block); + st_setsize(block, nsize); + st_setptr(block, p); + st_insertblock(block); + if (usegraphics) { + int luatype = st_getluatype(block); + if (ptr != p) { /* memory location changed */ + vm_newmemop(LMP_VM_REALLOC, ptr, LUA_TFREE, osize); /*erase old block*/ + vm_newmemop(LMP_VM_REALLOC, p, luatype, nsize); + } else { + if (nsize > osize) { /* enlarging block */ + vm_newmemop(LMP_VM_REALLOC, (char *) ptr + osize, luatype, nsize - osize); + } else if (osize > nsize) { /* shrinking block - erase extra part */ + vm_newmemop(LMP_VM_REALLOC, (char*) ptr + nsize, LUA_TFREE, osize - nsize); + } + } + } + updatecounters(LMP_REALLOC, nsize - osize, 0); + } + return p; +} + +/* STATIC FUNCTIONS */ +static void initcounters() { + ac_string=0;ac_function=0;ac_userdata=0;ac_thread=0;ac_table=0;ac_other=0; + nallocs=0;alloc_size=0; + nreallocs=0;realloc_size=0; + nfrees=0;free_size=0; + memoryuse=0;maxmemoryuse=0; +} + +/* check alloctype and update counters accordingly */ +static void updatecounters (int alloctype, size_t size, size_t luatype) { + if (alloctype == LMP_FREE) { + nfrees = nfrees + 1; + free_size = free_size + size; + memoryuse = memoryuse - size; + } else if (alloctype == LMP_REALLOC) { + nreallocs = nreallocs + 1; + realloc_size = realloc_size + size; + memoryuse = memoryuse + size; + if (memoryuse > maxmemoryuse) { + maxmemoryuse = memoryuse; + } + } else if (alloctype == LMP_MALLOC) { + nallocs = nallocs + 1; + alloc_size = alloc_size + size; + memoryuse = memoryuse + size; + if (memoryuse > maxmemoryuse) { + maxmemoryuse = memoryuse; + } + switch(luatype) { + case LUA_TSTRING: + ac_string++; + break; + case LUA_TFUNCTION: + ac_function++; + break; + case LUA_TUSERDATA: + ac_userdata++; + break; + case LUA_TTHREAD: + ac_thread++; + break; + case LUA_TTABLE: + ac_table++; + break; + default: + ac_other++; + } + } +} + +/* +** writes the report in the standard output. If not usegraphics, calculates +** program memory usage and sugest memory consumption parameter for future +** execution. + */ +static void generatereport() { + float mem = ((float) (Maddress - Laddress) / 1000000) + 0.1; + + if (!usegraphics) + mem = mem + 0.4; /* empiric size of graphic mem usage */ + +printf("===================================================================\n"); +printf("Number of Mallocs=%ld\tTotal Malloc Size=%ld\n", nallocs, alloc_size); +printf("Number of Reallocs=%ld\tTotal Realloc Size=%ld\n", nreallocs, realloc_size); +printf("Number of Frees=%ld\tTotal Free Size=%ld\n", nfrees, free_size); +printf("\nNumber of Allocs of Each Type:\n"); +printf(" String=%d | Function=%d | Userdata=%d | Thread=%d | Table=%d | Other=%d\n", ac_string, ac_function, ac_userdata, ac_thread, ac_table, ac_other); +printf("\nMaximum Memory Used=%ld bytes\n", maxmemoryuse); + + if (!usegraphics && nallocs > 0) { +printf("\nWe suggest you run the application again using %.1f as parameter\n", mem); + } +printf("===================================================================\n"); +} + diff --git a/src/lmp.h b/src/lmp.h new file mode 100644 index 0000000..2e6aeee --- /dev/null +++ b/src/lmp.h @@ -0,0 +1,39 @@ +/* +** +** Author: Pablo Musa +** Creation Date: may 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** This module is responsible by defining the new allocation function and +** by collecting all information about memory management. +** At the end of the program execution or when luamemprofiler.stop() is +** called, it generates a log containing several memory information. +** +*/ + +#ifndef LMP_LMP_H +#define LMP_LMP_H + +/* +** Initializes the counters, sets the lowest address of the heap and +** enables/disables the use of the graphic module (vm_start). +*/ +void lmp_start (int lowestaddress, float memused, int usegraphics); + +/* +** Finalizes the counters, free all blocks structures, stop the graphic +** module (vm_stop) [if started] and generates the report (number of: mallocs, +** frees, tables, ...). +*/ +void lmp_stop (); + +/* +** Checks the alloc type (malloc, free, realloc) and update data in +** accordance. Create, remove or update block structures, update report +** counters (mallocs, tables, etc.) and call vm_newmemop if graphic +** module is enabled. +*/ +void *lmp_alloc (void *ud, void *ptr, size_t osize, size_t nsize); + +#endif diff --git a/src/lmp_struct.c b/src/lmp_struct.c new file mode 100644 index 0000000..9fe8927 --- /dev/null +++ b/src/lmp_struct.c @@ -0,0 +1,206 @@ +/* +** +** Author: Pablo Musa +** Creation Date: aug 16 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** See lmp_struct.h for module overview +** +*/ + + +#include +#include "lmp_struct.h" +#include "lua.h" + +#define HASH_SIZE 23 /* empiric hash size - need more tests to confirm */ + + +/* simple hash function */ +static int hashfunc(void *ptr) { + return ((uintptr_t) ptr) % HASH_SIZE; +} + + +/* STATIC GLOBAL VARIABLE */ +static lmp_Block **lmp_head = NULL; /* hashtable for all blocks */ +static int usegraphics; + + +/* GLOBAL VARIABLES - filter lists */ +/* multiply linked lists used for type filtering. used only in graphic mode */ +lmp_Block *lmp_string = NULL; +lmp_Block *lmp_function = NULL; +lmp_Block *lmp_userdata = NULL; +lmp_Block *lmp_thread = NULL; +lmp_Block *lmp_table = NULL; +lmp_Block *lmp_other = NULL; +lmp_Block *lmp_all = NULL; /* used to redraw all blocks */ + + +void st_newhash(int usegraphic) { + int i; + usegraphics = usegraphic; + lmp_head = (lmp_Block **) malloc (HASH_SIZE * sizeof(lmp_Block*)); + for (i = 0; i < HASH_SIZE; i++) { + lmp_head[i] = NULL; + } +} + +void st_destroyhash() { + int i; + + for(i = 0; i < HASH_SIZE; i++) { + lmp_Block *p; + lmp_Block *head = lmp_head[i]; + while (head != NULL) { + p = head; + head = head->next; + free(p); + } + } + free(lmp_head); + + if (usegraphics) { + lmp_string = NULL; + lmp_function = NULL; + lmp_userdata = NULL; + lmp_thread = NULL; + lmp_table = NULL; + lmp_other = NULL; + lmp_all = NULL; + } +} + +lmp_Block *st_removeblock (void *ptr) { + lmp_Block *p, *ant = NULL; + int i = hashfunc(ptr); + for (p = lmp_head[i]; p != NULL; ant = p, p = p->next) { + if (p->ptr == ptr) { + if (ant == NULL) { + lmp_head[i] = p->next; + } else { + ant->next = p->next; + } + p->next = NULL; + + if (usegraphics) { + if (p->prevtype != NULL) { + p->prevtype->nexttype = p->nexttype; + } + if (p->nexttype != NULL) { + p->nexttype->prevtype = p->prevtype; + } + + if (p->prevall != NULL) { + p->prevall->nextall = p->nextall; + } + if (p->nextall != NULL) { + p->nextall->prevall = p->prevall; + } + } + + return p; + } + } + return NULL; +} + +void st_insertblock (lmp_Block *block) { + lmp_Block **type; + + int i = hashfunc(block->ptr); + block->next = lmp_head[i]; + lmp_head[i] = block; + + if (usegraphics) { + switch (block->luatype) { + case LUA_TSTRING: + type = &lmp_string; + break; + case LUA_TFUNCTION: + type = &lmp_function; + break; + case LUA_TUSERDATA: + type = &lmp_userdata; + break; + case LUA_TTHREAD: + type = &lmp_thread; + break; + case LUA_TTABLE: + type = &lmp_table; + break; + default: /* OTHER */ + type = &lmp_other; + break; + } + if (*type != NULL) { + (*type)->prevtype = block; + } + block->nexttype = *type; + block->prevtype = NULL; + *type = block; + + if (lmp_all != NULL) { + lmp_all->prevall = block; + } + block->nextall = lmp_all; + block->prevall = NULL; + lmp_all = block; + } +} + +void st_initblock (lmp_Block *block, void *ptr, size_t size, size_t luatype) { + block->ptr = ptr; + block->size = size; + block->luatype = luatype; + block->next = NULL; + if (usegraphics) { + block->nexttype = NULL; + block->prevtype = NULL; + block->nextall = NULL; + block->prevall = NULL; + } +} + +void *st_getptr(lmp_Block *block) { + return block->ptr; +} + +size_t st_getsize(lmp_Block *block) { + return block->size; +} + +size_t st_getluatype(lmp_Block *block) { + return block->luatype; +} + +lmp_Block *st_getnext(lmp_Block *block) { + return block->next; +} + +lmp_Block *st_getnexttype(lmp_Block *block) { + return usegraphics ? block->nexttype : NULL; +} + +lmp_Block *st_getprevtype(lmp_Block *block) { + return usegraphics ? block->prevtype : NULL; +} + +lmp_Block *st_getnextall(lmp_Block *block) { + return usegraphics ? block->nextall : NULL; +} + +lmp_Block *st_getprevall(lmp_Block *block) { + return usegraphics ? block->prevall : NULL; +} + +void st_setsize(lmp_Block *block, size_t size) { + block->size = size; +} + +void st_setptr(lmp_Block *block, void *ptr) { + block->ptr = ptr; +} + diff --git a/src/lmp_struct.h b/src/lmp_struct.h new file mode 100644 index 0000000..bc5238e --- /dev/null +++ b/src/lmp_struct.h @@ -0,0 +1,89 @@ +/* +** +** Author: Pablo Musa +** Creation Date: aug 16 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** This module is responsible by defining the block structure used keep +** information of each allocation and by implementing data structures to +** hold these blocks. The main data structure is a hash table with predefined +** size and separate chaining with list heads. There are other seven multiply +** linked lists used for type filtering. However these lists are used just in +** the graphic module and do not produce overhead when graphics are disabled. +** +*/ + + +#ifndef LMP_LMPSTRUCT_H +#define LMP_LMPSTRUCT_H + + +#include + + +/* +** Holds memory address, size and type of each block allocated. +** Can have connection with 3 structures (hash table, type list and all list). +** The hash table is the module main structure, the 'type list' is the list +** where all blocks of a specific types are linked. The 'all list' is a list +** where all blocks are sequentially linked. +*/ +struct lmp_block { + void *ptr; + size_t size; + size_t luatype; + struct lmp_block *next; + struct lmp_block *nexttype; + struct lmp_block *prevtype; + struct lmp_block *nextall; + struct lmp_block *prevall; +}; +typedef struct lmp_block lmp_Block; + + +/* +** Sets global usegraphics, malloc and initialize the hash table. +*/ +void st_newhash(int usegraphic); + +/* +** Destroy and free the hash table and if usegraphics reset filter lists. +*/ +void st_destroyhash(); + +/* +** Searches for a block with specified ptr address. If the block is found, +** removes the block from the hash table. If usegraghics, also removes +** the block from his specific 'filter list' and from 'all list'. +*/ +lmp_Block *st_removeblock (void *ptr); + +/* +** Inserts the specified block into the hash table. If usegraphics, also +** inserts the block into his specific 'filter list' and into 'all list'. +*/ +void st_insertblock (lmp_Block *block); + +/* +** Initializes the specified block with the specified values and sets the +** hash table pointer to NULL. If usegraphics, initialize the other pointers. +*/ +void st_initblock (lmp_Block *block, void *ptr, size_t nsize, size_t luatype); + +/* +** Gets and Sets. +*/ +void *st_getptr(lmp_Block *block); +size_t st_getsize(lmp_Block *block); +size_t st_getluatype(lmp_Block *block); +lmp_Block *st_getnext(lmp_Block *block); +lmp_Block *st_getnexttype(lmp_Block *block); +lmp_Block *st_getprevtype(lmp_Block *block); +lmp_Block *st_getnextall(lmp_Block *block); +lmp_Block *st_getprevall(lmp_Block *block); + +void st_setsize(lmp_Block *block, size_t size); +void st_setptr(lmp_Block *block, void *ptr); + +#endif diff --git a/src/luamemprofiler.c b/src/luamemprofiler.c new file mode 100644 index 0000000..cdc263e --- /dev/null +++ b/src/luamemprofiler.c @@ -0,0 +1,151 @@ +/* +** Author: Pablo Musa +** Creation Date: mar 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** This module is responsible for registering the luamemprofiler lib in the +** Lua environment. It also sets a finalizer for the luamemprofiler library +** which restores the lua_State original function when the library is garbage +** collected. +** The library implements two main functions (start and stop). +** The start function receives an optional parameter (a number containing +** the expected memory consumption) which determines if the library will +** display real-time information and the granularity of the blocks. +** + */ + +#include +#include +#include +#include +#include + +#include "lmp.h" + +/* Keeps the default allocation function and the ud of a lua_State */ +typedef struct lmp_allocstructure { + lua_Alloc f; + void *ud; +} lmp_Alloc; + +/* +** Called when main program ends. +** Restores lua_State original allocation function. +*/ +static int finalize (lua_State *L) { + lmp_Alloc *s; + + /* check lmp_Alloc */ + if (!lua_isuserdata(L, -1)) { + lua_pushstring(L, "incorrect argument"); + lua_error(L); + } + + /* get lmp_Alloc and restore original allocation function */ + s = (lmp_Alloc *) lua_touserdata(L, -1); + if (s->f != lua_getallocf (L, NULL)) { + lua_setallocf(L, s->f, s->ud); + lmp_stop(); + } + + return 0; +} + +/* Register finalize function as metatable */ +static void create_finalizer(lua_State *L, lua_Alloc f, void *ud) { + lmp_Alloc *s; + + /* create metatable with finalize function (__gc field) */ + luaL_newmetatable(L, "luamemprofiler_mt"); + lua_pushcfunction(L, finalize); + lua_setfield(L, -2, "__gc"); + + /* create 'alloc' userdata (one ud for each Lua_State) */ + s = (lmp_Alloc*) lua_newuserdata(L, (size_t) sizeof(lmp_Alloc)); + s->f = f; + s->ud = ud; + + /* set userdata metatable */ + luaL_setmetatable(L, "luamemprofiler_mt"); + + /* insert userdata into registry table so it cannot be collected */ + lua_setfield(L, LUA_REGISTRYINDEX, "luamemprofiler_ud"); +} + +/* Main module function. Starts the library */ +static int luamemprofiler_start(lua_State *L) { + static lua_Alloc f; + static void *ud; + + float memused; + int usegraphics = 0; + + /* get the amount of memory expected to be used AND set enable graphics */ + memused = (float) lua_tonumber(L, 1); + if (memused) + usegraphics = 1; + + /* get default allocation function */ + f = lua_getallocf(L, &ud); + + /* check if start has been called before */ + if (f == lmp_alloc) { + /* restore default allocation function and remove library finalizer */ + lmp_Alloc *s; + lua_getfield(L, LUA_REGISTRYINDEX, "luamemprofiler_ud"); + s = (lmp_Alloc *) lua_touserdata(L, -1); + lua_setallocf(L, s->f, s->ud); + lua_getmetatable(L, -1); + lua_pushnil(L); + lua_setfield(L, -2, "__gc"); + + lua_pushstring(L, "calling luamemprofiler start function twice"); + lua_error(L); + } + + /* create data_structure and set finalizer */ + create_finalizer(L, f, ud); + lua_setallocf(L, lmp_alloc, ud); + + /* L is in most cases the lowest address of the heap (easiest to access) */ + lmp_start((uintptr_t) L, memused, usegraphics); + return 0; +} + +/* restore default allocation function and stop the other modules */ +static int luamemprofiler_stop(lua_State *L) { + lmp_Alloc *s; + + /* get 'alloc' userdata and restore original allocation function */ + lua_pushstring(L, "luamemprofiler_ud"); + lua_rawget(L, LUA_REGISTRYINDEX); + s = (lmp_Alloc*) lua_touserdata(L, -1); + if (s == NULL) { + lua_pushstring(L, "calling luamemprofiler stop function without calling start function"); + lua_error(L); + } + lua_pop(L, 1); + lua_setallocf(L, s->f, s->ud); + + lmp_stop(); + return 0; +} + + +/********************************** + * register structs and functions * + **********************************/ + +/* luamemprofiler function registration array */ +static const luaL_Reg luamemprofiler[] = { + { "start", luamemprofiler_start}, + { "stop", luamemprofiler_stop}, + { NULL, NULL } +}; + +/* register luamemprofiler functions */ +LUALIB_API int luaopen_luamemprofiler (lua_State *L) { + luaL_newlib(L, luamemprofiler); + return 1; +} diff --git a/src/vmemory.c b/src/vmemory.c new file mode 100644 index 0000000..d843cde --- /dev/null +++ b/src/vmemory.c @@ -0,0 +1,707 @@ +/* +** +** Author: Pablo Musa +** Creation Date: mar 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** See vmemory.h for module overview +** +*/ + + +#include +#include +#include +#include +#include +#include + + +#include "graphic.h" +#include "vmemory.h" +#include "lmp_struct.h" +#include "lmp.h" + + +#define WINDOW_TITLE "luamemprofiler v1.0" +#define ICON_PATH "logo.bmp" + +/* screen state */ +#define LMP_PAUSE 0 +#define LMP_EXEC 1 +#define LMP_FINISH -1 +#define LMP_ZOOM_OUT 0 +#define LMP_ZOOM_IN 1 + +#define MIN_mb_SIZE 1 +#define MIN_mb_WIDTH 800 +#define MIN_mb_HEIGHT 600 + +#define BOX_XINI 10 /* MINIMUM = 10 */ +#define BOX_YINI 10 /* MINIMUM = 10 */ +#define BOX_XEND (mb_width + BOX_XINI) +#define BOX_YEND (mb_height + BOX_YINI) + +#define RTCOLUMN_WIDTH 150 /* right column width */ +#define BTROW_HEIGHT 70 /* bottom row height */ + +#define BASE_SPACE 10 /* space for separating text from the box */ +#define BOX_BORDER 3 /* memory box border width */ + +#define LMP_FLINE 0 /* bottom row starts writing in first line (0) */ +#define LMP_ON 1 +#define LMP_OFF 0 + + +/* filter type menu - positions to draw, bool toggled, draw Color, and text */ +struct menuitem { + int x; + int y; + int toggle; + Color color; + const char *name; +}; +typedef struct menuitem LMP_Menuitem; + + +/* GLOBAL VARIABLES */ +/* one block list for each filter type */ +extern lmp_Block *lmp_string; +extern lmp_Block *lmp_function; +extern lmp_Block *lmp_userdata; +extern lmp_Block *lmp_thread; +extern lmp_Block *lmp_table; +extern lmp_Block *lmp_other; +extern lmp_Block *lmp_all; + +/* one menu for each filter type */ +static LMP_Menuitem mi_string; +static LMP_Menuitem mi_function; +static LMP_Menuitem mi_userdata; +static LMP_Menuitem mi_thread; +static LMP_Menuitem mi_table; +static LMP_Menuitem mi_other; + +/* STATIC GLOBAL VARIABLES */ +static Screen *screen; +static uintptr_t laddress; /* first address of the program */ +static uintptr_t baseaddr; /* base address of the memory box */ +static int state = LMP_PAUSE; /* luamemprofiler state (paused or executing) */ +static int zoom = LMP_ZOOM_OUT; /* zoom state (in or out) */ + +static int sc_width; /* sc = screen */ +static int sc_height; +static int mb_width; /* mb = memory box */ +static int mb_height; +static int BYTES_PER_PIXEL = 4; +static int BLOCK_HEIGHT = 2; + +static int statesy; /* used to update the display with state and zoom */ + +/* STATIC FUNCTIONS */ +static void drawbtrow(); +static void toggleall(); +static void drawstates(); +static void drawmembox(); +static void checkevent(); +static void clearmembox(); +static void untoggleall(); +static int istoggled(size_t luatype); +static void drawcallstack(int fline); +static void drawrtcolumn(float memused); +static int setcanvassize (float memused); +static void inverttoggle(LMP_Menuitem *mi); +static void drawmenuitem(LMP_Menuitem *item); +static void drawreport(const char *text, int line); +static void drawmemblock(int addr, size_t luatype, size_t size); +static void writeblockinfo(void *ptr,size_t luatype,size_t size,int alloctype); +static void calcmemdata(void *ptr, size_t size, int *reladdr, size_t *relsize); +static void initmenuitem(LMP_Menuitem *mi, int x, int y, int toggle, + Color color, const char* name); + +/* GLOBAL FUNCTIONS */ +void vm_start(int lowestaddress, float memused) { + /* if memused is very low uses default value and returns it */ + memused = setcanvassize (memused); + screen = gr_newscreen(sc_width, sc_height, ICON_PATH, WINDOW_TITLE); + laddress = lowestaddress; + baseaddr = laddress; + + gr_drawbackground(screen, LMP_VM_BACKGROUND_CL); + drawmembox(); + drawrtcolumn(memused); + drawbtrow(); + + checkevent(); +} + +void vm_stop() { + char dummy; + state = LMP_FINISH; + drawreport("Execution finished. The report is in the Terminal.", LMP_FLINE); + drawstates(); + printf("Press Enter To Finish!"); + scanf("%c", &dummy); + + gr_destroyscreen(screen); +} + +void vm_newmemop(int memop, void *ptr, size_t luatype, size_t size) { + int p; + size_t mb_size; + calcmemdata(ptr, size, &p, &mb_size); /* calculate relative address */ + if (p > 0) { /* check if is a valid address */ + /* draw full block breaking lines if necessary */ + if (istoggled(luatype)) + drawmemblock(p, luatype, mb_size); + if (state == LMP_PAUSE) { + /* write in 'bottom row' block information(parameters) and call stack */ + writeblockinfo(ptr, luatype, size, memop); + } + /* check if there is any valid event and treat it */ + checkevent(); + } + /* DEBUG else { possible print if block is smaller than baseaddress + printf("Lower Pointer ptr=%d laddr=%d p=%d\n", (int) ptr, laddress, p); + } */ +} + +/* STATIC FUNCTIONS */ + +/* uses baseaddress to calculate the memory box position of a block */ +static void calcmemdata(void *ptr, size_t size, int *reladdr, size_t *relsize) { + *reladdr = ((uintptr_t) ptr - baseaddr) / BYTES_PER_PIXEL; + *relsize = (size / BYTES_PER_PIXEL); + if (*relsize == 0) { + *relsize = 1; + } +} + +/* draw specified text in report area (botton row) */ +static void drawreport(const char *text, int line) { + int x = BOX_XINI; + int y = BOX_YEND + BASE_SPACE + (line * gr_gettextheight(screen)); + + /* if line does not fit screen size, omit line */ + if (y > sc_height - gr_gettextheight(screen)) { + return; + } + + /* if line is first line, new message -> clear bottom row */ + if (line == LMP_FLINE) { + gr_setdrawcolor(screen, LMP_VM_BACKGROUND_CL); + gr_drawblock(screen, BOX_XINI, BOX_XEND, BOX_YEND+BASE_SPACE, BTROW_HEIGHT); + } + + /* validate text size */ + if (strlen(text) > (mb_width / gr_gettextwidth(screen))) { + gr_drawtext(screen, "Sorry, but the text is too large.", x, y); + } else { + gr_drawtext(screen, text, x, y); + } +} + +/* draw up to 3 levels of the call stack */ +static void drawcallstack(int fline) { + char textbuff[80] = ""; + int res, i = 0; + lua_Debug ld; + lua_State *L = (lua_State *) laddress; + res = lua_getstack(L, i, &ld); + while (res == 1 && i < (BTROW_HEIGHT/gr_gettextwidth(screen))) { + int r; + r = lua_getinfo(L, "lnS", &ld); + if (r != 0) { + int line = LMP_FLINE + i + fline; /* start in fline */ + if (strcmp(ld.what, "main") == 0) { /* main program execution */ + sprintf(textbuff, "Main - line:%d", ld.currentline); + drawreport(textbuff, line); + break; + } + if (strcmp(ld.what, "Lua") == 0) { /* some function execution */ + sprintf(textbuff, "Lua - file:'%s' func:'%s' field:'%s' line:%d", + ld.short_src, ld.name, ld.namewhat, ld.currentline); + drawreport(textbuff, line); + } + if (strcmp(ld.what, "C") == 0) { /* some C function execution */ + sprintf(textbuff, "C - func'%s' field:'%s'", ld.name, ld.namewhat); + drawreport(textbuff, line); + } + } else { + printf("luamemprofiler internal error: vmemory -> debuginfo -> invalid what.\n"); + exit(EXIT_FAILURE); + } + i++; + res = lua_getstack(L, i, &ld); + } +} + +/* draw blocks in the correct place (recursive calls for big blocks) */ +static void drawmemblock(int addr, size_t luatype, size_t size) { + int x, y; + x = (addr % mb_width) + BOX_XINI; + y = ((addr / mb_width) * BLOCK_HEIGHT) + BOX_YINI; + + /* just draw into valid areas */ + if (y >= BOX_YINI && (y + BLOCK_HEIGHT) <= BOX_YEND) { + if (x + size > BOX_XEND){ /* break block to fit membox */ + size_t extrasize = size - (BOX_XEND + 1 - x); /* +1 -> XEND is valid */ + size = size - extrasize; + + /* recursive call for breaking one block in different lines */ + drawmemblock(addr + (size - 1), luatype, extrasize); /* -1 -> XEND */ + } + + switch(luatype) { + case LUA_TSTRING: + gr_setdrawcolor(screen, LMP_VM_STRING_CL); + break; + case LUA_TFUNCTION: + gr_setdrawcolor(screen, LMP_VM_FUNCTION_CL); + break; + case LUA_TUSERDATA: + gr_setdrawcolor(screen, LMP_VM_USERDATA_CL); + break; + case LUA_TTHREAD: + gr_setdrawcolor(screen, LMP_VM_THREAD_CL); + break; + case LUA_TTABLE: + gr_setdrawcolor(screen, LMP_VM_TABLE_CL); + break; + case LUA_TFREE: + gr_setdrawcolor(screen, LMP_VM_FREE_CL); + break; + default: + gr_setdrawcolor(screen, LMP_VM_OTHER_CL); + } + + /* block length is size, so draw from x to "size-1" */ + gr_drawblock(screen, x, x + size - 1, y, BLOCK_HEIGHT); + } + /* DEBUG else { possible use when all blocks have to be drawn + printf("draw block (%d, %d) addr = %d\n", x, y, addr); } */ +} + +/* +** traverse a filter list drawing all blocks. 'block' is the first block in the +** list and 'fnextblock' is a function that returns the next block in the list +** or NULL +*/ +void drawblocklist(lmp_Block *block, lmp_Block* (*fnextblock) (lmp_Block*)) { + int p; + size_t mb_size; + while (block != NULL) { /* list is not empty */ + calcmemdata(block->ptr, st_getsize(block), &p, &mb_size); + if (p > 0) { + int luatype = istoggled(block->luatype) ? block->luatype : LUA_TFREE; + drawmemblock(p, luatype, mb_size); + } + block = fnextblock(block); + } +} + +/* +** redraw blocks using bigger size and new baseaddress. The new base address is +** calculated using 'y' coordinate. All blocks above this 'y' (y included) are +** redrawn. +*/ +static void zoomin(int x, int y) { + int p; + size_t mb_size; + lmp_Block *block; + + zoom = LMP_ZOOM_IN; /* change zoom state */ + drawstates(); /* update display with new state */ + clearmembox(); /* clear memory box for new blocks in zoom mode */ + + /* calculates baseaddress using old baseaddress and 'y' */ + baseaddr = (((y - BOX_YINI) / BLOCK_HEIGHT) * (mb_width) + * BYTES_PER_PIXEL) + baseaddr; + BYTES_PER_PIXEL = BYTES_PER_PIXEL / 2; /* width 2x bigger */ + BLOCK_HEIGHT = BLOCK_HEIGHT * 2; /* height 2x bigger */ + for(block = lmp_all; block != NULL; block = st_getnextall(block)) { + /* calculates new block values in memry box (relative address and size) */ + p = ((uintptr_t) block->ptr - baseaddr) / BYTES_PER_PIXEL; + mb_size = (block->size / BYTES_PER_PIXEL); + if (mb_size == 0) { + mb_size = 1; + } + /* check if block is inside zoom */ + if (istoggled(block->luatype) && p >= 0 && + p <= (mb_width * BYTES_PER_PIXEL) * (mb_height / BLOCK_HEIGHT)) { + drawmemblock(p, block->luatype, mb_size); + } + } +} + +/* redraw blocks using smaller size and old baseaddress. */ +static void zoomout() { + zoom = LMP_ZOOM_OUT; /* change zoom state */ + drawstates(); /* update display with new state */ + clearmembox(); /* clear memory box for new blocks without zoom mode */ + + BYTES_PER_PIXEL = BYTES_PER_PIXEL * 2; /* width 2x smaller */ + BLOCK_HEIGHT = BLOCK_HEIGHT / 2; /* height 2x smaller */ + + baseaddr = laddress; /* restore baseaddress */ + drawblocklist(lmp_all, st_getnextall); /* draw all blocks */ +} + +static void checkevent() { + int eventtype; + LMP_Event event; + + if (state == LMP_EXEC) { /* normal execution - only accepts pause command */ + eventtype = gr_getevent(screen, &event); /* gets an event if exists */ + if (eventtype == LMP_EVENT_KEY && event.kevent.key == ' ') { /* pause */ + state = LMP_PAUSE; + drawstates(); /* update display with new state */ + drawreport("Press: 'space' to resume execution; 'n' to resume until next memory operation;", LMP_FLINE); + drawreport("'c' to clear the memory box; 's,f,u,h,t,o' to redraw blocks of specific type;", LMP_FLINE + 1); + drawreport("'a' to redraw all blocks; left-click for zoom in and right-click for zoom out.", LMP_FLINE + 2); + } + } + + while (state != LMP_EXEC) { /* execution is paused or finished */ + lmp_Block* (*fnextblock) (lmp_Block*) = st_getnexttype; + lmp_Block *block = NULL; + + eventtype = gr_waitevent(screen, &event); /* wait for a valid event */ + if (eventtype == LMP_EVENT_KEY) { + switch (event.kevent.key) { + case ' ': /* space key - continue - resumes normal execution */ + state = LMP_EXEC; + drawstates(); + drawreport("Press 'space' to Pause execution.", LMP_FLINE); + return; + case 'n': /* next - execute next memory operation */ + return; + case 's': /* draw filter type - set correct list and toggle */ + inverttoggle(&mi_string); + block = lmp_string; + break; + case 'f': + inverttoggle(&mi_function); + block = lmp_function; + break; + case 'u': + inverttoggle(&mi_userdata); + block = lmp_userdata; + break; + case 'h': + inverttoggle(&mi_thread); + block = lmp_thread; + break; + case 't': + inverttoggle(&mi_table); + block = lmp_table; + break; + case 'o': + inverttoggle(&mi_other); + block = lmp_other; + break; + case 'a': /* draw all blocks */ + toggleall(); + block = lmp_all; + fnextblock = st_getnextall; + break; + case 'c': /* erase all blocks from memory box */ + untoggleall(); + clearmembox(); + } + drawblocklist(block, fnextblock); + } else if (eventtype == LMP_EVENT_MOUSE) { + if (event.mevent.b == LEFT_BUTTON && zoom == LMP_ZOOM_OUT) { + zoomin(event.mevent.x, event.mevent.y); + } else if (event.mevent.b == RIGHT_BUTTON && zoom == LMP_ZOOM_IN) { + zoomout(); + } + } + } +} + +/* +** write in bottom row the memory operation and the block info. +** block info = allocation type, block (address, type and size) and call stack +*/ +static void writeblockinfo(void *ptr, size_t luatype, size_t size, int alloctype) { + char textbuff[60]; + char ltype[9]; + char atype[8]; + switch(luatype) { + case LUA_TSTRING: + strcpy(ltype, "String"); + break; + case LUA_TFUNCTION: + strcpy(ltype, "Function"); + break; + case LUA_TUSERDATA: + strcpy(ltype, "Userdata"); + break; + case LUA_TTHREAD: + strcpy(ltype, "Thread"); + break; + case LUA_TTABLE: + strcpy(ltype, "Table"); + break; + default: + strcpy(ltype, "Other"); + break; + } + switch(alloctype) { + case LMP_VM_FREE: + strcpy(atype, "Free"); + break; + case LMP_VM_MALLOC: + strcpy(atype, "Malloc"); + break; + case LMP_VM_REALLOC: + strcpy(atype, "Realloc"); + break; + } + + sprintf(textbuff, "%s | addr = %p | type = %s | size = %luB", atype, + ptr, ltype, (unsigned long) size); + drawreport(textbuff, LMP_FLINE); + drawcallstack(LMP_FLINE + 1); +} + +/* draw border lines and clear memory box */ +static void drawmembox() { + int i; + + gr_setdrawcolor(screen, BLACK); + for (i = 1; i <= BOX_BORDER; i++) { + int x1 = BOX_XINI - i, x2 = BOX_XEND + i; + int y1 = BOX_YINI - i, y2 = BOX_YEND + i; + gr_drawline(screen, x1, y1, x2, y1); + gr_drawline(screen, x1, y2, x2, y2); + gr_drawline(screen, x1, y1, x1, y2); + gr_drawline(screen, x2, y1, x2, y2); + } + clearmembox(); +} + +/* draw one item of the right column */ +static void drawmenuitem(LMP_Menuitem *item) { + int offset = gr_gettextwidth(screen) * strlen(item->name) + 3; + int bx = item->x + offset; + int by = item->y + 4; + gr_settextcolor(screen, BLACK); + gr_drawtext(screen, item->name, item->x, item->y); + if (item->toggle) { + gr_setdrawcolor(screen, item->color); + gr_drawblock(screen, bx, bx + 10, by, 10); + } else { + gr_setdrawcolor(screen, LMP_VM_BACKGROUND_CL); + gr_drawblock(screen, bx, bx + 10, by, 10); + + gr_setdrawcolor(screen, item->color); + gr_drawline(screen, bx, by, bx + 10, by); + gr_drawline(screen, bx, by, bx, by + 10); + gr_drawline(screen, bx, by+ 10, bx + 10, by + 10); + gr_drawline(screen, bx + 10, by, bx + 10, by + 10); + } +} + +/* write a line division(black) and a label(red) in (x,y) coordinate */ +static int drawl(const char *text, int x, int y) { + int text_width = strlen(text) * gr_gettextwidth(screen); + int center_offset = (RTCOLUMN_WIDTH - (text_width + BASE_SPACE))/2; + + gr_setdrawcolor(screen, BLACK); + gr_drawline(screen, x, y, sc_width - BASE_SPACE, y); + y = y + 10; + + gr_settextcolor(screen, RED); + gr_drawtext(screen, text, x + center_offset, y); + return y; +} + +/* write initial message in the bottom row */ +static void drawbtrow() { + gr_settextcolor(screen, BLACK); + drawreport("Welcome to the luamemprofiler library. Press 'space' to run the program", LMP_FLINE); + drawreport("normally or 'n' to execute the program until next memory operation.", LMP_FLINE + 1); +} + +/* draw right column, including initial states (paused, zoom in) */ +static void drawrtcolumn(float memused) { + int x = BOX_XEND + BOX_BORDER + BASE_SPACE; + int y = BOX_YINI; + int offset = 30; + char textbuff[15]; + + /* draw basic information */ + y = drawl("BASIC INFO", x, y); + y = y + offset; + + gr_settextcolor(screen, BLACK); + gr_drawtext(screen, "Memory Size", x, y); + y = y + gr_gettextwidth(screen) + 5; + sprintf(textbuff, "%.1fMb", memused); + gr_drawtext(screen, textbuff, x, y); + y = y + offset; + + gr_drawtext(screen, "Granularity", x, y); + y = y + gr_gettextwidth(screen) + 5; + sprintf(textbuff, "1x2 px = %dB", BYTES_PER_PIXEL); + gr_drawtext(screen, textbuff, x, y); + y = y + offset; + + /* draw key menu and labels */ + y = drawl("COMMANDS", x, y); + y = y + offset; + + gr_settextcolor(screen, BLACK); + initmenuitem(&mi_string, x, y, LMP_ON, LMP_VM_STRING_CL, "s - String"); + drawmenuitem(&mi_string); + y = y + offset; + initmenuitem(&mi_function, x, y, LMP_ON, LMP_VM_FUNCTION_CL, "f - Function"); + drawmenuitem(&mi_function); + y = y + offset; + initmenuitem(&mi_userdata, x, y, LMP_ON, LMP_VM_USERDATA_CL, "u - Userdata"); + drawmenuitem(&mi_userdata); + y = y + offset; + initmenuitem(&mi_thread, x, y, LMP_ON, LMP_VM_THREAD_CL, "h - Thread"); + drawmenuitem(&mi_thread); + y = y + offset; + initmenuitem(&mi_table, x, y, LMP_ON, LMP_VM_TABLE_CL, "t - Table"); + drawmenuitem(&mi_table); + y = y + offset; + initmenuitem(&mi_other, x, y, LMP_ON, LMP_VM_OTHER_CL, "o - Other"); + drawmenuitem(&mi_other); + y = y + offset; + gr_drawtext(screen, "a - All", x, y); + y = y + offset; + gr_drawtext(screen, "c - Clear", x, y); + y = y + offset; + gr_drawtext(screen, "n - Next", x, y); + y = y + offset; + + /* draw luamemprofiler state and zoom state */ + y = drawl("STATE", x, y); + y = y + offset; + + statesy = y; /* set where to redraw states */ + gr_settextcolor(screen, BLACK); + gr_drawtext(screen, "lmp: PAUSED", x, y); + y = y + offset; + gr_drawtext(screen, "zoom: OUT", x, y); +} + +/* update states (pause x execution || [zoom] in x out */ +static void drawstates() { + int x = BOX_XEND + BOX_BORDER + BASE_SPACE; + int y = statesy; + int offset = 30; + + /* erase old text */ + gr_setdrawcolor(screen, LMP_VM_BACKGROUND_CL); + gr_drawblock(screen, x, x + RTCOLUMN_WIDTH, y, 50); + + /* write new text */ + gr_settextcolor(screen, BLACK); + if(state == LMP_PAUSE) { + gr_drawtext(screen, "lmp: PAUSED", x, y); + } else if (state == LMP_EXEC) { + gr_drawtext(screen, "lmp: EXECUTING", x, y); + } else if (state == LMP_FINISH) { + gr_drawtext(screen, "lmp: FINISHED", x, y); + } + y = y + offset; + + if(zoom == LMP_ZOOM_OUT) { + gr_drawtext(screen, "zoom: OUT", x, y); + } else if(zoom == LMP_ZOOM_IN) { + gr_drawtext(screen, "zoom: IN", x, y); + } + +} + +/* calculates and sets screen and memory box width and height */ +static int setcanvassize (float memused) { + if (memused <= (float) MIN_mb_SIZE) { + mb_width = MIN_mb_WIDTH; + mb_height = MIN_mb_HEIGHT; + memused = MIN_mb_SIZE; + } else { /* MAX MEM FOR 800 x 600 resolution */ + int side; + if (memused > 1) { + BYTES_PER_PIXEL = ((int) memused) * 4; + } + /* (1Mb * BLOCK_HEIGHT / BYTES_PER_PIXEL / PROPORTION(4:3)) */ + side = (int) sqrt(memused*1000000*BLOCK_HEIGHT/BYTES_PER_PIXEL/(4*3))+1; + mb_width = side * 4; + mb_height = side * 3; + } + sc_width = BOX_XINI + mb_width + BOX_BORDER + BASE_SPACE + RTCOLUMN_WIDTH; + sc_height = BOX_YINI + mb_height + BOX_BORDER + BASE_SPACE + BTROW_HEIGHT; + return memused; +} + +/* paint all memory box with defined color (erase drawn blocks) */ +static void clearmembox() { + gr_setdrawcolor(screen, LMP_VM_MEMBOX_CL); + gr_drawblock(screen, BOX_XINI, BOX_XEND, BOX_YINI, BOX_YEND - BOX_YINI); +} + +static int istoggled(size_t luatype) { + switch(luatype) { + case LUA_TSTRING: + return mi_string.toggle; + case LUA_TFUNCTION: + return mi_function.toggle; + case LUA_TUSERDATA: + return mi_userdata.toggle; + case LUA_TTHREAD: + return mi_thread.toggle; + case LUA_TTABLE: + return mi_table.toggle; + default: + return mi_other.toggle; + } +} + +static void toggleall() { + mi_string.toggle = LMP_ON; + drawmenuitem(&mi_string); + mi_function.toggle = LMP_ON; + drawmenuitem(&mi_function); + mi_userdata.toggle = LMP_ON; + drawmenuitem(&mi_userdata); + mi_thread.toggle = LMP_ON; + drawmenuitem(&mi_thread); + mi_table.toggle = LMP_ON; + drawmenuitem(&mi_table); + mi_other.toggle = LMP_ON; + drawmenuitem(&mi_other); +} + +static void untoggleall() { + mi_string.toggle = LMP_OFF; + drawmenuitem(&mi_string); + mi_function.toggle = LMP_OFF; + drawmenuitem(&mi_function); + mi_userdata.toggle = LMP_OFF; + drawmenuitem(&mi_userdata); + mi_thread.toggle = LMP_OFF; + drawmenuitem(&mi_thread); + mi_table.toggle = LMP_OFF; + drawmenuitem(&mi_table); + mi_other.toggle = LMP_OFF; + drawmenuitem(&mi_other); +} + +static void initmenuitem(LMP_Menuitem *mi, int x, int y, int toggle, + Color color, const char* name) { + mi->x = x; mi->y = y; mi->toggle = toggle; + mi->color = color; mi->name = name; +} + +/* change toggle settings and redraw menu item */ +static void inverttoggle(LMP_Menuitem *mi) { + mi->toggle = !mi->toggle; + drawmenuitem(mi); +} diff --git a/src/vmemory.h b/src/vmemory.h new file mode 100644 index 0000000..d7c8c46 --- /dev/null +++ b/src/vmemory.h @@ -0,0 +1,57 @@ +/* +** +** Author: Pablo Musa +** Creation Date: mar 27 2011 +** Last Modification: aug 22 2011 +** See Copyright Notice in COPYRIGHT +** +** This module manages the graphical display offered by the library. It defines +** the size of each element: right column, bottom row, memory box, box border, +** etc. It also dynamically calculates: block's bytes per pixel based on the +** memory consumption; the relative address of each pointer; the x, y +** coordinate to the block; the zoom in and zoom out. +** Finally, this module is responsible by managing the events and the +** corresponding actions. +** +*/ + +#ifndef LMP_VMEMORY_H +#define LMP_VMEMORY_H + +#include "graphic.h" + +#define LMP_VM_FREE 0 +#define LMP_VM_MALLOC 1 +#define LMP_VM_REALLOC 2 +#define LUA_TFREE 11 + +#define LMP_VM_STRING_CL DARKRED +#define LMP_VM_FUNCTION_CL DARKMAGENTA +#define LMP_VM_USERDATA_CL DARKGREEN +#define LMP_VM_THREAD_CL DARKORANGE +#define LMP_VM_TABLE_CL DARKBLUE +#define LMP_VM_OTHER_CL DIMGRAY +#define LMP_VM_FREE_CL WHITE + +#define LMP_VM_BACKGROUND_CL LTGRAY +#define LMP_VM_MEMBOX_CL WHITE + + +/* +** Calculates window size (based on expected memory consumption). +** Initializes the whole window (memory box, bottom row, right column). +*/ +void vm_start(int lowestaddress, float memused); + +/* +** Calls gr_destroyscreen to destroy the window. +*/ +void vm_stop(); + +/* +** Module main function. It is responsible by managing new memory operations +** and user events. +*/ +void vm_newmemop(int memop, void *ptr, size_t luatype, size_t size); + +#endif diff --git a/tests/out/wfc.txt b/tests/out/wfc.txt index 7d7ed38..ebc3e81 100644 --- a/tests/out/wfc.txt +++ b/tests/out/wfc.txt @@ -1,12 +1,12 @@ =================================================================== -Number of Mallocs=13984 Total Malloc Size=1179949 +Number of Mallocs=13982 Total Malloc Size=1179789 Number of Reallocs=12 Total Realloc Size=65520 -Number of Frees=3860 Total Free Size=369797 +Number of Frees=3857 Total Free Size=369551 Number of Allocs of Each Type: - String=6142 | Function=4 | Userdata=1 | Thread=0 | Table=2606 | Other=5231 + String=6142 | Function=4 | Userdata=1 | Thread=0 | Table=2606 | Other=5229 -Maximum Memory Used=875672 bytes +Maximum Memory Used=875758 bytes -We suggest you run the application again using 349.0 as parameter +We suggest you run the application again using -1212.4 as parameter ===================================================================