diff options
Diffstat (limited to 'lib/tools/c_src')
-rw-r--r-- | lib/tools/c_src/Makefile | 6 | ||||
-rw-r--r-- | lib/tools/c_src/Makefile.in | 239 | ||||
-rw-r--r-- | lib/tools/c_src/depend.mk | 17 | ||||
-rw-r--r-- | lib/tools/c_src/erl_memory.c | 2950 | ||||
-rw-r--r-- | lib/tools/c_src/erl_memory_trace_block_table.c | 761 | ||||
-rw-r--r-- | lib/tools/c_src/erl_memory_trace_block_table.h | 73 |
6 files changed, 4046 insertions, 0 deletions
diff --git a/lib/tools/c_src/Makefile b/lib/tools/c_src/Makefile new file mode 100644 index 0000000000..0cdc50e6f5 --- /dev/null +++ b/lib/tools/c_src/Makefile @@ -0,0 +1,6 @@ +# +# Invoke with GNU make or clearmake -C gnu. +# + +include $(ERL_TOP)/make/run_make.mk + diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in new file mode 100644 index 0000000000..e6b76e2238 --- /dev/null +++ b/lib/tools/c_src/Makefile.in @@ -0,0 +1,239 @@ +# ``The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved via the world wide web at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# The Initial Developer of the Original Code is Ericsson Utvecklings AB. +# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +# AB. All Rights Reserved.'' +# +# $Id$ +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/erts/include/internal/$(TARGET)/ethread.mk + +USING_MINGW=@MIXED_CYGWIN_MINGW@ +USING_VC=@MIXED_CYGWIN_VC@ + +CC=@CC@ +LD=@LD@ +AR=@AR@ +RANLIB=@RANLIB@ +RM=@RM@ +MKDIR=@MKDIR@ +INSTALL=@INSTALL@ +INSTALL_DIR=@INSTALL_DIR@ +INSTALL_DATA=@INSTALL_DATA@ +INSTALL_PROGRAM=@INSTALL_PROGRAM@ +LDFLAGS=@LDFLAGS@ + +ifeq ($(TYPE),debug) +CFLAGS = @DEBUG_CFLAGS@ -DDEBUG +TYPEMARKER=.debug +PRE_LD = +ifeq ($(TARGET),win32) +LDFLAGS += -g +endif +else +ifeq ($(TYPE),purify) +CFLAGS = @CFLAGS@ -DPURIFY +TYPEMARKER=.purify +PRE_LD = purify $(PURIFY_BUILD_OPTIONS) +else +ifeq ($(TYPE),quantify) +CFLAGS = @CFLAGS@ -DQUANTIFY +TYPEMARKER=.quantify +PRE_LD = quantify $(QUANTIFY_BUILD_OPTIONS) +else +ifeq ($(TYPE),purecov) +CFLAGS = @DEBUG_CFLAGS@ -DPURECOV +TYPEMARKER=.purecov +PRE_LD = purecov $(PURECOV_BUILD_OPTIONS) +else +override TYPE=opt +CFLAGS = @CFLAGS@ +PRE_LD = +TYPEMARKER = +endif +endif +endif +endif + +ifeq ($(findstring -D_GNU_SOURCE,$(CFLAGS)),) +THR_DEFS = $(ETHR_DEFS) +else +# Remove duplicate -D_GNU_SOURCE +THR_DEFS = $(filter-out -D_GNU_SOURCE%, $(ETHR_DEFS)) +endif + +LIBS=@LIBS@ +CREATE_DIRS= + +TT_DIR=$(TARGET)/$(TYPE) + +BIN_DIR=../bin/$(TARGET) +OBJ_DIR=../obj/$(TT_DIR) + +CREATE_DIRS += $(BIN_DIR) $(OBJ_DIR) + +PROGS= +DRIVERS= + + +# +# emem sources, objects, includes, libs, etc... +# + + +ifneq ($(strip $(ETHR_LIB_NAME)),) +# Need ethread package for emem +PROGS += $(BIN_DIR)/emem$(TYPEMARKER)@EXEEXT@ +endif + +EMEM_OBJ_DIR=$(OBJ_DIR)/emem +CREATE_DIRS += $(EMEM_OBJ_DIR) + +EMEM_INCLUDES = -I$(ERL_TOP)/erts/include \ + -I$(ERL_TOP)/erts/include/$(TARGET) \ + -I$(ERL_TOP)/erts/include/internal \ + -I$(ERL_TOP)/erts/include/internal/$(TARGET) + +EMEM_HEADERS = erl_memory_trace_block_table.h +EMEM_SRCS = erl_memory.c erl_memory_trace_block_table.c + +EMEM_CFLAGS = $(THR_DEFS) $(subst O2,O3, $(CFLAGS)) $(EMEM_INCLUDES) +EMEM_LDFLAGS = $(LDFLAGS) + +ifeq ($(USING_VC), yes) +ifeq ($(TYPE),debug) +MT_LIB=MDd +else +MT_LIB=MD +endif + +EMEM_CFLAGS += -$(MT_LIB) +EMEM_LDFLAGS += -$(MT_LIB) +EMEM_ERTS_LIB=erts_$(MT_LIB)$(TYPEMARKER) + +else + +EMEM_ERTS_LIB=erts_r$(TYPEMARKER) + +endif + +EMEM_LIBS = $(LIBS) \ + -L$(ERL_TOP)/erts/lib/$(TARGET) \ + -L$(ERL_TOP)/erts/lib/internal/$(TARGET) \ + -l$(EMEM_ERTS_LIB) \ + -l$(ETHR_LIB_NAME)$(TYPEMARKER) \ + $(ETHR_X_LIBS) + +EMEM_OBJS = $(addprefix $(EMEM_OBJ_DIR)/,$(notdir $(EMEM_SRCS:.c=.o))) + +# +# Misc targets +# + +all: $(CREATE_DIRS) erts_lib $(PROGS) $(DRIVERS) + +erts_lib: + cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE) + + +docs: + +clean: + $(RM) -rf ../obj/* + $(RM) -rf ../bin/* + $(RM) -f ./*~ + +.PHONY: all erts_lib docs clean + +# +# Make dir targets +# + +$(CREATE_DIRS): + $(MKDIR) -p $@ + +# +# Object targets +# + +$(EMEM_OBJ_DIR)/%.o: %.c + $(CC) $(EMEM_CFLAGS) -o $@ -c $< + +# +# Driver targets +# + +# +# Program targets +# + +$(BIN_DIR)/emem$(TYPEMARKER)@EXEEXT@: $(EMEM_OBJS) + $(PRE_LD) $(LD) $(EMEM_LDFLAGS) -o $@ $(EMEM_OBJS) $(EMEM_LIBS) + +# +# Release targets +# +include $(ERL_TOP)/make/otp_release_targets.mk +include ../vsn.mk +RELSYSDIR = $(RELEASE_PATH)/lib/tools-$(TOOLS_VSN) + +release_spec: all + $(INSTALL_DIR) $(RELSYSDIR)/c_src + $(INSTALL_DATA) $(EMEM_SRCS) $(EMEM_HEADERS) $(RELSYSDIR)/c_src +ifneq ($(PROGS),) + $(INSTALL_DIR) $(RELSYSDIR)/bin + $(INSTALL_PROGRAM) $(PROGS) $(RELSYSDIR)/bin +endif + +release_docs_spec: + +.PHONY: release_spec release_docs_spec + +# +# Make dependencies +# + +ifeq ($(USING_VC), yes) +DEP_CC=@EMU_CC@ +else +DEP_CC=$(CC) +endif + +SED_REPL_EMEM_OBJ_DIR=s|^\([^:]*\)\.o:|$$(EMEM_OBJ_DIR)/\1.o:|g +SED_REPL_OBJ_DIR=s|^\([^:]*\)\.o:|$$(OBJ_DIR)/\1.o:|g +SED_REPL_TT_DIR=s|$(TT_DIR)/|$$(TT_DIR)/|g +SED_REPL_TARGET=s|$(TARGET)/|$$(TARGET)/|g +SED_REPL_ERL_TOP=s|$(ERL_TOP)/|$$(ERL_TOP)/|g + +SED_EMEM_DEPEND=sed '$(SED_REPL_EMEM_OBJ_DIR);$(SED_REPL_TT_DIR);$(SED_REPL_TARGET);$(SED_REPL_ERL_TOP)' +SED_DEPEND=sed '$(SED_REPL_OBJ_DIR);$(SED_REPL_TT_DIR);$(SED_REPL_TARGET);$(SED_REPL_ERL_TOP)' + +DEPEND_MK=depend.mk + +dep depend: + @echo "Generating dependency file $(DEPEND_MK)..." + @echo "# Generated dependency rules." > $(DEPEND_MK); + @echo "# Do *not* edit this file; instead, run 'make depend'." \ + >> $(DEPEND_MK); + @echo "# " >> $(DEPEND_MK); + @echo "# emem objects..." >> $(DEPEND_MK); + $(DEP_CC) -MM $(EMEM_CFLAGS) $(EMEM_SRCS) \ + | $(SED_EMEM_DEPEND) >> $(DEPEND_MK) + @echo "# EOF" >> $(DEPEND_MK); + +.PHONY: dep depend + +include $(DEPEND_MK) + +# eof diff --git a/lib/tools/c_src/depend.mk b/lib/tools/c_src/depend.mk new file mode 100644 index 0000000000..01da30e7c6 --- /dev/null +++ b/lib/tools/c_src/depend.mk @@ -0,0 +1,17 @@ +# Generated dependency rules. +# Do *not* edit this file; instead, run 'make depend'. +# +# emem objects... +$(EMEM_OBJ_DIR)/erl_memory.o: erl_memory.c \ + $(ERL_TOP)/erts/include/erl_fixed_size_int_types.h \ + $(ERL_TOP)/erts/include/$(TARGET)/erl_int_sizes_config.h \ + $(ERL_TOP)/erts/include/erl_memory_trace_parser.h \ + erl_memory_trace_block_table.h \ + $(ERL_TOP)/erts/include/internal/ethread.h \ + $(ERL_TOP)/erts/include/internal/$(TARGET)/ethread_header_config.h +$(EMEM_OBJ_DIR)/erl_memory_trace_block_table.o: erl_memory_trace_block_table.c \ + erl_memory_trace_block_table.h \ + $(ERL_TOP)/erts/include/erl_fixed_size_int_types.h \ + $(ERL_TOP)/erts/include/$(TARGET)/erl_int_sizes_config.h \ + $(ERL_TOP)/erts/include/erl_memory_trace_parser.h +# EOF diff --git a/lib/tools/c_src/erl_memory.c b/lib/tools/c_src/erl_memory.c new file mode 100644 index 0000000000..a0e139f059 --- /dev/null +++ b/lib/tools/c_src/erl_memory.c @@ -0,0 +1,2950 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + + +/* + * Description: + * + * Author: Rickard Green + */ + +/* Headers to include ... */ + +#ifdef __WIN32__ +# include <winsock2.h> +# undef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else +# if defined(__linux__) && defined(__GNUC__) +# define _GNU_SOURCE 1 +# endif +# include <unistd.h> +# include <sys/types.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <fcntl.h> +# include <netdb.h> +# include <arpa/inet.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> + +#include "erl_fixed_size_int_types.h" +#include "erl_memory_trace_parser.h" +#include "erl_memory_trace_block_table.h" +#include "ethread.h" + +/* Increment when changes are made */ +#define EMEM_VSN_STR "0.9" + +/* Features not fully implemented yet */ +#define EMEM_A_SWITCH 0 +#define EMEM_C_SWITCH 0 +#define EMEM_c_SWITCH 0 +#define EMEM_d_SWITCH 0 + +/* Some system specific defines ... */ +#ifdef __WIN32__ +# define ssize_t int +# define GET_SOCK_ERRNO() (WSAGetLastError() - WSABASEERR) +# define IS_INVALID_SOCKET(X) ((X) == INVALID_SOCKET) +# ifdef __GNUC__ +# define INLINE __inline__ +# else +# define INLINE __forceinline +# endif +# define DIR_SEP_CHAR '\\' +#else +# define SOCKET int +# define closesocket close +# define GET_SOCK_ERRNO() (errno ? errno : INT_MAX) +# define INVALID_SOCKET (-1) +# define IS_INVALID_SOCKET(X) ((X) < 0) +# ifdef __GNUC__ +# define INLINE __inline__ +# else +# define INLINE +# endif +# define DIR_SEP_CHAR '/' +#endif + +#define EM_ERL_CMD_FILE_NAME "erl_cmd.txt" +#define EM_OUTPUT_FILE_SUFFIX ".emem" + +#define PRINT_OPERATIONS 0 + +/* Our own assert() ... */ +#ifdef DEBUG +#define ASSERT(A) ((void) ((A) ? 1 : assert_failed(__FILE__, __LINE__, #A))) +#include <stdio.h> +static int assert_failed(char *f, int l, char *a) +{ + fprintf(stderr, "%s:%d: Assertion failed: %s\n", f, l, a); + abort(); + return 0; +} + +#else +#define ASSERT(A) ((void) 1) +#endif + +#define ERR_RET(X) return (X) +#if 1 +# undef ERR_RET +# define ERR_RET(X) abort() +#endif + +/* #define HARD_DEBUG */ + + +#define EM_EXIT_RESULT (EMTBT_MIN_ERROR - 1) +#define EM_TRUNCATED_TRACE_ERROR (EMTBT_MIN_ERROR - 2) +#define EM_INTERNAL_ERROR (EMTBT_MIN_ERROR - 3) + +#define EM_DEFAULT_BUF_SZ 8192 + +#define EM_LINES_UNTIL_HEADER 20 +#define EM_NO_OF_OPS 400 +#define EM_MAX_CONSECUTIVE_TRACE_READS 10 +#define EM_MAX_NO_OF_TRACE_BUFS 1280 +#define EM_MIN_TRACE_READ_SIZE (EM_DEFAULT_BUF_SZ/20) +#define EM_TIME_FIELD_WIDTH 11 + +static void error(int res); +static void error_msg(int res, char *msg); + +typedef struct { + usgnd_int_max size; + usgnd_int_max min_size; + usgnd_int_max max_size; + usgnd_int_max max_ever_size; + usgnd_int_max no; + usgnd_int_max min_no; + usgnd_int_max max_no; + usgnd_int_max max_ever_no; + usgnd_int_max allocs; + usgnd_int_max reallocs; + usgnd_int_max frees; +} em_mem_info; + +typedef struct em_buffer_ { + struct em_buffer_ *next; + int write; + usgnd_int_8 *data; + usgnd_int_8 *data_end; + usgnd_int_8 *end; + size_t size; + usgnd_int_8 start[EM_DEFAULT_BUF_SZ]; +} em_buffer; + +typedef struct { + int no_writer; + int no_reader; + size_t tot_buf_size; + size_t max_buf_size; + char *name; + em_buffer *first; + em_buffer *last; + ethr_mutex mutex; + ethr_cond cond; + int used_def_buf_a; + em_buffer def_buf_a; + int used_def_buf_b; + em_buffer def_buf_b; +} em_buf_queue; + +typedef struct { + usgnd_int_8 *ptr; + size_t size; +} em_area; + +typedef struct { + char *name; + int ix; +} em_output_types; + +typedef struct { + + /* Memory allocation functions */ + void * (*alloc)(size_t); + void * (*realloc)(void *, size_t); + void (*free)(void *); + + emtbt_table *block_table; + emtbt_table **carrier_table; + + struct { + em_mem_info total; + em_mem_info *btype; + em_mem_info *allctr; + em_mem_info **allctr_prv_crr; + em_mem_info **allctr_usd_crr; + + struct { + usgnd_int_32 secs; + usgnd_int_32 usecs; + } stop_time; + emtp_op_type stop_reason; + usgnd_int_32 exit_status; + } info; + + /* Input ... */ + struct { + usgnd_int_16 listen_port; + SOCKET socket; + usgnd_int_max total_trace_size; + int error; + char *error_descr; + em_buf_queue queue; + } input; + + /* Output ... */ + struct { + usgnd_int_32 next_print; + usgnd_int_32 next_print_inc; + char *header; + size_t header_size; + size_t values_per_object; + size_t values_per_line; + size_t field_width; + int verbose; + int total; + int all_allctrs; + int no_allctrs; + em_output_types *allctrs; + int all_btypes; + int no_btypes; + em_output_types *btypes; + int max_min_values; + int block_counts; + int op_counts; + int lines_until_header; + FILE *stream; + char *file_name; +#if EMEM_d_SWITCH + char *dir_name; + FILE *erl_cmd_file; + struct { + ethr_mutex *mutex; + ethr_cond *cond; + } go; +#endif + em_buf_queue queue; + } output; + + /* Trace info */ + emtp_state *trace_state; + emtp_info trace_info; + +} em_state; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Threads... * + * * +\* */ + +static INLINE void +mutex_init(ethr_mutex *mtx) +{ + int res = ethr_mutex_init(mtx); + if (res) + error_msg(res, "Mutex init"); +} + +static INLINE void +mutex_destroy(ethr_mutex *mtx) +{ + int res = ethr_mutex_destroy(mtx); + if (res) + error_msg(res, "Mutex destroy"); +} + +static INLINE void +mutex_lock(ethr_mutex *mtx) +{ + int res = ethr_mutex_lock(mtx); + if (res) + error_msg(res, "Mutex lock"); +} + +static INLINE void +mutex_unlock(ethr_mutex *mtx) +{ + int res = ethr_mutex_unlock(mtx); + if (res) + error_msg(res, "Mutex unlock"); +} + +static INLINE void +cond_init(ethr_cond *cnd) +{ + int res = ethr_cond_init(cnd); + if (res) + error_msg(res, "Cond init"); +} + +static INLINE void +cond_destroy(ethr_cond *cnd) +{ + int res = ethr_cond_destroy(cnd); + if (res) + error_msg(res, "Cond destroy"); +} + +static INLINE void +cond_wait(ethr_cond *cnd, ethr_mutex *mtx) +{ + int res = ethr_cond_wait(cnd, mtx); + if (res) + error_msg(res, "Cond wait"); +} + +static INLINE void +cond_signal(ethr_cond *cnd) +{ + int res = ethr_cond_signal(cnd); + if (res) + error_msg(res, "Cond signal"); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Buffer queues * + * * +\* */ + +static INLINE void +reset_buffer(em_buffer *b, size_t size) +{ + b->write = 1; + b->next = NULL; + if (size) { + b->size = size; + b->end = b->start + size; + } + b->data_end = b->data = b->start; +} + +static void +init_queue(em_state *state, em_buf_queue *queue) +{ + reset_buffer(&queue->def_buf_a, EM_DEFAULT_BUF_SZ); + reset_buffer(&queue->def_buf_b, EM_DEFAULT_BUF_SZ); + queue->first = NULL; + queue->last = NULL; + queue->no_writer = 0; + queue->no_reader = 0; + queue->tot_buf_size = 0; + queue->max_buf_size = ~0; + queue->name = ""; + queue->used_def_buf_a = 0; + queue->used_def_buf_b = 0; + mutex_init(&queue->mutex); + cond_init(&queue->cond); +} + +static void +destroy_queue(em_state *state, em_buf_queue *queue) +{ + while (queue->first) { + em_buffer *buf = queue->first; + queue->first = queue->first->next; + if (buf != &queue->def_buf_a && buf != &queue->def_buf_b) + (*state->free)((void *) buf); + } + mutex_destroy(&queue->mutex); + cond_destroy(&queue->cond); +} + +static void +disconnect_queue_writer(em_buf_queue *queue) +{ + mutex_lock(&queue->mutex); + queue->no_writer = 1; + cond_signal(&queue->cond); + mutex_unlock(&queue->mutex); +} + +static void +disconnect_queue_reader(em_buf_queue *queue) +{ + mutex_lock(&queue->mutex); + queue->no_reader = 1; + cond_signal(&queue->cond); + mutex_unlock(&queue->mutex); +} + +static int +is_queue_writer_disconnected(em_buf_queue *queue) +{ + int res; + mutex_lock(&queue->mutex); + res = queue->no_writer; + mutex_unlock(&queue->mutex); + return res; +} + +static int +is_queue_reader_disconnected(em_buf_queue *queue) +{ + int res; + mutex_lock(&queue->mutex); + res = queue->no_reader; + mutex_unlock(&queue->mutex); + return res; +} + +static INLINE void +dequeue(em_state *state, em_buf_queue *queue) +{ + em_buffer *buf; + + ASSERT(queue->first); + ASSERT(queue->tot_buf_size > 0); + + buf = queue->first; + queue->first = buf->next; + if (!queue->first) + queue->last = NULL; + + ASSERT(queue->tot_buf_size >= buf->size); + queue->tot_buf_size -= buf->size; + + if (buf == &queue->def_buf_a) + queue->used_def_buf_a = 0; + else if (buf == &queue->def_buf_b) + queue->used_def_buf_b = 0; + else + (*state->free)((void *) buf); + +} + + +static INLINE em_buffer * +enqueue(em_state *state, em_buf_queue *queue, size_t min_size) +{ + em_buffer *buf; + + if (min_size > EM_DEFAULT_BUF_SZ) + goto alloc_buf; + + if (!queue->used_def_buf_a) { + buf = &queue->def_buf_a; + queue->used_def_buf_a = 1; + reset_buffer(buf, 0); + } + else if (!queue->used_def_buf_b) { + buf = &queue->def_buf_b; + queue->used_def_buf_b = 1; + reset_buffer(buf, 0); + } + else { + size_t bsize; + alloc_buf: + + bsize = EM_DEFAULT_BUF_SZ; + if (bsize < min_size) + bsize = min_size; + + buf = (em_buffer *) (*state->alloc)(sizeof(em_buffer) + + (sizeof(usgnd_int_8) + * (bsize-EM_DEFAULT_BUF_SZ))); + if (buf) { + buf->size = bsize; + reset_buffer(buf, bsize); + } + } + + if (queue->last) { + ASSERT(queue->first); + queue->last->write = 0; + queue->last->next = buf; + } + else { + ASSERT(!queue->first); + queue->first = buf; + } + + queue->tot_buf_size += buf->size; + queue->last = buf; + + return buf; +} + +static void +get_next_read_area(em_area *area, em_state *state, em_buf_queue *queue) +{ + mutex_lock(&queue->mutex); + + while (!queue->first || queue->first->data == queue->first->data_end) { + if (queue->first && (!queue->first->write + || queue->first->data == queue->first->end)) { + dequeue(state, queue); + continue; + } + + if (queue->no_writer) { + area->ptr = NULL; + area->size = 0; + mutex_unlock(&queue->mutex); + return; + } + cond_wait(&queue->cond, &queue->mutex); + } + + ASSERT(queue->first->data < queue->first->data_end); + + area->ptr = queue->first->data; + area->size = queue->first->data_end - queue->first->data; + + queue->first->data = queue->first->data_end; + + mutex_unlock(&queue->mutex); +} + +static INLINE void +wrote_area_aux(em_area *area, em_state *state, em_buf_queue *queue, int do_lock) +{ + em_buffer *buf; + + if (do_lock) + mutex_lock(&queue->mutex); + + buf = queue->last; + + ASSERT(area->ptr); + ASSERT(area->size); + + ASSERT(buf); + ASSERT(buf->data_end == area->ptr); + ASSERT(buf->end >= area->ptr + area->size); + + buf->data_end = area->ptr + area->size; + + area->ptr = NULL; + area->size = 0; + + cond_signal(&queue->cond); + + if (do_lock) + mutex_unlock(&queue->mutex); +} + +static INLINE void +wrote_area(em_area *area, em_state *state, em_buf_queue *queue) +{ + wrote_area_aux(area, state, queue, 1); +} + +static void +get_next_write_area(em_area *area, em_state *state, em_buf_queue *queue, + size_t size) +{ + em_buffer *buf; + + mutex_lock(&queue->mutex); + + ASSERT(!area->size || area->ptr); + + if (area->size) + wrote_area_aux(area, state, queue, 0); + + buf = ((queue->last && queue->last->end - queue->last->data_end >= size) + ? queue->last + : enqueue(state, queue, size)); + + if (buf) { + ASSERT(buf->end - buf->data_end >= size); + area->ptr = buf->data_end; + area->size = buf->end - buf->data_end; + } + else { + area->ptr = NULL; + area->size = 0; + } + + if (queue->tot_buf_size > queue->max_buf_size) { + fprintf(stderr, + "emem: Maximum %s buffer size (%lu) exceeded. " + "Terminating...\n", + queue->name, + (unsigned long) queue->max_buf_size); + exit(1); + } + + mutex_unlock(&queue->mutex); + +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Output * + * * +\* */ + +static INLINE size_t +write_str(usgnd_int_8 **dstpp, char *srcp) +{ + size_t i = 0; + if (dstpp) + while (srcp[i]) + *((*dstpp)++) = (usgnd_int_8) srcp[i++]; + else + while (srcp[i]) i++; + return i; +} + + +static size_t +write_strings(usgnd_int_8 **ptr, + char **strings, + char *first_line_prefix, + char *line_prefix, + size_t max_line_size) +{ + size_t size; + size_t tot_size = 0; + size_t line_size = 0; + size_t line_prefix_size; + sgnd_int_32 ix; + + tot_size = line_size = line_prefix_size = write_str(ptr, first_line_prefix); + + for (ix = 0; strings[ix]; ix++) { + size = write_str(NULL, strings[ix]); + if (line_size + 1 + size > max_line_size) { + tot_size += write_str(ptr, "\n"); + tot_size += write_str(ptr, line_prefix); + line_size = line_prefix_size; + } + tot_size += write_str(ptr, " "); + tot_size += ptr ? write_str(ptr, strings[ix]) : size; + line_size += 1 + size; + } + + tot_size += write_str(ptr, "\n"); + + return tot_size; +} + +static size_t +write_title(usgnd_int_8 **bufp, size_t *overflow, size_t width, char *str) +{ + size_t i, sz, ws; + usgnd_int_8 *p, *endp; + + /* + * Writes at least one '|' character at the beginning. + * Right aligns "str". + * If "str" is larger than "width - 1" and overflow is NULL, + * then "str" is trucated; otherwise, string is not truncated. + */ + + if (width <= 0) + return 0; + + if (!bufp && !overflow) + return width; + + sz = strlen(str) + 1; + if (sz > width) { + ws = 0; + if (overflow) + *overflow += sz - width; + else + sz = width; + } + else { + ws = width - sz; + if (overflow) { + if (ws >= *overflow) { + ws -= *overflow; + *overflow = 0; + } + else { + *overflow -= ws; + ws = 0; + } + } + sz += ws; + } + if (!bufp) + return sz; + + p = *bufp; + endp = p + width; + + *(p++) = '|'; + while (ws > 1) { + ws--; + *(p++) = (usgnd_int_8) ' '; + } + + i = 0; + while (str[i] && (overflow || p < endp)) + *(p++) = (usgnd_int_8) str[i++]; + + while (ws) { + ws--; + *(p++) = (usgnd_int_8) ' '; + } + + ASSERT(overflow || p == endp); + ASSERT(sz == (size_t) (p - *bufp)); + *bufp = p; + return sz; +} + +static size_t +write_obj_sub_titles(em_state *state, usgnd_int_8 **bufp, size_t *overflow) +{ + size_t field_width = state->output.field_width; + size_t size = write_title(bufp, overflow, field_width, "size"); + if (state->output.max_min_values) { + size += write_title(bufp, overflow, field_width, "min size"); + size += write_title(bufp, overflow, field_width, "max size"); + } + if (state->output.block_counts) { + size += write_title(bufp, overflow, field_width, "no"); + if (state->output.max_min_values) { + size += write_title(bufp, overflow, field_width, "min no"); + size += write_title(bufp, overflow, field_width, "max no"); + } + } + if (state->output.op_counts) { + size += write_title(bufp, overflow, field_width, "alloc()"); + size += write_title(bufp, overflow, field_width, "realloc()"); + size += write_title(bufp, overflow, field_width, "free()"); + } + + return size; +} + +static size_t +write_header(em_state *state, usgnd_int_8 *ptr, int trunc) +{ +#define MIN_LTEXT_SZ 18 +#define HEADER_EOL_STR "|\n" + usgnd_int_8 *p; + usgnd_int_8 **pp; + int i; + size_t overflow; + size_t *ofp; + size_t obj_size = state->output.values_per_object*state->output.field_width; + size_t size = 0; + int have_seg_crr = state->trace_info.have_segment_carrier_info; + + if (ptr) { + p = ptr; + pp = &p; + } + else { + p = NULL; + pp = NULL; + } + + overflow = 0; + ofp = trunc ? NULL : &overflow; + + size += write_title(pp, ofp, EM_TIME_FIELD_WIDTH, "time"); + + if (state->output.total) { + int no = 1; + if (have_seg_crr) { + if (state->info.allctr_prv_crr[state->trace_info.segment_ix]) + no++; + if (state->info.allctr_usd_crr[state->trace_info.segment_ix]) + no++; + } + size += write_title(pp, ofp, (have_seg_crr ? 3 : 1)*obj_size, "total"); + } + + for (i = 0; i < state->output.no_allctrs; i++) { + int no = 1; + if (state->info.allctr_prv_crr[state->output.allctrs[i].ix]) + no++; + if (state->info.allctr_usd_crr[state->output.allctrs[i].ix]) + no++; + size += write_title(pp, ofp, no*obj_size, state->output.allctrs[i].name); + } + + for (i = 0; i < state->output.no_btypes; i++) + size += write_title(pp, ofp, obj_size, state->output.btypes[i].name); + + size += write_str(pp, HEADER_EOL_STR); + + overflow = 0; + size += write_title(pp, ofp, EM_TIME_FIELD_WIDTH, ""); + + if (state->output.total) { + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "alcd blks" + : "allocated blocks")); + if (have_seg_crr) { + if (state->info.allctr_prv_crr[state->trace_info.segment_ix]) + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "mpd segs" + : "mapped segments")); + if (state->info.allctr_usd_crr[state->trace_info.segment_ix]) + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "chd segs" + : "cached segments")); + } + } + + for (i = 0; i < state->output.no_allctrs; i++) { + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "alcd blks" + : "allocated blocks")); + if (state->info.allctr_prv_crr[state->output.allctrs[i].ix]) + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "prvd crrs" + : "provided carriers")); + if (state->info.allctr_usd_crr[state->output.allctrs[i].ix]) + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "usd crrs" + : "used carriers")); + } + for (i = 0; i < state->output.no_btypes; i++) + size += write_title(pp, ofp, obj_size, (obj_size <= MIN_LTEXT_SZ + ? "alcd blks" + : "allocated blocks")); + + + size += write_str(pp, HEADER_EOL_STR); + overflow = 0; + size += write_title(pp, ofp, EM_TIME_FIELD_WIDTH, ""); + + if (state->output.total) { + size += write_obj_sub_titles(state, pp, ofp); + if (have_seg_crr) { + if (state->info.allctr_prv_crr[state->trace_info.segment_ix]) + size += write_obj_sub_titles(state, pp, ofp); + if (state->info.allctr_usd_crr[state->trace_info.segment_ix]) + size += write_obj_sub_titles(state, pp, ofp); + } + } + + for (i = 0; i < state->output.no_allctrs; i++) { + size += write_obj_sub_titles(state, pp, ofp); + if (state->info.allctr_prv_crr[state->output.allctrs[i].ix]) + size += write_obj_sub_titles(state, pp, ofp); + if (state->info.allctr_usd_crr[state->output.allctrs[i].ix]) + size += write_obj_sub_titles(state, pp, ofp); + } + + for (i = 0; i < state->output.no_btypes; i++) + size += write_obj_sub_titles(state, pp, ofp); + + size += write_str(pp, HEADER_EOL_STR); +#undef MIN_LTEXT_SZ +#undef HEADER_EOL_STR + return size; +} + +static INLINE void +write_mem_info(em_state *state, usgnd_int_8 **p, em_mem_info *mi) +{ + int fw = state->output.field_width - 1; + *p += sprintf(*p, "%*" USGND_INT_MAX_FSTR " ", fw, mi->size); + if (state->output.max_min_values) + *p += sprintf(*p, + "%*" USGND_INT_MAX_FSTR + " %*" USGND_INT_MAX_FSTR " ", + fw, mi->min_size, + fw, mi->max_size); + if (state->output.block_counts) { + *p += sprintf(*p, "%*" USGND_INT_MAX_FSTR " ", fw, mi->no); + if (state->output.max_min_values) + *p += sprintf(*p, + "%*" USGND_INT_MAX_FSTR + " %*" USGND_INT_MAX_FSTR " ", + fw, mi->min_no, + fw, mi->max_no); + } + if (state->output.op_counts) + *p += sprintf(*p, + "%*" USGND_INT_MAX_FSTR + " %*" USGND_INT_MAX_FSTR + " %*" USGND_INT_MAX_FSTR " ", + fw, mi->allocs, + fw, mi->reallocs, + fw, mi->frees); + + /* Update max ever values */ + if (mi->max_ever_size < mi->max_size) + mi->max_ever_size = mi->max_size; + if (mi->max_ever_no < mi->max_no) + mi->max_ever_no = mi->max_no; + /* Reset max/min values */ + mi->max_size = mi->min_size = mi->size; + mi->max_no = mi->min_no = mi->no; +} + +static INLINE void +write_max_ever_mem_info(em_state *state, usgnd_int_8 **p, em_mem_info *mi) +{ + int fw = state->output.field_width - 1; + *p += sprintf(*p, "%*" USGND_INT_MAX_FSTR " ", fw, mi->max_ever_size); + if (state->output.max_min_values) + *p += sprintf(*p, "%*s %*s ", fw, "", fw, ""); + if (state->output.block_counts) { + *p += sprintf(*p, "%*" USGND_INT_MAX_FSTR " ", fw, mi->max_ever_no); + if (state->output.max_min_values) + *p += sprintf(*p, "%*s %*s ", fw, "", fw, ""); + } + if (state->output.op_counts) + *p += sprintf(*p, "%*s %*s %*s ", fw, "", fw, "", fw, ""); +} + +static void +print_string(em_state *state, char *str) +{ + em_area area = {NULL, 0}; + usgnd_int_8 *p; + + /* Get area */ + + get_next_write_area(&area,state,&state->output.queue,write_str(NULL,str)); + + p = (usgnd_int_8 *) area.ptr; + area.size = write_str(&p, str); + + /* Leave area */ + + wrote_area(&area, state, &state->output.queue); + +} + +static int +print_emu_arg(em_state *state) +{ + em_area area = {NULL, 0}; + char hostname[100]; + char carg[22]; + struct sockaddr_in saddr; + struct hostent *hp; + struct in_addr iaddr; + usgnd_int_16 port; + int saddr_size = sizeof(saddr); + size_t size; + char *format = "> Emulator command line argument: +Mit %s\n"; + + if (getsockname(state->input.socket, + (struct sockaddr *) &saddr, + &saddr_size) != 0) + goto error; + + port = ntohs(saddr.sin_port); + + ASSERT(state->input.listen_port == 0 || state->input.listen_port == port); + + state->input.listen_port = port; + + if (gethostname(hostname, sizeof(hostname)) != 0) + goto error; + + hp = gethostbyname(hostname); + if (!hp) + goto error; + + if (hp->h_addr_list) { + (void) memcpy(&iaddr.s_addr, *hp->h_addr_list, sizeof(iaddr.s_addr)); + (void) sprintf(carg, "%s:%d", inet_ntoa(iaddr), (int) port); + } + else + (void) sprintf(carg, "127.0.0.1:%d", (int) port); + +#if EMEM_d_SWITCH + + if (state->output.erl_cmd_file) { + fprintf(state->output.erl_cmd_file, "+Mit %s\n", carg); + fclose(state->output.erl_cmd_file); + state->output.erl_cmd_file = NULL; + } + +#endif + + size = strlen(format) + strlen(carg); + + /* Get area */ + + get_next_write_area(&area, state, &state->output.queue, size); + + area.size = sprintf(area.ptr, format, carg); + + /* Leave area */ + + wrote_area(&area, state, &state->output.queue); + + return 0; + + error: + return GET_SOCK_ERRNO(); +} + +static size_t +write_allocator_info(em_state *state, usgnd_int_8 *ptr) +{ + usgnd_int_32 aix, i, j; + char *header = "> Allocator information:\n"; + char *allctr_str = "> * Allocator:"; + char *crr_prv_str = "> * Carrier providers:"; + char *blk_tp_str = "> * Block types:"; + char *line_prefix = "> "; + size_t size = 0; + char **strings; + size_t strings_size; + size_t max_line_size = 80; + usgnd_int_8 *p = ptr; + usgnd_int_8 **pp = ptr ? &p : NULL; + + strings_size = state->trace_info.max_block_type_ix + 1; + if (strings_size < state->trace_info.max_allocator_ix + 1) + strings_size = state->trace_info.max_allocator_ix + 1; + + strings = (char **) (*state->alloc)((strings_size + 1)*sizeof(char *)); + if (!strings) + error(ENOMEM); + + size += write_str(pp, header); + + for (aix = 0; aix <= state->trace_info.max_allocator_ix; aix++) { + emtp_allocator *allctr = state->trace_info.allocator[aix]; + if (!allctr->valid) + continue; + + strings[0] = allctr->name; + strings[1] = NULL; + size += write_strings(pp,strings,allctr_str,line_prefix,max_line_size); + + i = 0; + if (allctr->carrier.provider) + for (j = 0; j < allctr->carrier.no_providers; j++) { + usgnd_int_32 cpix = allctr->carrier.provider[j]; + if (cpix == state->trace_info.segment_ix) + strings[i++] = "segment"; + else + strings[i++] = state->trace_info.allocator[cpix]->name; + } + strings[i] = NULL; + size += write_strings(pp,strings,crr_prv_str,line_prefix,max_line_size); + + i = 0; + for (j = 0; j <= state->trace_info.max_block_type_ix; j++) + if (state->trace_info.block_type[j]->allocator == aix) + strings[i++] = state->trace_info.block_type[j]->name; + strings[i] = NULL; + size += write_strings(pp,strings,blk_tp_str,line_prefix,max_line_size); + } + + (*state->free)((void *) strings); + + return size; +} + +static void +print_main_header(em_state *state) +{ +#if HAVE_INT_64 +#define MAX_WORD_SZ_STR "64" +#else +#define MAX_WORD_SZ_STR "32" +#endif + em_area area = {NULL, 0}; + char *format1 = + "> emem version: " EMEM_VSN_STR "\n" + "> Nodename: %s\n" + "> Hostname: %s\n" + "> Pid: %s\n" + "> Start time (UTC): "; + char *format2 = "%4.4" USGND_INT_32_FSTR + "-%2.2" USGND_INT_32_FSTR "-%2.2" USGND_INT_32_FSTR + " %2.2" USGND_INT_32_FSTR ":%2.2" USGND_INT_32_FSTR + ":%2.2" USGND_INT_32_FSTR ".%6.6" USGND_INT_32_FSTR "\n"; + char *format3 = + "> Trace parser version: %" USGND_INT_32_FSTR ".%" USGND_INT_32_FSTR + "\n" + "> Actual trace version: %" USGND_INT_32_FSTR ".%" USGND_INT_32_FSTR + "\n" + "> Maximum trace word size: " MAX_WORD_SZ_STR " bits\n" + "> Actual trace word size: %d bits\n"; + size_t size = (strlen(format1) + + (state->trace_info.start_time.month + ? (strlen(format2) + 7*10) + : 1) + + strlen(format3) + + strlen(state->trace_info.nodename) + + strlen(state->trace_info.hostname) + + strlen(state->trace_info.pid) + + 5*10 + 1); + + if (state->output.verbose) { + size += write_allocator_info(state, NULL); + } + + size += write_header(state, NULL, 0); + + /* Get area */ + get_next_write_area(&area, state, &state->output.queue, size); + + area.size = sprintf(area.ptr, + format1, + state->trace_info.nodename, + state->trace_info.hostname, + state->trace_info.pid); + if (state->trace_info.start_time.month) + area.size += sprintf(area.ptr + area.size, + format2, + state->trace_info.start_time.year, + state->trace_info.start_time.month, + state->trace_info.start_time.day, + state->trace_info.start_time.hour, + state->trace_info.start_time.minute, + state->trace_info.start_time.second, + state->trace_info.start_time.micro_second); + else + *(area.ptr + area.size++) = '\n'; + area.size += sprintf(area.ptr + area.size, + format3, + state->trace_info.version.parser.major, + state->trace_info.version.parser.minor, + state->trace_info.version.trace.major, + state->trace_info.version.trace.minor, + state->trace_info.bits); + + if (state->output.verbose) { + area.size += write_allocator_info(state, area.ptr + area.size); + } + + area.size += write_header(state, area.ptr + area.size, 0); + + /* Leave area */ + wrote_area(&area, state, &state->output.queue); +#undef MAX_WORD_SZ_STR +} + +static void +print_main_footer(em_state *state) +{ + em_area area = {NULL, 0}; + usgnd_int_8 *p; + int i; + char *stop_str = + "> Trace stopped\n"; + char *exit_str = + "> Emulator exited with code: %" USGND_INT_32_FSTR "\n"; + char *format = + "> Total trace size: %" USGND_INT_MAX_FSTR " bytes\n" + "> Average band width used: %" USGND_INT_MAX_FSTR " Kbit/s\n"; + size_t size; + usgnd_int_max tsz = state->input.total_trace_size; + usgnd_int_32 secs = state->info.stop_time.secs; + usgnd_int_32 usecs = state->info.stop_time.usecs; + usgnd_int_max bw; + + /* Max size of the max value line. Each value can at most use 21 + characters: largest possible usgnd_int_64 (20 digits) and one + white space. */ + size = state->output.values_per_line*21 + 1; + + switch (state->info.stop_reason) { + case EMTP_STOP: + size += strlen(stop_str) + 1; + break; + case EMTP_EXIT: + size += strlen(exit_str); + size += 10; /* Enough for one unsgn_int_32 */ + size++; + break; + default: + break; + } + size += strlen(format); + size += 2*20; /* Enough for two unsgn_int_64 */ + size += 2; + + bw = (tsz + 1023)/1024; + bw *= 1000; + bw /= secs*1000 + usecs/1000; + bw *= 8; + + /* Get area */ + + get_next_write_area(&area, state, &state->output.queue, size); + + p = area.ptr; + + p += sprintf(p, "> %-*s", EM_TIME_FIELD_WIDTH - 2, "Maximum:"); + + if (state->output.total) { + int six = state->trace_info.segment_ix; + write_max_ever_mem_info(state, &p, &state->info.total); + if (state->trace_info.have_segment_carrier_info) { + if (state->info.allctr_prv_crr[six]) + write_max_ever_mem_info(state, + &p, + state->info.allctr_prv_crr[six]); + if (state->info.allctr_usd_crr[six]) + write_max_ever_mem_info(state, + &p, + state->info.allctr_usd_crr[six]); + } + } + for (i = 0; i < state->output.no_allctrs; i++) { + int ix = state->output.allctrs[i].ix; + write_max_ever_mem_info(state, &p, &state->info.allctr[ix]); + if (state->info.allctr_prv_crr[ix]) + write_max_ever_mem_info(state, + &p, + state->info.allctr_prv_crr[ix]); + if (state->info.allctr_usd_crr[ix]) + write_max_ever_mem_info(state, + &p, + state->info.allctr_usd_crr[ix]); + } + for (i = 0; i < state->output.no_btypes; i++) + write_max_ever_mem_info(state, + &p, + &state->info.btype[state->output.btypes[i].ix]); + + p += sprintf(p, "\n"); + + switch (state->info.stop_reason) { + case EMTP_STOP: + p += sprintf(p, stop_str); + break; + case EMTP_EXIT: + p += sprintf(p, exit_str, state->info.exit_status); + break; + default: + break; + } + + p += sprintf(p, format, tsz, bw); + + area.size = p - area.ptr; + + ASSERT(area.size <= size); + + /* Leave area */ + + wrote_area(&area, state, &state->output.queue); + +} + +static void +print_info(em_state *state, usgnd_int_32 secs, char *extra) +{ + usgnd_int_8 *p; + int i; + size_t size; + em_area area = {NULL, 0}; + + /* Get area */ + + size = 0; + if (!state->output.lines_until_header) + size += state->output.header_size; + + /* Max size of one line of values. Each value can at most use 21 + characters: largest possible usgnd_int_64 (20 digits) and one white + space. */ + size += state->output.values_per_line*21 + 1; + + if (extra) + size += write_str(NULL, extra); + + get_next_write_area(&area, state, &state->output.queue, size); + + /* Write to area */ + + p = area.ptr; + + if (!state->output.lines_until_header) { + memcpy((void *) area.ptr, + (void *) state->output.header, + state->output.header_size); + p += state->output.header_size; + state->output.lines_until_header = EM_LINES_UNTIL_HEADER; + } + else + state->output.lines_until_header--; + + p += sprintf(p, "%*" USGND_INT_32_FSTR " ", EM_TIME_FIELD_WIDTH - 1, secs); + + if (state->output.total) { + int six = state->trace_info.segment_ix; + write_mem_info(state, &p, &state->info.total); + if (state->trace_info.have_segment_carrier_info) { + if (state->info.allctr_prv_crr[six]) + write_mem_info(state, &p, state->info.allctr_prv_crr[six]); + if (state->info.allctr_usd_crr[six]) + write_mem_info(state, &p, state->info.allctr_usd_crr[six]); + } + } + for (i = 0; i < state->output.no_allctrs; i++) { + int ix = state->output.allctrs[i].ix; + write_mem_info(state, &p, &state->info.allctr[ix]); + if (state->info.allctr_prv_crr[ix]) + write_mem_info(state, &p, state->info.allctr_prv_crr[ix]); + if (state->info.allctr_usd_crr[ix]) + write_mem_info(state, &p, state->info.allctr_usd_crr[ix]); + } + for (i = 0; i < state->output.no_btypes; i++) + write_mem_info(state, + &p, + &state->info.btype[state->output.btypes[i].ix]); + + p += sprintf(p, "\n"); + + if (extra) + p += write_str(&p, extra); + + ASSERT(area.size >= p - area.ptr); + area.size = p - area.ptr; + + /* Leave area */ + + wrote_area(&area, state, &state->output.queue); +} + +static void +reset_mem_info(em_mem_info *mi) +{ + mi->size = 0; + mi->min_size = 0; + mi->max_size = 0; + mi->max_ever_size = 0; + mi->no = 0; + mi->min_no = 0; + mi->max_no = 0; + mi->max_ever_no = 0; + mi->allocs = 0; + mi->reallocs = 0; + mi->frees = 0; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * State creation and destruction * + * * +\* */ + +static void +destroy_state(em_state *state) +{ + int i; + void (*freep)(void *); + + freep = state->free; + + if (state->block_table) + emtbt_destroy_table(state->block_table); + + if (state->carrier_table) { + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) + if (state->carrier_table[i]) + emtbt_destroy_table(state->carrier_table[i]); + state->carrier_table--; + (*freep)((void *) state->carrier_table); + } + + if (state->info.btype) { + state->info.btype--; + (*freep)((void *) state->info.btype); + } + + if (state->info.allctr) { + state->info.allctr--; + (*freep)((void *) state->info.allctr); + } + + if (state->info.allctr_prv_crr) { + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) + if (state->info.allctr_prv_crr[i]) + (*freep)((void *) state->info.allctr_prv_crr[i]); + state->info.allctr_prv_crr--; + (*freep)((void *) state->info.allctr_prv_crr); + } + + + if (state->info.allctr_usd_crr) { + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) + if (state->info.allctr_usd_crr[i]) + (*freep)((void *) state->info.allctr_usd_crr[i]); + state->info.allctr_usd_crr--; + (*freep)((void *) state->info.allctr_usd_crr); + } + + emtp_state_destroy(state->trace_state); + destroy_queue(state, &state->input.queue); + + if (state->output.btypes) + (*freep)((void *) state->output.btypes); + if (state->output.allctrs) + (*freep)((void *) state->output.allctrs); + destroy_queue(state, &state->output.queue); + +#if EMEM_d_SWITCH + + if (state->output.go.mutex) { + mutex_destroy(state->output.go.mutex); + (*state->free)((void *) state->output.go.mutex); + state->output.go.mutex = NULL; + } + if (state->output.go.cond) { + cond_destroy(state->output.go.cond); + (*state->free)((void *) state->output.go.cond); + state->output.go.cond = NULL; + } + +#endif + + if (!IS_INVALID_SOCKET(state->input.socket)) { + closesocket(state->input.socket); + state->input.socket = INVALID_SOCKET; + } + + (*freep)((void *) state); +} + +static em_state * +new_state(void * (*alloc)(size_t), + void * (*realloc)(void *, size_t), + void (*free)(void *)) +{ + em_state *state = NULL; + + state = (*alloc)(sizeof(em_state)); + if (!state) + goto error; + + /* Stuff that might fail (used after the error label) */ + + state->trace_state = NULL; + + /* Init state ... */ + + state->alloc = alloc; + state->realloc = realloc; + state->free = free; + + state->block_table = NULL; + state->carrier_table = NULL; + + reset_mem_info(&state->info.total); + state->info.btype = NULL; + state->info.allctr = NULL; + + state->info.allctr_prv_crr = NULL; + state->info.allctr_usd_crr = NULL; + + state->info.stop_time.secs = 0; + state->info.stop_time.usecs = 0; + state->info.stop_reason = EMTP_UNDEF; + state->info.exit_status = 0; + + state->output.next_print = 0; + state->output.next_print_inc = 10; + state->output.header = NULL; + state->output.header_size = 0; + state->output.values_per_object = 0; + state->output.values_per_line = 0; + state->output.field_width = 11; + state->output.verbose = 0; + state->output.total = 0; + state->output.all_allctrs = 0; + state->output.no_allctrs = 0; + state->output.allctrs = NULL; + state->output.all_btypes = 0; + state->output.no_btypes = 0; + state->output.btypes = NULL; + state->output.max_min_values = 0; + state->output.block_counts = 0; + state->output.op_counts = 0; + state->output.lines_until_header = EM_LINES_UNTIL_HEADER; + +#if PRINT_OPERATIONS + state->output.stream = stderr; +#else + state->output.stream = stdout; +#endif + state->output.file_name = NULL; +#if EMEM_d_SWITCH + state->output.dir_name = NULL; + state->output.erl_cmd_file = NULL; + state->output.go.mutex = NULL; + state->output.go.cond = NULL; +#endif + + init_queue(state, &state->output.queue); + state->output.queue.max_buf_size = 10*1024*1024; + state->output.queue.name = "output"; + + state->trace_state = emtp_state_new(alloc, realloc, free); + if (!state->trace_state) + goto error; + + state->trace_info.version.parser.major = 0; + state->trace_info.version.parser.minor = 0; + state->trace_info.version.trace.major = 0; + state->trace_info.version.trace.minor = 0; + state->trace_info.bits = 0; + state->trace_info.max_allocator_ix = 0; + state->trace_info.allocator = NULL; + state->trace_info.max_block_type_ix = 0; + state->trace_info.block_type = NULL; + + state->input.listen_port = 0; + state->input.socket = INVALID_SOCKET; + state->input.total_trace_size = 0; + state->input.error = 0; + state->input.error_descr = NULL; + + init_queue(state, &state->input.queue); + state->input.queue.max_buf_size = 10*1024*1024; + state->input.queue.name = "input"; + + return state; + + error: + if (state) { + if (state->trace_state) + emtp_state_destroy(state->trace_state); + (*free)(state); + } + return NULL; +} + + +static emtbt_table * +mk_block_table(em_state *state) +{ + return emtbt_new_table(state->trace_info.bits == 64, + state->alloc, + state->realloc, + state->free); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * * + * * +\* */ +#if PRINT_OPERATIONS +void print_op(em_state *state, emtp_operation *op); +#endif + +static INLINE void +update_max_values(em_mem_info *mi) +{ + if (mi->max_size < mi->size) + mi->max_size = mi->size; + if (mi->max_no < mi->no) + mi->max_no = mi->no; +} + +static INLINE void +update_min_values(em_mem_info *mi) +{ + if (mi->min_size > mi->size) + mi->min_size = mi->size; + if (mi->min_no > mi->no) + mi->min_no = mi->no; +} + +static INLINE void +update_alloc_op(em_mem_info *mi, usgnd_int_max size) +{ + mi->allocs++; + mi->size += size; + mi->no++; + update_max_values(mi); +} + +static INLINE void +update_realloc_op(em_mem_info *mi, + usgnd_int_max size, + usgnd_int_max prev_size, + int no_change) +{ + mi->reallocs++; + ASSERT(mi->size >= prev_size); + mi->size -= prev_size; + mi->size += size; + if (no_change) { + if (no_change > 0) + mi->no++; + else { + ASSERT(mi->no > 0); + mi->no--; + } + } + update_max_values(mi); + update_min_values(mi); +} + +static INLINE void +update_free_op(em_mem_info *mi, usgnd_int_max prev_size) +{ + mi->frees++; + ASSERT(mi->size >= prev_size); + mi->size -= prev_size; + ASSERT(mi->no > 0); + mi->no--; + update_min_values(mi); +} + +static int +insert_operations(em_state *state, emtp_operation ops[], size_t len) +{ + emtbt_table *crr_table; + emtbt_block old_blk; + usgnd_int_32 prev_size; + usgnd_int_max size; + size_t i; + int res; + int aix, btix, crrix; + + for (i = 0; i < len; i++) { + + while (state->output.next_print <= ops[i].time.secs) { + print_info(state, state->output.next_print, NULL); + state->output.next_print += state->output.next_print_inc; + } + + switch (ops[i].type) { + + case EMTP_ALLOC: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + btix = (int) ops[i].u.block.type; + aix = state->trace_info.block_type[btix]->allocator; + + if (!ops[i].u.block.new_ptr) + continue; + + res = emtbt_alloc_op(state->block_table, &ops[i]); + if (res != 0) + ERR_RET(res); + + size = ops[i].u.block.new_size; + + update_alloc_op(&state->info.btype[btix], size); + update_alloc_op(&state->info.allctr[aix], size); + update_alloc_op(&state->info.total, size); + break; + + case EMTP_REALLOC: { + int no; +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + + res = emtbt_realloc_op(state->block_table, &ops[i], &old_blk); + if (res != 0) + ERR_RET(res); + + size = ops[i].u.block.new_size; + prev_size = old_blk.size; + + if (!ops[i].u.block.prev_ptr) + btix = (int) ops[i].u.block.type; + else + btix = (int) old_blk.type; + aix = state->trace_info.block_type[btix]->allocator; + + no = ((!old_blk.pointer && ops[i].u.block.new_ptr) + ? 1 + : ((old_blk.pointer && !ops[i].u.block.new_size) + ? -1 + : 0)); + + update_realloc_op(&state->info.btype[btix], size, prev_size, no); + update_realloc_op(&state->info.allctr[aix], size, prev_size, no); + update_realloc_op(&state->info.total, size, prev_size, no); + + break; + } + case EMTP_FREE: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + + if (!ops[i].u.block.prev_ptr) + continue; + + res = emtbt_free_op(state->block_table, &ops[i], &old_blk); + if (res != 0) + ERR_RET(res); + + prev_size = old_blk.size; + btix = (int) old_blk.type; + aix = state->trace_info.block_type[btix]->allocator; + + + update_free_op(&state->info.btype[btix], prev_size); + update_free_op(&state->info.allctr[aix], prev_size); + update_free_op(&state->info.total, prev_size); + + break; + + case EMTP_CARRIER_ALLOC: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + + aix = (int) ops[i].u.block.type; + + crrix = (int) ops[i].u.block.carrier_type; + if (!state->carrier_table[crrix]) { + state->carrier_table[crrix] = mk_block_table(state); + if (!state->carrier_table[crrix]) + ERR_RET(ENOMEM); + } + crr_table = state->carrier_table[crrix]; + + if (!ops[i].u.block.new_ptr) + continue; + + res = emtbt_alloc_op(crr_table, &ops[i]); + if (res != 0) + ERR_RET(res); + + size = ops[i].u.block.new_size; + + if (state->info.allctr_usd_crr[aix]) + update_alloc_op(state->info.allctr_usd_crr[aix], size); + if (state->info.allctr_prv_crr[crrix]) + update_alloc_op(state->info.allctr_prv_crr[crrix], size); + update_alloc_op(&state->info.allctr[crrix], size); + + break; + + case EMTP_CARRIER_REALLOC: { + int no; +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + + crrix = (int) ops[i].u.block.carrier_type; + if (!state->carrier_table[crrix]) { + state->carrier_table[crrix] = mk_block_table(state); + if (!state->carrier_table[crrix]) + ERR_RET(ENOMEM); + } + crr_table = state->carrier_table[crrix]; + + res = emtbt_realloc_op(crr_table, &ops[i], &old_blk); + if (res != 0) + ERR_RET(res); + + size = ops[i].u.block.new_size; + prev_size = old_blk.size; + + if (!ops[i].u.block.prev_ptr) + aix = (int) ops[i].u.block.type; + else + aix = (int) old_blk.type; + + + no = ((!old_blk.pointer && ops[i].u.block.new_ptr) + ? 1 + : ((old_blk.pointer && !ops[i].u.block.new_size) + ? -1 + : 0)); + + if (state->info.allctr_usd_crr[aix]) + update_realloc_op(state->info.allctr_usd_crr[aix], + size, + prev_size, + no); + if (state->info.allctr_prv_crr[crrix]) + update_realloc_op(state->info.allctr_prv_crr[crrix], + size, + prev_size, + no); + update_realloc_op(&state->info.allctr[crrix], + size, + prev_size, + no); + break; + } + case EMTP_CARRIER_FREE: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + + crrix = (int) ops[i].u.block.carrier_type; + crr_table = state->carrier_table[crrix]; + if (!crr_table) + ERR_RET(EMTBT_FREE_NOBLK_ERROR); + + if (!ops[i].u.block.prev_ptr) + continue; + + res = emtbt_free_op(crr_table, &ops[i], &old_blk); + if (res != 0) + ERR_RET(res); + + prev_size = old_blk.size; + aix = (int) old_blk.type; + + if (state->info.allctr_usd_crr[aix]) + update_free_op(state->info.allctr_usd_crr[aix], prev_size); + if (state->info.allctr_prv_crr[crrix]) + update_free_op(state->info.allctr_prv_crr[crrix], prev_size); + update_free_op(&state->info.allctr[crrix], prev_size); + + break; + + case EMTP_STOP: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + state->info.stop_reason = EMTP_STOP; + state->info.stop_time.secs = ops[i].time.secs; + state->info.stop_time.usecs = ops[i].time.usecs; + print_info(state, ops[i].time.secs, NULL); + return EM_EXIT_RESULT; + case EMTP_EXIT: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + state->info.stop_reason = EMTP_EXIT; + state->info.exit_status = ops[i].u.exit_status; + state->info.stop_time.secs = ops[i].time.secs; + state->info.stop_time.usecs = ops[i].time.usecs; + print_info(state, ops[i].time.secs, NULL); + return EM_EXIT_RESULT; + default: +#if PRINT_OPERATIONS + print_op(state, &ops[i]); +#endif + /* Ignore not understood operation */ + break; + } + } + return 0; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * * + * * +\* */ + +static const char * +error_string(int error) +{ + const char *str; + const char *error_str; + static const char unknown_error[] = "Unknown error"; + + error_str = unknown_error; + + if (error > 0) { + char *str = strerror(error); + if (str) + error_str = str; + } + else if (error < 0) { + str = emtp_error_string(error); + if (!str) { + str = emtbt_error_string(error); + if (!str) { + switch (error) { + case EM_TRUNCATED_TRACE_ERROR: + error_str = "Truncated trace"; + break; + case EM_INTERNAL_ERROR: + error_str = "emem internal error"; + break; + default: + break; + } + } + } + + if (str) + error_str = str; + } + + return error_str; +} + +static void +error(int res) +{ + error_msg(res, NULL); +} + +static void +error_msg(int res, char *msg) +{ + fprintf(stderr, + "emem: %s%sFatal error: %s (%d)\n", + msg ? msg : "", + msg ? ": ": "", + error_string(res), + res); + exit(1); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * * + * * +\* */ + +#if EMEM_d_SWITCH + +static size_t +write_output_filename(usgnd_int_8 *ptr, + char *dirname, + char *nodename, + char *hostname, + char *datetime, + char *pid) +{ + size_t sz = 0; + usgnd_int_8 *p = ptr; + usgnd_int_8 **pp = ptr ? &p : NULL; + sz += write_str(pp, dirname); + if (pp) *((*pp)++) = DIR_SEP_CHAR; + sz++; + sz += write_str(pp, nodename); + sz += write_str(pp, "_"); + sz += write_str(pp, hostname); + sz += write_str(pp, "_"); + sz += write_str(pp, datetime); + sz += write_str(pp, "_"); + sz += write_str(pp, pid); + sz += write_str(pp, EM_OUTPUT_FILE_SUFFIX); + if (pp) *((*pp)++) = '\0'; + sz++; + return sz; +} + +static char * +make_output_filename(em_state *state) +{ + char *fname; + size_t fname_size; + char *nodename = state->trace_info.nodename; + char *hostname = state->trace_info.hostname; + char *pid = state->trace_info.pid; + char dt_buf[20]; + char *date_time = NULL; + + if (*nodename == '\0') + nodename = "nonode"; + if (*hostname == '\0') + hostname = "nohost"; + if (!state->trace_info.start_time.day) + date_time = "notime"; + else { + sprintf(dt_buf, + "%d-%2.2d-%2.2d_%2.2d.%2.2d.%2.2d", + state->trace_info.start_time.year % 10000, + state->trace_info.start_time.month % 100, + state->trace_info.start_time.day % 100, + state->trace_info.start_time.hour % 100, + state->trace_info.start_time.minute % 100, + state->trace_info.start_time.second % 100); + date_time = &dt_buf[0]; + } + if (*pid == '\0') + pid = "nopid"; + + fname = (*state->alloc)(write_output_filename(NULL, + state->output.dir_name, + nodename, + hostname, + date_time, + pid)); + if (!fname) + return NULL; + + (void) write_output_filename(fname, + state->output.dir_name, + nodename, + hostname, + date_time, + pid); + return fname; +} + +#endif + +static int +complete_state(em_state *state) +{ + int i, j, vpo, vpl; + void * (*allocp)(size_t); + void * (*reallocp)(void *, size_t); + void (*freep)(void *); + size_t size = sizeof(emtp_info); + + if (!emtp_get_info(&state->trace_info, &size, state->trace_state) + || size < sizeof(emtp_info)) + return EM_INTERNAL_ERROR; + +#if EMEM_d_SWITCH + + if (!state->output.stream) { + char *fname = make_filename(state); + mutex_lock(state->output.go.mutex); + state->output.stream = fopen(fname, "w"); + if (!state->output.stream) { + disconnect_queue_reader(&state->input.queue); + disconnect_queue_writer(&state->output.queue); + } + cond_signal(state->output.go.cond); + mutex_unlock(state->output.go.mutex); + (*state->free)((void *) fname); + if (!state->output.stream) + return EIO; + } + +#endif + + allocp = state->alloc; + reallocp = state->realloc; + freep = state->free; + + + state->carrier_table = (*allocp)((state->trace_info.max_allocator_ix+2) + * sizeof(emtbt_table *)); + if (!state->carrier_table) + return ENOMEM; + state->carrier_table++; + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) + state->carrier_table[i] = NULL; + + + state->block_table = mk_block_table(state); + state->info.btype = (*allocp)((state->trace_info.max_block_type_ix+2) + * sizeof(em_mem_info)); + state->info.allctr = (*allocp)((state->trace_info.max_allocator_ix+2) + * sizeof(em_mem_info)); + if (!state->block_table || !state->info.btype || !state->info.allctr) + return ENOMEM; + + state->info.btype++; + state->info.allctr++; + + state->info.allctr_prv_crr + = (*allocp)((state->trace_info.max_allocator_ix+2) + * sizeof(em_mem_info *)); + if (!state->info.allctr_prv_crr) + return ENOMEM; + state->info.allctr_prv_crr++; + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) + state->info.allctr_prv_crr[i] = NULL; + + state->info.allctr_usd_crr + = (*allocp)((state->trace_info.max_allocator_ix+2) + * sizeof(em_mem_info *)); + if (!state->info.allctr_usd_crr) + return ENOMEM; + state->info.allctr_usd_crr++; + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) + state->info.allctr_usd_crr[i] = NULL; + + if (state->output.all_btypes) { + if (state->output.btypes) + (*state->free)((void *) state->output.btypes); + state->output.no_btypes = state->trace_info.max_block_type_ix + 2; + state->output.btypes = (*allocp)(state->output.no_btypes + * sizeof(em_output_types)); + if (!state->output.btypes) + return ENOMEM; + } + + if (state->output.all_allctrs) { + if (state->output.allctrs) + (*state->free)((void *) state->output.allctrs); + state->output.no_allctrs = state->trace_info.max_allocator_ix + 2; + state->output.allctrs = (*allocp)(state->output.no_allctrs + * sizeof(em_output_types)); + if (!state->output.allctrs) + return ENOMEM; + } + + for (i = -1; i <= state->trace_info.max_block_type_ix; i++) { + /* Save block type if we should print info about it */ + emtp_block_type *btp = state->trace_info.block_type[i]; + reset_mem_info(&state->info.btype[i]); + if (state->output.no_btypes) { + if (state->output.all_btypes) { + state->output.btypes[i+1].name = btp->name; + state->output.btypes[i+1].ix = btp->valid ? i : -1; + } + else { + for (j = 0; j < state->output.no_btypes; j++) + if (strcmp(btp->name, state->output.btypes[j].name) == 0) { + state->output.btypes[j].ix = i; + break; + } + } + } + } + + /* Remove invalid block types */ + if (state->output.no_btypes) { + for (i = 0, j = 0; i < state->output.no_btypes; i++) { + if (state->output.btypes[i].ix >= 0) { + state->output.btypes[j].name = state->output.btypes[i].name; + state->output.btypes[j].ix = state->output.btypes[i].ix; + j++; + } + } + state->output.no_btypes = j; + } + + for (i = -1; i <= state->trace_info.max_allocator_ix; i++) { + /* Save allocator if we should print info about it */ + emtp_allocator *ap = state->trace_info.allocator[i]; + reset_mem_info(&state->info.allctr[i]); + if (state->output.no_allctrs) { + if (state->output.all_allctrs) { + state->output.allctrs[i+1].name = ap->name; + state->output.allctrs[i+1].ix = ap->valid ? i : -1; + } + else { + for (j = 0; j < state->output.no_allctrs; j++) + if (strcmp(ap->name, state->output.allctrs[j].name) == 0) { + state->output.allctrs[j].ix = i; + break; + } + } + } + + /* Allocate em_mem_info if used carrier info is available */ + if (ap->flags & EMTP_ALLOCATOR_FLAG_HAVE_USED_CARRIERS_INFO + || (i == state->trace_info.segment_ix + && state->trace_info.have_segment_carrier_info)) { + state->info.allctr_usd_crr[i] + = (em_mem_info *) (*allocp)(sizeof(em_mem_info)); + if (!state->info.allctr_usd_crr[i]) + return ENOMEM; + reset_mem_info(state->info.allctr_usd_crr[i]); + } + + /* Allocate em_mem_info for carrier providers */ + if (ap->carrier.provider) { + sgnd_int_32 j; + for (j = 0; j < ap->carrier.no_providers; j++) { + sgnd_int_32 crr_prvdr = ap->carrier.provider[j]; + if (!state->info.allctr_prv_crr[crr_prvdr]) { + state->info.allctr_prv_crr[crr_prvdr] + = (em_mem_info *) (*allocp)(sizeof(em_mem_info)); + if (!state->info.allctr_prv_crr[crr_prvdr]) + return ENOMEM; + reset_mem_info(state->info.allctr_prv_crr[crr_prvdr]); + } + } + } + } + + /* Remove invalid allocators */ + if (state->output.no_allctrs) { + for (i = 0, j = 0; i < state->output.no_allctrs; i++) { + if (state->output.allctrs[i].ix >= 0) { + state->output.allctrs[j].name = state->output.allctrs[i].name; + state->output.allctrs[j].ix = state->output.allctrs[i].ix; + j++; + } + } + state->output.no_allctrs = j; + } + + if (state->output.no_btypes) { + state->output.btypes = (*reallocp)(state->output.btypes, + sizeof(em_output_types) + * state->output.no_btypes); + if (!state->output.btypes) + return ENOMEM; + } + + if (state->output.no_allctrs) { + state->output.allctrs = (*reallocp)(state->output.allctrs, + sizeof(em_output_types) + * state->output.no_allctrs); + if (!state->output.allctrs) + return ENOMEM; + } + + + vpo = 1; + if (state->output.max_min_values) + vpo += 2; + if (state->output.block_counts) { + vpo++; + if (state->output.max_min_values) + vpo += 2; + } + if (state->output.op_counts) + vpo += 3; + + state->output.values_per_object = vpo; + + vpl = 0; + vpl++; /* time */ + if (state->output.total) { + vpl += vpo; /* total allocated */ + if (state->trace_info.have_segment_carrier_info) { + vpl += vpo; /* total carriers */ + vpl += vpo; /* cached carriers */ + } + } + for (i = 0; i < state->output.no_allctrs; i++) { + vpl += vpo; /* allocated */ + if (state->trace_info.have_carrier_info) { + if (state->info.allctr_prv_crr[state->output.allctrs[i].ix]) + vpl += vpo; /* provided carriers */ + vpl += vpo; /* used carriers */ + } + } + vpl += state->output.no_btypes*vpo; /* allocated */ + + state->output.values_per_line = vpl; + + state->output.header_size = write_header(state, NULL, 1); + state->output.header = (*state->alloc)(state->output.header_size + 1); + if (!state->output.header) + return ENOMEM; + size = write_header(state, state->output.header, 1); + ASSERT(state->output.header_size == size); + return 0; +} + +static int +process_trace(em_state *state) +{ + emtp_operation ops[EM_NO_OF_OPS]; + int res; + size_t ops_len; + em_area area; + + while (1) { + get_next_read_area(&area, state, &state->input.queue); + if (!area.size) + return EM_TRUNCATED_TRACE_ERROR; + res = emtp_parse(state->trace_state, + &area.ptr, &area.size, + NULL, 0, NULL); + if (res == EMTP_HEADER_PARSED) + break; + if (res == EMTP_NEED_MORE_TRACE) + continue; + + if (res < 0) + return res; + else + return EM_TRUNCATED_TRACE_ERROR; + } + + res = complete_state(state); + if (res != 0) + return res; + + print_main_header(state); + + while (1) { + if (!area.size) { + get_next_read_area(&area, state, &state->input.queue); + if (!area.size) + return EM_TRUNCATED_TRACE_ERROR; + + } + + + while (area.size) { + ops_len = EM_NO_OF_OPS; + res = emtp_parse(state->trace_state, + &area.ptr, &area.size, + ops, sizeof(emtp_operation), &ops_len); + if (res < 0) + return res; + + res = insert_operations(state, ops, ops_len); + if (res != 0) + return res; + + } + + } + +} + +static void +usage(char *sw, char *error) +{ + int status = 0; + FILE *filep = stdout; +#ifdef __WIN32__ +#define SW_CHAR "/" +#else +#define SW_CHAR "-" +#endif + + if (error) { + ASSERT(sw); + status = 1; + filep = stderr; + fprintf(filep, "emem: %s: %s\n", sw, error); + } + fprintf(filep, + "Usage: emem " +#if EMEM_A_SWITCH + "[" SW_CHAR "A <ALLOCATOR>] " +#endif + "[" SW_CHAR "a <ALLOCATOR>] " + "[" SW_CHAR "b <BLOCK TYPE>] " +#if EMEM_C_SWITCH + "[" SW_CHAR "C <CLASS>] " +#endif +#if EMEM_c_SWITCH + "[" SW_CHAR "c <CLASS>] " +#endif + "{" +#if EMEM_d_SWITCH + SW_CHAR "d <DIRNAME>|" +#endif + SW_CHAR "f <FILENAME>} " + "[" SW_CHAR "h] " + "[" SW_CHAR "i <SECONDS>] " + "[" SW_CHAR "m] " + "[" SW_CHAR "n] " + "[" SW_CHAR "o] " + "{" SW_CHAR "p <PORT>} " + "[" SW_CHAR "t] " + "[" SW_CHAR "v] " + "\n"); + if (error) + exit(1); + else { + char *help_str = + "\n" + " [] - switch is allowed any number of times\n" + " {} - switch is allowed at most one time\n" +#if EMEM_d_SWITCH + " | - exclusive or\n" +#endif + "\n" + " Switches:\n" +#if EMEM_A_SWITCH + " " SW_CHAR "a <A> - display info about Allocator <A> and all block types using <A>\n" +#endif + " " SW_CHAR "a <A> - display info about allocator <A>\n" + " " SW_CHAR "b <B> - display info about block type <B>\n" +#if EMEM_C_SWITCH + " " SW_CHAR "C <C> - display info about class <C> and all block types in class <C>\n" +#endif +#if EMEM_c_SWITCH + " " SW_CHAR "b <B> - display info about class <C>\n" +#endif +#if EMEM_d_SWITCH + " " SW_CHAR "d <D> - run as daemon and set output directory to <D>\n" +#endif + " " SW_CHAR "f <F> - set output file to <F>\n" + " " SW_CHAR "h - display help and exit\n" + " " SW_CHAR "i <I> - set display interval to <I> seconds\n" + " " SW_CHAR "m - display max/min values\n" + " " SW_CHAR "n - display block/carrier/segment count values\n" + " " SW_CHAR "o - display operation count values\n" + " " SW_CHAR "p <P> - set listen port to <P>\n" + " " SW_CHAR "t - display info about total values\n" + " " SW_CHAR "v - verbose output\n"; + fprintf(filep, help_str); + exit(0); + } + +#undef SW_CHAR +} + + +static void +parse_args(em_state *state, int argc, char *argv[]) +{ + int port; + int i; + + port = -1; + + i = 1; + while (i < argc) { + if ((argv[i][0] != '-' && argv[i][0] != '/') || argv[i][2] != '\0') { + unknown_switch: + usage(argv[i], "unknown switch"); + } + + switch (argv[i][1]) { +#if EMEM_A_SWITCH + case 'A': /* TODO: Allocator + blocktypes using allocator */ +#endif + case 'a': + if (i + 1 >= argc) + usage(argv[i], "missing allocator"); + i++; + if (state->output.all_allctrs || strcmp(argv[i],"all") == 0) { + state->output.all_allctrs = 1; + break; + } + + if (!state->output.allctrs) + state->output.allctrs + = (*state->alloc)(sizeof(em_output_types)*argc/2); + if (!state->output.allctrs) + error(ENOMEM); + state->output.allctrs[state->output.no_allctrs].name = argv[i]; + state->output.allctrs[state->output.no_allctrs].ix = -1; + state->output.no_allctrs++; + break; + case 'b': + if (i + 1 >= argc) + usage(argv[i], "missing block type"); + i++; + if (state->output.all_btypes || strcmp(argv[i],"all") == 0) { + state->output.all_btypes = 1; + break; + } + + if (!state->output.btypes) + state->output.btypes + = (*state->alloc)(sizeof(em_output_types)*argc/2); + if (!state->output.btypes) + error(ENOMEM); + state->output.btypes[state->output.no_btypes].name = argv[i]; + state->output.btypes[state->output.no_btypes].ix = -1; + state->output.no_btypes++; + break; +#if EMEM_C_SWITCH +#endif +#if EMEM_c_SWITCH + case 'c': + break; +#endif +#if EMEM_d_SWITCH + case 'd': { + char *p; + char *fname; + if (state->output.dir_name) + usage(argv[i], "directory already set"); + if (state->output.file_name) + usage(argv[i], "file name already set"); + if (i + 1 >= argc) + usage(argv[i], "missing directory name"); + state->output.dir_name = argv[i+1]; + fname = (*state->alloc)(strlen(state->output.dir_name) + + 1 + + strlen(EM_ERL_CMD_FILE_NAME) + + 1); + state->output.go.mutex = (*state->alloc)(sizeof(ethr_mutex)); + state->output.go.cond = (*state->alloc)(sizeof(ethr_cond)); + if (!fname || !state->output.go.mutex || !state->output.go.cond) + error(ENOMEM); + p = fname; + (void) write_str(&p, state->output.dir_name); + *(p++) = DIR_SEP_CHAR; + (void) write_str(&p, EM_ERL_CMD_FILE_NAME); + *(p++) = '\0'; + state->output.erl_cmd_file = fopen(fname, "w"); + if (!state->output.erl_cmd_file) + usage(argv[i], "cannot create files in directory"); + (*state->free)((void *) fname); + state->output.stream = NULL; + mutex_init(state->output.go.mutex); + cond_init(state->output.go.cond); + i++; + break; + } +#endif + case 'f': +#if EMEM_d_SWITCH + if (state->output.dir_name) + usage(argv[i], "directory already set"); +#endif + if (state->output.file_name) + usage(argv[i], "file name already set"); + if (i + 1 >= argc) + usage(argv[i], "missing file name"); + state->output.file_name = argv[i+1]; + state->output.stream = fopen(state->output.file_name, "w"); + if (!state->output.stream) + usage(argv[i], "cannot create file"); + if (setvbuf(state->output.stream, NULL, _IONBF, 0) != 0) { + fprintf(stderr, + "emem: failed to set file %s in unbuffered mode\n", + state->output.file_name); + exit(1); + } + i++; + break; + case 'h': + usage(NULL, NULL); + break; + case 'i': { + int interval; + if (argv[i][2]) + goto unknown_switch; + + if (i + 1 >= argc) + usage(argv[i], "missing interval"); + interval = atoi(argv[i+1]); + if (interval < 1) + usage(argv[i], "bad interval"); + i++; + state->output.next_print_inc = interval; + break; + } + case 'm': + state->output.max_min_values = 1; + break; + case 'n': + state->output.block_counts = 1; + break; + case 'o': + state->output.op_counts = 1; + break; + case 'p': + if (state->input.listen_port) + usage(argv[i], "port already set"); + if (i + 1 >= argc) + usage(argv[i], "missing port number"); + port = atoi(argv[i+1]); + if (port <= 1024 || port >= (1 << 16)) + usage(argv[i], "bad port number"); + i++; + state->input.listen_port = (usgnd_int_16) port; + break; + case 't': + state->output.total = 1; + break; + case 'v': + state->output.verbose = 1; + break; + default: + goto unknown_switch; + } + i++; + } + + if (!state->output.allctrs && !state->output.btypes) + state->output.total = 1; +} + +static int +init_connection(em_state *state) +{ + int res; + SOCKET lsock; + SOCKET sock = INVALID_SOCKET; + struct sockaddr_in my_addr; + int oth_addr_len; + struct sockaddr_in oth_addr; +#ifdef __WIN32__ + WORD wVersionRequested = MAKEWORD(2,0); + WSADATA wsaData; + + if (WSAStartup(wVersionRequested, &wsaData) != 0) + return EIO; + + if ((LOBYTE(wsaData.wVersion) != 2) || (HIBYTE(wsaData.wVersion) != 0)) + return EIO; +#endif + + do_socket: + sock = socket(AF_INET, SOCK_STREAM, 0); + if (IS_INVALID_SOCKET(sock)) { + res = GET_SOCK_ERRNO(); + if (res == EINTR) + goto do_socket; + goto error; + } + + memset((void *) &my_addr, 0, sizeof(struct sockaddr_in)); + + my_addr.sin_family = AF_INET; + my_addr.sin_addr.s_addr = htonl(INADDR_ANY); + my_addr.sin_port = htons(state->input.listen_port); + + do_bind: + if (bind(sock, (struct sockaddr*) &my_addr, sizeof(my_addr)) < 0) { + res = GET_SOCK_ERRNO(); + if (res == EINTR) + goto do_bind; + goto error; + } + + do_listen: + if (listen(sock, 1) < 0) { + res = GET_SOCK_ERRNO(); + if (res == EINTR) + goto do_listen; + goto error; + } + + lsock = sock; + state->input.socket = sock; + + res = print_emu_arg(state); + if (res != 0) + goto error; + + print_string(state, "> Waiting for emulator to connect... "); + + do_accept: + oth_addr_len = sizeof(oth_addr); + sock = accept(lsock, (struct sockaddr *) &oth_addr, &oth_addr_len); + if (IS_INVALID_SOCKET(sock)) { + res = GET_SOCK_ERRNO(); + if (res == EINTR) + goto do_accept; + sock = lsock; + goto error; + } + + print_string(state, "connected\n"); + + closesocket(lsock); + state->input.socket = sock; + + return 0; + + error: + if (!IS_INVALID_SOCKET(sock)) { + closesocket(sock); + state->input.socket = INVALID_SOCKET; + } + return res; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * IO threads * + * * +\* */ + +/* + * The input thread reads from a socket and puts the received data + * in the input buffer queue. + * + * Note: There is intentionally no flow control. If the emem program + * cannot process data as fast as it arrives, it is supposed + * to crash when hitting the maximum buffersize; otherwise, + * the traced emulator would be slowed down. + */ +static void * +input_thread_func(void *arg) +{ + int res; + char *edescr = NULL; + ssize_t recv_sz; + usgnd_int_max total_trace_size = 0; + em_state *state = (em_state *) arg; + em_area area = {NULL, 0}; + SOCKET sock = state->input.socket; + em_buf_queue *queue = &state->input.queue; + + while(1) { + get_next_write_area(&area, + state, + queue, + EM_MIN_TRACE_READ_SIZE); + + if (!area.ptr) { + res = ENOMEM; + edescr = "Input alloc"; + goto stop; + } + + do_recv: + if (is_queue_reader_disconnected(queue)) { + res = 0; + edescr = "Input"; + goto stop; + } + recv_sz = recv(sock, (void *) area.ptr, area.size, 0); + if (recv_sz <= 0) { + res = GET_SOCK_ERRNO(); + if (res == EINTR) + goto do_recv; + edescr = "Input recv"; + goto stop; + } + area.size = (size_t) recv_sz; + total_trace_size += (usgnd_int_max) recv_sz; + } + + stop: + state->input.error = res; + state->input.error_descr = edescr; + state->input.total_trace_size = total_trace_size; + disconnect_queue_writer(queue); + if (!IS_INVALID_SOCKET(state->input.socket)) { + closesocket(sock); + state->input.socket = INVALID_SOCKET; + } + return NULL; +} + + +static void * +output_thread_func(void *arg) +{ + em_state *state = (em_state *) arg; + em_area area = {NULL, 0}; + +#if EMEM_d_SWITCH + + if (state->output.go.mutex) { + mutex_lock(state->output.go.mutex); + while (!state->output.stream + && !is_queue_writer_disconnected(&state->output.queue)) + cond_wait(state->output.go.cond, state->output.go.mutex); + mutex_unlock(state->output.go.mutex); + + mutex_destroy(state->output.go.mutex); + (*state->free)((void *) state->output.go.mutex); + state->output.go.mutex = NULL; + cond_destroy(state->output.go.cond); + (*state->free)((void *) state->output.go.cond); + state->output.go.cond = NULL; + } + +#endif + + while (1) { + get_next_read_area(&area, state, &state->output.queue); + if (!area.size) { + disconnect_queue_reader(&state->output.queue); + if (is_queue_writer_disconnected(&state->output.queue)) + goto stop; + else + error_msg(EIO, "Output queue"); + } + if (fwrite((void *) area.ptr, + sizeof(usgnd_int_8), + area.size, + state->output.stream) != area.size) { + disconnect_queue_reader(&state->output.queue); + error_msg(0, "Write"); + } + } + + stop: + if (state->output.stream != stdout && state->output.stream != stderr) + fclose(state->output.stream); + return NULL; +} + + +int +main(int argc, char *argv[]) +{ + int res, ires, jres; + ethr_tid input_tid; + ethr_tid output_tid; + em_state *state; + + /* set stdout in unbuffered mode */ + if (setvbuf(stdout, NULL, _IONBF, 0) != 0) { + fprintf(stderr, "emem: failed to set stdout in unbuffered mode\n"); + exit(1); + } + + if (ethr_init(NULL) != 0) { + fprintf(stderr, "emem: failed to initialize thread package\n"); + exit(1); + } + + state = new_state(malloc, realloc, free); + if (!state) + error(ENOMEM); + + parse_args(state, argc, argv); + + res = ethr_thr_create(&output_tid, + output_thread_func, + (void *) state, + NULL); + if (res != 0) + error_msg(res, "Output thread create"); + +#ifdef DEBUG + print_string(state, "> [debug]\n"); +#endif +#ifdef PURIFY + print_string(state, "> [purify]\n"); +#endif +#ifdef QUANTIFY + print_string(state, "> [quantify]\n"); +#endif +#ifdef PURECOV + print_string(state, "> [purecov]\n"); +#endif + + res = init_connection(state); + if (res != 0) + error_msg(res, "Initialize connection"); + + res = ethr_thr_create(&input_tid, + input_thread_func, + (void *) state, + NULL); + if (res != 0) + error_msg(res, "Input thread create"); + + res = process_trace(state); + + disconnect_queue_reader(&state->input.queue); + + jres = ethr_thr_join(input_tid, NULL); + if (jres != 0) + error_msg(jres, "Input thread join"); + + if (res == EM_EXIT_RESULT) + print_main_footer(state); + disconnect_queue_writer(&state->output.queue); + + jres = ethr_thr_join(output_tid, NULL); + if (jres != 0) + error_msg(jres, "Output thread join"); + + ires = state->input.error; + + destroy_state(state); + +#ifdef __WIN32__ + WSACleanup(); +#endif + + switch (res) { + case EM_EXIT_RESULT: + res = 0; + break; + case EM_TRUNCATED_TRACE_ERROR: + error_msg(ires, state->input.error_descr); + break; + default: + error(res); + break; + } + + return 0; +} + + +#if PRINT_OPERATIONS +void +print_op(em_state *state, emtp_operation *op) +{ + +#if 0 + printf("%5" USGND_INT_32_FSTR ":%6.6" USGND_INT_32_FSTR " ", + op->time.secs, op->time.usecs); +#endif + if (state->trace_info.version.parser.major >= 2) { + + switch (op->type) { + case EMTP_ALLOC: + printf(" %" USGND_INT_MAX_FSTR " = alloc(%" USGND_INT_16_FSTR + ", %" USGND_INT_MAX_FSTR ")\n", + op->u.block.new_ptr, + op->u.block.type, + op->u.block.new_size); + break; + case EMTP_REALLOC: + printf(" %" USGND_INT_MAX_FSTR " = realloc(%" USGND_INT_16_FSTR + ", %" USGND_INT_MAX_FSTR ", %" USGND_INT_MAX_FSTR ")\n", + op->u.block.new_ptr, + op->u.block.type, + op->u.block.prev_ptr, + op->u.block.new_size); + break; + case EMTP_FREE: + printf(" free(%" USGND_INT_16_FSTR ", %" USGND_INT_MAX_FSTR ")" + "\n", + op->u.block.type, + op->u.block.prev_ptr); + break; + case EMTP_CARRIER_ALLOC: + printf(" %" USGND_INT_MAX_FSTR " = carrier_alloc(%" + USGND_INT_16_FSTR ", %" USGND_INT_16_FSTR ", %" + USGND_INT_MAX_FSTR ")\n", + op->u.block.new_ptr, + op->u.block.carrier_type, + op->u.block.type, + op->u.block.new_size); + break; + case EMTP_CARRIER_REALLOC: + printf(" %" USGND_INT_MAX_FSTR " = carrier_realloc(%" + USGND_INT_16_FSTR ", %" USGND_INT_16_FSTR ", %" + USGND_INT_MAX_FSTR ", %" USGND_INT_MAX_FSTR ")\n", + op->u.block.new_ptr, + op->u.block.carrier_type, + op->u.block.type, + op->u.block.prev_ptr, + op->u.block.new_size); + case EMTP_CARRIER_FREE: + printf(" carrier_free(%" USGND_INT_16_FSTR ", %" USGND_INT_16_FSTR + ", %" USGND_INT_MAX_FSTR ")\n", + op->u.block.carrier_type, + op->u.block.type, + op->u.block.prev_ptr); + break; + default: + printf(" op = %d\n", op->type); + break; + } + + } + else { + + switch (op->type) { + case EMTP_ALLOC: + printf(" %" USGND_INT_MAX_FSTR " = alloc(%" USGND_INT_MAX_FSTR ")" + "\n", + op->u.block.new_ptr, + op->u.block.new_size); + break; + case EMTP_REALLOC: + printf(" %" USGND_INT_MAX_FSTR " = realloc(%" USGND_INT_MAX_FSTR + ", %" USGND_INT_MAX_FSTR ")\n", + op->u.block.new_ptr, + op->u.block.prev_ptr, + op->u.block.new_size); + break; + case EMTP_FREE: + printf(" free(%" USGND_INT_MAX_FSTR ")\n", + op->u.block.prev_ptr); + break; + default: + printf(" op = %d\n", op->type); + break; + } + } + fflush(stdout); +} +#endif diff --git a/lib/tools/c_src/erl_memory_trace_block_table.c b/lib/tools/c_src/erl_memory_trace_block_table.c new file mode 100644 index 0000000000..9c19358f14 --- /dev/null +++ b/lib/tools/c_src/erl_memory_trace_block_table.c @@ -0,0 +1,761 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + + +/* + * Description: + * + * Author: Rickard Green + */ + +/* Headers to include ... */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "erl_memory_trace_block_table.h" +#include <errno.h> + +#undef HARD_DEBUG +#undef REALLY_HARD_DEBUG +#ifdef DEBUG +# define HARD_DEBUG 0 +# define REALLY_HARD_DEBUG 0 +#else +# define HARD_DEBUG 0 +# define REALLY_HARD_DEBUG 0 +#endif + +/* Some system specific defines ... */ +#if defined(__WIN32__) && !defined(__GNUC__) +# define INLINE __forceinline +#else +# ifdef __GNUC__ +# define INLINE __inline__ +# else +# define INLINE +# endif +#endif + +/* Our own assert() ... */ +#ifdef DEBUG +#define ASSERT(A) ((void) ((A) ? 1 : assert_failed(__FILE__, __LINE__, #A))) +#include <stdio.h> +static int assert_failed(char *f, int l, char *a) +{ + fprintf(stderr, "%s:%d: Assertion failed: %s\n", f, l, a); + abort(); + return 0; +} + +#else +#define ASSERT(A) ((void) 1) +#endif + + +#define EMTBT_BLOCKS_PER_POOL 1000 + +typedef struct emtbt_block_pool_ { + struct emtbt_block_pool_ *next; + emtbt_block blocks[1]; +} emtbt_block_pool; + +struct emtbt_table_ { + void * (*alloc)(size_t); + void * (*realloc)(void *, size_t); + void (*free)(void *); + int is_64_bit; + int no_blocks; + int no_of_buckets; + int max_used_buckets; + int min_used_buckets; + int used_buckets; + int current_size_index; + emtbt_block *blocks; + emtbt_block ** buckets; + + + /* Fixed size allocation of blocks */ + emtbt_block_pool *block_pools; + emtbt_block *free_blocks; + int blocks_per_pool; + +}; + + +static emtbt_block null_blk = {0}; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Block table * + * * +\* */ + +#if HARD_DEBUG +static void check_table(emtbt_table *table); +#endif + +static emtbt_block * +block_alloc_new_pool(emtbt_table *tab) +{ + size_t size; + emtbt_block_pool *poolp; + + size = sizeof(emtbt_block_pool) - sizeof(emtbt_block); + size += tab->blocks_per_pool*sizeof(emtbt_block); + + poolp = (*tab->alloc)(size); + + if (poolp) { + int i; + emtbt_block *blks; + + poolp->next = tab->block_pools; + tab->block_pools = poolp; + + blks = (emtbt_block *) poolp->blocks; + + for (i = 1; i < tab->blocks_per_pool - 1; i++) + blks[i].next = &blks[i + 1]; + blks[tab->blocks_per_pool - 1].next = NULL; + tab->free_blocks = &blks[1]; + + return &blks[0]; + } + return NULL; +} + +static INLINE emtbt_block * +block_alloc(emtbt_table *tab) +{ + emtbt_block *res; +#if HARD_DEBUG + check_table(tab); +#endif + + if (tab->free_blocks) { + res = tab->free_blocks; + tab->free_blocks = tab->free_blocks->next; + } + else { + res = block_alloc_new_pool(tab); + } + +#ifdef DEBUG + res->next = ((emtbt_block *) 0xfffffff0); + res->prev = ((emtbt_block *) 0xfffffff0); + res->bucket = ((emtbt_block **) 0xfffffff0); +#endif + +#if HARD_DEBUG + check_table(tab); +#endif + + return res; +} + +static INLINE void +block_free(emtbt_table *tab, emtbt_block *bp) +{ + +#if HARD_DEBUG + check_table(tab); +#endif + + bp->next = tab->free_blocks; + tab->free_blocks = bp; + +#if HARD_DEBUG + check_table(tab); +#endif + + +} + +#define PRIME0 ((usgnd_int_32) 268438039) +#define PRIME1 ((usgnd_int_32) 268440479) +#define PRIME2 ((usgnd_int_32) 268439161) +#define PRIME3 ((usgnd_int_32) 268437017) + +#define MK_HASH(H, P, IS64) \ +do { \ + (H) = (P) & 0xff; \ + (H) *= PRIME0; \ + (H) += ((P) >> 8) & 0xff; \ + (H) *= PRIME1; \ + (H) += ((P) >> 16) & 0xff; \ + (H) *= PRIME2; \ + (H) += ((P) >> 24) & 0xff; \ + (H) *= PRIME3; \ + if ((IS64)) { \ + (H) += ((P) >> 32) & 0xff; \ + (H) *= PRIME0; \ + (H) += ((P) >> 40) & 0xff; \ + (H) *= PRIME1; \ + (H) += ((P) >> 48) & 0xff; \ + (H) *= PRIME2; \ + (H) += ((P) >> 56) & 0xff; \ + (H) *= PRIME3; \ + } \ +} while (0) + +static const int table_sizes[] = { + 3203, + 4813, + 6421, + 9643, + 12853, + 19289, + 25717, + 51437, + 102877, + 205759, + 411527, + 823117, + 1646237, + 3292489, + 6584983, + 13169977, + 26339969, + 52679969 +}; + +#if HARD_DEBUG + +static void +check_table(emtbt_table *table) +{ + int no_blocks; + emtbt_block *block, *prev_block; + + no_blocks = 0; + block = table->blocks; + ASSERT(!block || !block->prev); + prev_block = NULL; + while (block) { + usgnd_int_32 hash; + MK_HASH(hash, block->pointer, table->is_64_bit); + ASSERT(hash == block->hash); + ASSERT(block->bucket - table->buckets + == hash % table->no_of_buckets); + ASSERT(!prev_block || prev_block == block->prev); + prev_block = block; + block = block->next; + no_blocks++; + ASSERT(table->no_blocks >= no_blocks); + } + + ASSERT(table->no_blocks == no_blocks); + +#if REALLY_HARD_DEBUG + { + int i; + for (i = 0; i < table->no_of_buckets; i++) { + int bucket_end_found; + emtbt_block **bucket; + if (!table->buckets[i]) + continue; + bucket_end_found = 0; + bucket = &table->buckets[i]; + for (block = table->blocks; block; block = block->next) { + if (block->bucket == bucket) { + if (!block->prev || block->prev->bucket != bucket) + ASSERT(*bucket == block); + if (!block->next || block->next->bucket != bucket) + bucket_end_found++; + } + } + ASSERT(bucket_end_found); + } + } +#endif + +} + +#endif + +static INLINE void +link_block(emtbt_table *table, emtbt_block **bucket, emtbt_block *block) +{ + ASSERT(bucket); + + block->bucket = bucket; + if (*bucket) { + block->next = *bucket; + block->prev = (*bucket)->prev; + if (block->prev) + block->prev->next = block; + else + table->blocks = block; + block->next->prev = block; + } + else { + block->next = table->blocks; + block->prev = NULL; + if (table->blocks) + table->blocks->prev = block; + table->blocks = block; + table->used_buckets++; + + } + *bucket = block; + table->no_blocks++; + +#if HARD_DEBUG + check_table(table); +#endif + +} + +static int +resize_table(emtbt_table *table, int new_no_of_buckets) +{ +#ifdef DEBUG + int org_no_blocks; +#endif + int i; + emtbt_block *block; + emtbt_block **buckets; + + if (new_no_of_buckets < table->no_of_buckets) { + /* shrink never fails */ + buckets = (emtbt_block **) (*table->realloc)(table->buckets, + (sizeof(emtbt_block *) + * new_no_of_buckets)); + if (!buckets) + return 1; + } + else if (new_no_of_buckets > table->no_of_buckets) { + (*table->free)((void *) table->buckets); + buckets = (emtbt_block **) (*table->alloc)((sizeof(emtbt_block *) + * new_no_of_buckets)); + if (!buckets) + return 0; + } + else + return 1; + + table->buckets = buckets; + table->no_of_buckets = new_no_of_buckets; + table->max_used_buckets = (4*new_no_of_buckets)/5; + table->min_used_buckets = new_no_of_buckets/5; + table->used_buckets = 0; + +#ifdef DEBUG + org_no_blocks = table->no_blocks; +#endif + + table->no_blocks = 0; + + + for (i = 0; i < new_no_of_buckets; i++) + buckets[i] = NULL; + + block = table->blocks; + table->blocks = NULL; + + while (block) { + emtbt_block *next_block = block->next; + link_block(table,&table->buckets[block->hash%new_no_of_buckets],block); + block = next_block; + } + + ASSERT(org_no_blocks == table->no_blocks); + + return 1; +} + +static INLINE int +grow_table(emtbt_table *table) +{ + if (table->current_size_index < sizeof(table_sizes)/sizeof(int)) { + int new_size; + table->current_size_index++; + new_size = table_sizes[table->current_size_index]; + ASSERT(new_size > 0); + return resize_table(table, new_size); + } + return 1; +} + +static INLINE void +shrink_table(emtbt_table *table) +{ + if (table->current_size_index > 0) { + int new_size; + table->current_size_index--; + new_size = table_sizes[table->current_size_index]; + ASSERT(new_size > 0); + (void) resize_table(table, new_size); + } +} + +static INLINE emtbt_block * +peek_block(emtbt_table *table, usgnd_int_max ptr) +{ + emtbt_block **bucket; + emtbt_block *block; + usgnd_int_32 hash; + + MK_HASH(hash, ptr, table->is_64_bit); + + bucket = &table->buckets[hash % table->no_of_buckets]; + block = *bucket; + if (!block) + return NULL; + + while (block->bucket == bucket) { + ASSERT(block); + if (block->pointer == ptr) + return block; + if (!block->next) + break; + block = block->next; + } + return NULL; +} + +static INLINE int +insert_block(emtbt_table *table, emtbt_block *block) +{ + emtbt_block **bucket; + emtbt_block *tmp_block; + usgnd_int_32 hash; + usgnd_int_max p; + +#if HARD_DEBUG + check_table(table); +#endif + + if (table->used_buckets >= table->max_used_buckets) { + if(!grow_table(table)) + return -1; + } + + p = block->pointer; + + MK_HASH(hash, p, table->is_64_bit); + block->hash = hash; + + bucket = &table->buckets[hash % table->no_of_buckets]; + tmp_block = *bucket; + if (tmp_block) { + while (tmp_block->bucket == bucket) { + if (tmp_block->pointer == p) + return 0; + if (!tmp_block->next) + break; + tmp_block = tmp_block->next; + } + } + + link_block(table, bucket, block); + + ASSERT(block == peek_block(table, p)); + + + return 1; +} + +static INLINE void +delete_block(emtbt_table *table, emtbt_block *block) +{ + emtbt_block **bucket; + + if (!block) + return; + +#if HARD_DEBUG + check_table(table); +#endif + + bucket = block->bucket; + ASSERT(bucket); + + if (block->prev) + block->prev->next = block->next; + else + table->blocks = block->next; + + if (block->next) + block->next->prev = block->prev; + + if (block == *bucket) { + ASSERT(!block->prev || block->prev->bucket != bucket); + if (block->next && block->next->bucket == bucket) + *bucket = block->next; + else { + ASSERT(table->used_buckets > 0); + *bucket = NULL; + table->used_buckets--; + } + } +#ifdef DEBUG + + block->next = ((emtbt_block *) 0xfffffff0); + block->prev = ((emtbt_block *) 0xfffffff0); + block->bucket = ((emtbt_block **) 0xfffffff0); +#endif + + ASSERT(table->no_blocks > 0); + table->no_blocks--; + + if (table->used_buckets < table->min_used_buckets) + shrink_table(table); + +#if HARD_DEBUG + check_table(table); +#endif + +} + +static INLINE emtbt_block * +fetch_block(emtbt_table *table, usgnd_int_max ptr) +{ + emtbt_block *block; + + block = peek_block(table, ptr); + delete_block(table, block); + return block; +} + + +const char *emtbt_error_string(int error) +{ + switch (error) { + case EMTBT_ALLOC_XBLK_ERROR: + return "Allocation to an already existing block"; + case EMTBT_REALLOC_NOBLK_ERROR: + return "Reallocation of non-existing block"; + case EMTBT_REALLOC_XBLK_ERROR: + return "Reallocation to an already existing block"; + case EMTBT_REALLOC_BLK_TYPE_MISMATCH: + return "Block types mismatch when reallocating"; + case EMTBT_FREE_NOBLK_ERROR: + return "Deallocation of non-existing block"; + case EMTBT_FREE_BLK_TYPE_MISMATCH: + return "Block types mismatch when deallocating"; + case EMTBT_INTERNAL_ERROR: + return "Block table internal error"; + default: + return NULL; + } + + +} + + +emtbt_table * +emtbt_new_table(int is_64_bit, + void * (*alloc)(size_t), + void * (*realloc)(void *, size_t), + void (*free)(void *)) +{ + emtbt_table *tab = (*alloc)(sizeof(emtbt_table)); + if (tab) { + tab->alloc = alloc; + tab->realloc = realloc; + tab->free = free; + tab->is_64_bit = is_64_bit; + tab->no_blocks = 0; + tab->no_of_buckets = 0; + tab->max_used_buckets = 0; + tab->min_used_buckets = 0; + tab->used_buckets = 0; + tab->current_size_index = 0; + tab->blocks = NULL; + tab->buckets = NULL; + + tab->block_pools = NULL; + tab->free_blocks = NULL; + tab->blocks_per_pool = EMTBT_BLOCKS_PER_POOL; + + } + return tab; +} + +void +emtbt_destroy_table(emtbt_table *tab) +{ + void (*freep)(void *); + emtbt_block_pool *poolp1, *poolp2; + + freep = tab->free; + + /* Free block pools */ + poolp1 = tab->block_pools; + while (poolp1) { + poolp2 = poolp1; + poolp1 = poolp1->next; + (*freep)((void *) poolp2); + } + + if (tab->buckets) + (*freep)((void *) tab->buckets); + + (*freep)((void *) tab); +} + + +#define CP_BLK(TO, FROM) \ +do { \ + (TO)->time.secs = (FROM)->time.secs; \ + (TO)->time.usecs = (FROM)->time.usecs; \ + (TO)->type = (FROM)->type; \ + (TO)->pointer = (FROM)->pointer; \ + (TO)->size = (FROM)->size; \ +} while (0) + +int +emtbt_alloc_op(emtbt_table *tab, emtp_operation *op) +{ + int res; + emtbt_block *blk; + + blk = block_alloc(tab); + if (!blk) + return ENOMEM; + + blk->time.secs = op->time.secs; + blk->time.usecs = op->time.usecs; + blk->type = op->u.block.type; + blk->pointer = op->u.block.new_ptr; + blk->size = op->u.block.new_size; + + res = insert_block(tab, blk); + if (res < 0) + return ENOMEM; + else if (res == 0) + return EMTBT_ALLOC_XBLK_ERROR; + return 0; +} + +int +emtbt_realloc_op(emtbt_table *tab, emtp_operation *op, emtbt_block *old_blk) +{ + int res; + emtbt_block *blk; + + if (!op->u.block.new_size) { + /* freed block */ + + blk = fetch_block(tab, op->u.block.prev_ptr); + if (!blk) + return EMTBT_REALLOC_NOBLK_ERROR; + + CP_BLK(old_blk, blk); + block_free(tab, blk); + } + else { + + if (!op->u.block.new_ptr) { + /* failed operation */ + if (!op->u.block.prev_ptr) + CP_BLK(old_blk, &null_blk); + else { + blk = peek_block(tab, op->u.block.prev_ptr); + if (!blk) + return EMTBT_REALLOC_NOBLK_ERROR; + CP_BLK(old_blk, blk); +#if 0 + if (blk->type != op->u.block.type) + return EMTBT_REALLOC_BLK_TYPE_MISMATCH; +#endif + } + } + else if (!op->u.block.prev_ptr) { + /* new block */ + + CP_BLK(old_blk, &null_blk); + blk = block_alloc(tab); + if (!blk) + return ENOMEM; + blk->type = op->u.block.type; + blk->pointer = op->u.block.new_ptr; + blk->time.secs = op->time.secs; + blk->time.usecs = op->time.usecs; + blk->size = op->u.block.new_size; + + res = insert_block(tab, blk); + if (res < 0) + return ENOMEM; + else if (res == 0) + return EMTBT_REALLOC_XBLK_ERROR; + } + else if (op->u.block.new_ptr == op->u.block.prev_ptr) { + /* resized block */ + blk = peek_block(tab, op->u.block.prev_ptr); + if (!blk) + return EMTBT_REALLOC_NOBLK_ERROR; + CP_BLK(old_blk, blk); + blk->time.secs = op->time.secs; + blk->time.usecs = op->time.usecs; + blk->size = op->u.block.new_size; +#if 0 + if (blk->type != op->u.block.type) + return EMTBT_REALLOC_BLK_TYPE_MISMATCH; +#endif + } + else { + /* moved block */ + blk = fetch_block(tab, op->u.block.prev_ptr); + if (!blk) + return EMTBT_REALLOC_NOBLK_ERROR; + CP_BLK(old_blk, blk); + blk->time.secs = op->time.secs; + blk->time.usecs = op->time.usecs; + blk->pointer = op->u.block.new_ptr; + blk->size = op->u.block.new_size; + res = insert_block(tab, blk); + if (res < 0) + return ENOMEM; + else if (res == 0) + return EMTBT_REALLOC_XBLK_ERROR; +#if 0 + if (blk->type != op->u.block.type) + return EMTBT_REALLOC_BLK_TYPE_MISMATCH; +#endif + } + } + return 0; + +} + + +int +emtbt_free_op(emtbt_table *tab, emtp_operation *op, emtbt_block *old_blk) +{ + emtbt_block *blk; + + if (!op->u.block.prev_ptr) + CP_BLK(old_blk, &null_blk); + else { + + blk = fetch_block(tab, op->u.block.prev_ptr); + if (!blk) + return EMTBT_FREE_NOBLK_ERROR; + + CP_BLK(old_blk, blk); + block_free(tab, blk); +#if 0 + if (blk->type != op->u.block.type) + return EMTBT_FREE_BLK_TYPE_MISMATCH; +#endif + } + return 0; +} diff --git a/lib/tools/c_src/erl_memory_trace_block_table.h b/lib/tools/c_src/erl_memory_trace_block_table.h new file mode 100644 index 0000000000..1b1f23c16f --- /dev/null +++ b/lib/tools/c_src/erl_memory_trace_block_table.h @@ -0,0 +1,73 @@ +/* ``The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved via the world wide web at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Initial Developer of the Original Code is Ericsson Utvecklings AB. + * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings + * AB. All Rights Reserved.'' + * + * $Id$ + */ + + +/* + * Description: + * + * Author: Rickard Green + */ + +#ifndef ERL_MEMORY_TRACE_BLOCK_TABLE_H__ +#define ERL_MEMORY_TRACE_BLOCK_TABLE_H__ + +#include <stdlib.h> +#include "erl_fixed_size_int_types.h" +#include "erl_memory_trace_parser.h" + + +#define EMTBT_ALLOC_XBLK_ERROR (EMTP_MIN_ERROR - 1) +#define EMTBT_REALLOC_NOBLK_ERROR (EMTP_MIN_ERROR - 2) +#define EMTBT_REALLOC_XBLK_ERROR (EMTP_MIN_ERROR - 3) +#define EMTBT_REALLOC_BLK_TYPE_MISMATCH (EMTP_MIN_ERROR - 4) +#define EMTBT_FREE_NOBLK_ERROR (EMTP_MIN_ERROR - 5) +#define EMTBT_FREE_BLK_TYPE_MISMATCH (EMTP_MIN_ERROR - 6) +#define EMTBT_INTERNAL_ERROR (EMTP_MIN_ERROR - 7) + +#define EMTBT_MIN_ERROR EMTBT_INTERNAL_ERROR + + +typedef struct emtbt_block_ { + + struct emtbt_block_ * next; + struct emtbt_block_ * prev; + usgnd_int_32 hash; + struct emtbt_block_ ** bucket; + + struct { + usgnd_int_32 secs; + usgnd_int_32 usecs; + } time; + usgnd_int_16 type; + usgnd_int_max pointer; + usgnd_int_max size; +} emtbt_block; + +typedef struct emtbt_table_ emtbt_table; + +const char *emtbt_error_string(int); +emtbt_table *emtbt_new_table(int, + void * (*)(size_t), + void * (*)(void *, size_t), + void (*)(void *)); +void emtbt_destroy_table(emtbt_table *); +int emtbt_alloc_op(emtbt_table *tab, emtp_operation *op); +int emtbt_realloc_op(emtbt_table *, emtp_operation *, emtbt_block *); +int emtbt_free_op(emtbt_table *, emtp_operation *, emtbt_block *); + +#endif |