/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2003-2012. All Rights Reserved.
*
* 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 online 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.
*
* %CopyrightEnd%
*/
/*
* 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;
char *data;
char *data_end;
char *end;
size_t size;
char 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 {
char *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)
{
ethr_mutex_lock(mtx);
}
static INLINE void
mutex_unlock(ethr_mutex *mtx)
{
ethr_mutex_unlock(mtx);
}
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 != 0 && res != EINTR)
error_msg(res, "Cond wait");
}
static INLINE void
cond_signal(ethr_cond *cnd)
{
ethr_cond_signal(cnd);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* 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(char)
* (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(char **dstpp, char *srcp)
{
size_t i = 0;
if (dstpp)
while (srcp[i])
*((*dstpp)++) = srcp[i++];
else
while (srcp[i]) i++;
return i;
}
static size_t
write_strings(char **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(char **bufp, size_t *overflow, size_t width, char *str)
{
size_t i, sz, ws;
char *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++) = ' ';
}
i = 0;
while (str[i] && (overflow || p < endp))
*(p++) = str[i++];
while (ws) {
ws--;
*(p++) = ' ';
}
ASSERT(overflow || p == endp);
ASSERT(sz == (size_t) (p - *bufp));
*bufp = p;
return sz;
}
static size_t
write_obj_sub_titles(em_state *state, char **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, char *ptr, int trunc)
{
#define MIN_LTEXT_SZ 18
#define HEADER_EOL_STR "|\n"
char *p;
char **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, char **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, char **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};
char *p;
/* Get area */
get_next_write_area(&area,state,&state->output.queue,write_str(NULL,str));
p = 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, char *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;
char *p = ptr;
char **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};
char *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, "%s", 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)
{
char *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(char *ptr,
char *dirname,
char *nodename,
char *hostname,
char *datetime,
char *pid)
{
size_t sz = 0;
char *p = ptr;
char **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,
(usgnd_int_8 **)&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,
(usgnd_int_8 **)&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 {
fprintf(filep,
"\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");
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(char),
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 || ethr_late_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