/*
 * Copyright (c) 2001-2018 Mellanox Technologies, Ltd. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <fcntl.h>
#include <errno.h>
#include <list>
#include <signal.h>
#include <getopt.h>		/* getopt()*/
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <vector>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include "utils/rdtsc.h"
#include "vma/util/utils.h"
#include "vma/util/vma_stats.h"
#include "vma/util/sys_vars.h"

using namespace std;

typedef std::list<int> fd_list_t;


typedef struct {
	in_addr_t		mc_grp;
	fd_list_t		fd_list;
} mc_group_fds_t;

typedef enum {
	e_K = 1024,
	e_M = 1048576
} units_t;

#define MODULE_NAME				"vmastat"
#define log_msg(log_fmt, log_args...)		printf(MODULE_NAME  ": " log_fmt "\n", ##log_args)
#define log_err(log_fmt, log_args...)		fprintf(stderr,MODULE_NAME ": " log_fmt "\n", ##log_args)
#define log_system_err(log_fmt, log_args...)	fprintf(stderr,MODULE_NAME ": " log_fmt " (errno=%d %s)\n", ##log_args, errno, strerror(errno))
#define log_dbg(log_fmt, log_args...)		printf(MODULE_NAME ": " log_fmt "\n", ##log_args)

#define BASE_HEADERS_NUM		2
#define BASIC_STATS_LINES_NUM		2
#define	 UPPER_SHORT_VIEW_HEADER	" %-7s %42s %31s\n"
#define LOWER_SHORT_VIEW_HEADER		" %-7s %10s %7s %8s %7s %6s %7s %7s %7s %7s\n"
#define RX_SHORT_VIEW			" %-3d %-3s %10u %7u %8u %7u %6.1f %7u %7u %7u %7u\n"
#define TX_SHORT_VIEW			" %-3s %-3s %10u %7u %8u %7u %-6s %7u %7u %7u %7u\n"
#define IOMUX_FORMAT			"%-8s%-2s %-9s%u%-1s%u %-12s %-9s%-5u %-7s%-4u %-5s%-2.2f%-3s %-5s%d%-1s\n"

#define MEDIUM_HEADERS_NUM		3
#define MEDIUM_STATS_LINES_NUM		2
#define	 UPPER_MEDIUM_VIEW_HEADER	" %-7s %65s %31s\n"
#define MIDDLE_MEDIUM_VIEW_HEADER	" %-7s %10s %7s %8s %7s %6s%23s %7s %7s %7s %7s\n"
#define LOWER_MEDIUM_VIEW_HEADER	" %50s %6s  %6s  %6s \n"
#define RX_MEDIUM_VIEW			" %-3d %-3s %10u %7u %8u %7u %6.1f %6u  %6u  %6u %7u %7u %7u %7u\n"
#define TX_MEDIUM_VIEW			" %-3s %-3s %10u %7u %8u %7u %29s %7u %7u %7u %7u\n"
#define CYCLES_SEPARATOR		"-------------------------------------------------------------------------------\n" 
#define FORMAT_STATS_32bit		"%-20s %u\n"
#define FORMAT_STATS_64bit		"%-20s %llu %-3s\n"
#define FORMAT_RING_PACKETS 		"%-20s %zu / %zu [kilobytes/packets] %-3s\n"
#define FORMAT_RING_INTERRUPT		"%-20s %zu / %zu [requests/received] %-3s\n"
#define FORMAT_RING_MODERATION		"%-20s %u / %u [frames/usec period] %-3s\n"
#define FORMAT_RING_DM_STATS		"%-20s %zu / %zu / %zu [kilobytes/packets/oob] %-3s\n"
#define FORMAT_RING_TAP_NAME		"%-20s %s\n"
#define FORMAT_RING_MASTER  		"%-20s %p\n"

#define INTERVAL			1
#define BYTES_TRAFFIC_UNIT		e_K
#define SCREEN_SIZE			24 
#define MAX_BUFF_SIZE			256
#define PRINT_DETAILS_MODES_NUM		2	
#define VIEW_MODES_NUM			5
#define DEFAULT_DELAY_SEC		1
#define DEFAULT_CYCLES			0
#define DEFAULT_VIEW_MODE		e_basic
#define DEFAULT_DETAILS_MODE		e_totals
#define	 DEFAULT_PROC_IDENT_MODE	e_by_runn_proccess
#define VLOG_DETAILS_NUM		4
#define INIT_VMA_LOG_DETAILS		-1
#define NANO_TO_MICRO(n)		(((n) + 500) / 1000)
#define SEC_TO_MICRO(n)			((n) * 1000000)
#define TIME_DIFF_in_MICRO(start,end)	(SEC_TO_MICRO((end).tv_sec-(start).tv_sec) + \
					(NANO_TO_MICRO((end).tv_nsec-(start).tv_nsec)))
// printf formating when IP is in network byte ordering (for LITTLE_ENDIAN)
#define NETWORK_IP_PRINTQUAD_LITTLE_ENDIAN(ip)     		(uint8_t)((ip)&0xff), (uint8_t)(((ip)>>8)&0xff),(uint8_t)(((ip)>>16)&0xff),(uint8_t)(((ip)>>24)&0xff)

// printf formating when IP is in host byte ordering (for LITTLE_ENDIAN)
#define HOST_IP_PRINTQUAD_LITTLE_ENDIAN(ip)     		(uint8_t)(((ip)>>24)&0xff),(uint8_t)(((ip)>>16)&0xff),(uint8_t)(((ip)>>8)&0xff),(uint8_t)((ip)&0xff)


#if __BYTE_ORDER == __LITTLE_ENDIAN
/* The host byte order is the same as network byte order, so these functions are all just identity.  */
#  define NIPQUAD(ip)     		NETWORK_IP_PRINTQUAD_LITTLE_ENDIAN(ip)
#else
# if __BYTE_ORDER == __BIG_ENDIAN
#  define NIPQUAD(ip)     		HOST_IP_PRINTQUAD_LITTLE_ENDIAN(ip)
# endif
#endif

bool 		g_b_exit = false;
struct 		sigaction g_sigact;
uint8_t* 	g_fd_mask;
uint32_t 	g_fd_map_size = e_K;

//statistic file
FILE* g_stats_file = stdout;

void usage(const char *argv0)
{
	printf("\nVMA Statistics\n");
	printf("Usage:\n");
	printf("\t%s [-p pid] [-k directory] [-v view] [-d details] [-i interval] \n", argv0);
	printf("\n");
	printf("Defaults:\n");
	printf("\tfind_pid=enabled, directory=\"%s\", view=1, details=1, interval=1, \n", MCE_DEFAULT_STATS_SHMEM_DIR);
	printf("\n");
	printf("Options:\n");
	printf("  -p, --pid=<pid>\t\tShow VMA statistics for process with pid: <pid>\n");
	printf("  -k, --directory=<directory>\tSet shared memory directory path to <directory>\n");
	printf("  -n, --name=<application>\tShow VMA statistics for application: <application>\n");
	printf("  -f, --find_pid\t\tFind and show statistics for VMA instance running (default)\n");
	printf("  -F, --forbid_clean\t\tBy setting this flag inactive shared objects would not be removed\n");
	printf("  -i, --interval=<n>\t\tPrint report every <n> seconds\n");
	printf("  -c, --cycles=<n>\t\tDo <n> report print cycles and exit, use 0 value for infinite (default)\n");
	printf("  -v, --view=<1|2|3|4|5>\tSet view type:1- basic info,2- extra info,3- full info,4- mc groups,5- similar to 'netstat -tunaep'\n");
	printf("  -d, --details=<1|2>\t\tSet details mode:1- to see totals,2- to see deltas\t\t\n");
	printf("  -z, --zero\t\t\tZero counters\n");
	printf("  -l, --log_level=<level>\tSet VMA log level to <level>(one of: none/panic/error/warn/info/details/debug/fine/finer/all)\n");
	printf("  -S, --fd_dump=<fd> [<level>]\tDump statistics for fd number <fd> using log level <level>. use 0 value for all open fds.\n");
	printf("  -D, --details_level=<level>\tSet VMA log details level to <level>(0 <= level <= 3)\n");
	printf("  -s, --sockets=<list|range>\tLog only sockets that match <list> or <range>, format: 4-16 or 1,9 (or combination)\n");
	printf("  -V, --version\t\t\tPrint version\n");
	printf("  -h, --help\t\t\tPrint this help message\n");
}

void update_delta_stat(socket_stats_t* p_curr_stat, socket_stats_t* p_prev_stat)
{
	int delay = INTERVAL;
	p_prev_stat->counters.n_tx_sent_byte_count = (p_curr_stat->counters.n_tx_sent_byte_count - p_prev_stat->counters.n_tx_sent_byte_count) / delay;
	p_prev_stat->counters.n_tx_sent_pkt_count = (p_curr_stat->counters.n_tx_sent_pkt_count - p_prev_stat->counters.n_tx_sent_pkt_count) / delay;
	p_prev_stat->counters.n_tx_drops = (p_curr_stat->counters.n_tx_drops - p_prev_stat->counters.n_tx_drops) / delay;
	p_prev_stat->counters.n_tx_errors = (p_curr_stat->counters.n_tx_errors - p_prev_stat->counters.n_tx_errors) / delay;
	p_prev_stat->counters.n_tx_dummy = (p_curr_stat->counters.n_tx_dummy - p_prev_stat->counters.n_tx_dummy) / delay;
	p_prev_stat->counters.n_tx_os_bytes = (p_curr_stat->counters.n_tx_os_bytes - p_prev_stat->counters.n_tx_os_bytes) / delay;
	p_prev_stat->counters.n_tx_os_packets = (p_curr_stat->counters.n_tx_os_packets - p_prev_stat->counters.n_tx_os_packets) / delay;
	p_prev_stat->counters.n_tx_os_eagain = (p_curr_stat->counters.n_tx_os_eagain - p_prev_stat->counters.n_tx_os_eagain) / delay;
	p_prev_stat->counters.n_tx_os_errors = (p_curr_stat->counters.n_tx_os_errors - p_prev_stat->counters.n_tx_os_errors) / delay;
	p_prev_stat->counters.n_rx_bytes = (p_curr_stat->counters.n_rx_bytes - p_prev_stat->counters.n_rx_bytes) / delay;
	p_prev_stat->counters.n_rx_packets = (p_curr_stat->counters.n_rx_packets - p_prev_stat->counters.n_rx_packets) / delay;
	p_prev_stat->counters.n_rx_eagain = (p_curr_stat->counters.n_rx_eagain - p_prev_stat->counters.n_rx_eagain) / delay;
	p_prev_stat->counters.n_rx_errors = (p_curr_stat->counters.n_rx_errors - p_prev_stat->counters.n_rx_errors) / delay;
	p_prev_stat->counters.n_rx_os_bytes = (p_curr_stat->counters.n_rx_os_bytes - p_prev_stat->counters.n_rx_os_bytes) / delay;
	p_prev_stat->counters.n_rx_os_packets = (p_curr_stat->counters.n_rx_os_packets - p_prev_stat->counters.n_rx_os_packets) / delay;
	p_prev_stat->counters.n_rx_os_eagain = (p_curr_stat->counters.n_rx_os_eagain - p_prev_stat->counters.n_rx_os_eagain) / delay;
	p_prev_stat->counters.n_rx_os_errors = (p_curr_stat->counters.n_rx_os_errors - p_prev_stat->counters.n_rx_os_errors) / delay;
	p_prev_stat->counters.n_rx_poll_miss = (p_curr_stat->counters.n_rx_poll_miss - p_prev_stat->counters.n_rx_poll_miss) / delay;
	p_prev_stat->counters.n_rx_poll_hit = (p_curr_stat->counters.n_rx_poll_hit - p_prev_stat->counters.n_rx_poll_hit) / delay;
	p_prev_stat->n_rx_ready_byte_count = p_curr_stat->n_rx_ready_byte_count;
	p_prev_stat->n_tx_ready_byte_count = p_curr_stat->n_tx_ready_byte_count;
	p_prev_stat->n_rx_ready_byte_limit = p_curr_stat->n_rx_ready_byte_limit;
	p_prev_stat->counters.n_rx_ready_byte_max = p_curr_stat->counters.n_rx_ready_byte_max;
	p_prev_stat->counters.n_rx_ready_byte_drop = (p_curr_stat->counters.n_rx_ready_byte_drop - p_prev_stat->counters.n_rx_ready_byte_drop) / delay;
	p_prev_stat->counters.n_rx_ready_pkt_drop = (p_curr_stat->counters.n_rx_ready_pkt_drop - p_prev_stat->counters.n_rx_ready_pkt_drop) / delay;
	p_prev_stat->n_rx_ready_pkt_count = p_curr_stat->n_rx_ready_pkt_count;
	p_prev_stat->counters.n_rx_ready_pkt_max = p_curr_stat->counters.n_rx_ready_pkt_max;
	p_prev_stat->n_rx_zcopy_pkt_count = p_curr_stat->n_rx_zcopy_pkt_count;

	p_prev_stat->threadid_last_rx = p_curr_stat->threadid_last_rx;
	p_prev_stat->threadid_last_tx = p_curr_stat->threadid_last_tx;

	p_prev_stat->counters.n_rx_migrations = (p_curr_stat->counters.n_rx_migrations - p_prev_stat->counters.n_rx_migrations) / delay;
	p_prev_stat->counters.n_tx_migrations = (p_curr_stat->counters.n_tx_migrations - p_prev_stat->counters.n_tx_migrations) / delay;
	p_prev_stat->counters.n_tx_retransmits = (p_curr_stat->counters.n_tx_retransmits - p_prev_stat->counters.n_tx_retransmits) / delay;
}

void update_delta_iomux_stat(iomux_func_stats_t* p_curr_stats, iomux_func_stats_t* p_prev_stats)
{
	int delay = INTERVAL;
	if (p_curr_stats && p_prev_stats) {
		p_prev_stats->n_iomux_errors = (p_curr_stats->n_iomux_errors - p_prev_stats->n_iomux_errors) / delay;
		p_prev_stats->n_iomux_os_rx_ready = (p_curr_stats->n_iomux_os_rx_ready - p_prev_stats->n_iomux_os_rx_ready) / delay;
		p_prev_stats->n_iomux_poll_hit = (p_curr_stats->n_iomux_poll_hit - p_prev_stats->n_iomux_poll_hit) / delay;
		p_prev_stats->n_iomux_poll_miss = (p_curr_stats->n_iomux_poll_miss - p_prev_stats->n_iomux_poll_miss) / delay;
		p_prev_stats->n_iomux_rx_ready = (p_curr_stats->n_iomux_rx_ready - p_prev_stats->n_iomux_rx_ready) / delay;
		p_prev_stats->n_iomux_timeouts = (p_curr_stats->n_iomux_timeouts - p_prev_stats->n_iomux_timeouts) / delay;
		p_prev_stats->threadid_last = p_curr_stats->threadid_last;
	}
}

void update_delta_ring_stat(ring_stats_t* p_curr_ring_stats, ring_stats_t* p_prev_ring_stats)
{
	int delay = INTERVAL;
	if (p_curr_ring_stats && p_prev_ring_stats) {
		p_prev_ring_stats->n_rx_byte_count = (p_curr_ring_stats->n_rx_byte_count - p_prev_ring_stats->n_rx_byte_count) / delay;
		p_prev_ring_stats->n_rx_pkt_count = (p_curr_ring_stats->n_rx_pkt_count - p_prev_ring_stats->n_rx_pkt_count) / delay;
		p_prev_ring_stats->n_tx_byte_count = (p_curr_ring_stats->n_tx_byte_count - p_prev_ring_stats->n_tx_byte_count) / delay;
		p_prev_ring_stats->n_tx_pkt_count = (p_curr_ring_stats->n_tx_pkt_count - p_prev_ring_stats->n_tx_pkt_count) / delay;
		p_prev_ring_stats->n_tx_retransmits = (p_curr_ring_stats->n_tx_retransmits - p_prev_ring_stats->n_tx_retransmits) / delay;
		if (p_prev_ring_stats->n_type == RING_TAP) {
			memcpy(p_prev_ring_stats->tap.s_tap_name, p_curr_ring_stats->tap.s_tap_name, sizeof(p_curr_ring_stats->tap.s_tap_name));
			p_prev_ring_stats->tap.n_tap_fd = p_curr_ring_stats->tap.n_tap_fd;
			p_prev_ring_stats->tap.n_rx_buffers = p_curr_ring_stats->tap.n_rx_buffers;
			p_prev_ring_stats->tap.n_vf_plugouts = (p_curr_ring_stats->tap.n_vf_plugouts - p_prev_ring_stats->tap.n_vf_plugouts);
		} else {
			p_prev_ring_stats->simple.n_rx_interrupt_received = (p_curr_ring_stats->simple.n_rx_interrupt_received - p_prev_ring_stats->simple.n_rx_interrupt_received) / delay;
			p_prev_ring_stats->simple.n_rx_interrupt_requests = (p_curr_ring_stats->simple.n_rx_interrupt_requests - p_prev_ring_stats->simple.n_rx_interrupt_requests) / delay;
			p_prev_ring_stats->simple.n_rx_cq_moderation_count = p_curr_ring_stats->simple.n_rx_cq_moderation_count;
			p_prev_ring_stats->simple.n_rx_cq_moderation_period = p_curr_ring_stats->simple.n_rx_cq_moderation_period;
			p_prev_ring_stats->simple.n_tx_dev_mem_allocated = p_curr_ring_stats->simple.n_tx_dev_mem_allocated;
			p_prev_ring_stats->simple.n_tx_dev_mem_byte_count = (p_curr_ring_stats->simple.n_tx_dev_mem_byte_count - p_prev_ring_stats->simple.n_tx_dev_mem_byte_count) / delay;
			p_prev_ring_stats->simple.n_tx_dev_mem_pkt_count = (p_curr_ring_stats->simple.n_tx_dev_mem_pkt_count - p_prev_ring_stats->simple.n_tx_dev_mem_pkt_count) / delay;
			p_prev_ring_stats->simple.n_tx_dev_mem_oob = (p_curr_ring_stats->simple.n_tx_dev_mem_oob - p_prev_ring_stats->simple.n_tx_dev_mem_oob) / delay;
		}
	}
}

void update_delta_cq_stat(cq_stats_t* p_curr_cq_stats, cq_stats_t* p_prev_cq_stats)
{
	int delay = INTERVAL;
	if (p_curr_cq_stats && p_prev_cq_stats) {
		p_prev_cq_stats->n_rx_drained_at_once_max = p_curr_cq_stats->n_rx_drained_at_once_max;
		p_prev_cq_stats->n_rx_pkt_drop = (p_curr_cq_stats->n_rx_pkt_drop - p_prev_cq_stats->n_rx_pkt_drop) / delay;
		p_prev_cq_stats->n_rx_sw_queue_len = p_curr_cq_stats->n_rx_sw_queue_len;
		p_prev_cq_stats->n_buffer_pool_len = p_curr_cq_stats->n_buffer_pool_len;
	}
}

void update_delta_bpool_stat(bpool_stats_t* p_curr_bpool_stats, bpool_stats_t* p_prev_bpool_stats)
{
	int delay = INTERVAL;
	if (p_curr_bpool_stats && p_prev_bpool_stats) {
		p_prev_bpool_stats->n_buffer_pool_size = p_curr_bpool_stats->n_buffer_pool_size;
		p_prev_bpool_stats->n_buffer_pool_no_bufs = (p_curr_bpool_stats->n_buffer_pool_no_bufs - p_prev_bpool_stats->n_buffer_pool_no_bufs) / delay;
	}
}

void print_ring_stats(ring_instance_block_t* p_ring_inst_arr)
{
	ring_stats_t* p_ring_stats = NULL;
	char post_fix[3] = "";

	if (user_params.print_details_mode == e_deltas)
		strcpy(post_fix, "/s");

	for (int i = 0; i < NUM_OF_SUPPORTED_RINGS; i++) {
		if (p_ring_inst_arr[i].b_enabled) {
			p_ring_stats = &p_ring_inst_arr[i].ring_stats;
			printf("======================================================\n");

			printf("\t%s=[%u]\n", ring_type_str[p_ring_stats->n_type], i);

			if (p_ring_stats->p_ring_master) {
				printf(FORMAT_RING_MASTER, "Master:", p_ring_stats->p_ring_master);
			}

			printf(FORMAT_RING_PACKETS, "Tx Offload:", p_ring_stats->n_tx_byte_count/BYTES_TRAFFIC_UNIT, p_ring_stats->n_tx_pkt_count, post_fix);
			printf(FORMAT_RING_PACKETS, "Rx Offload:", p_ring_stats->n_rx_byte_count/BYTES_TRAFFIC_UNIT, p_ring_stats->n_rx_pkt_count, post_fix);

			if (p_ring_stats->n_tx_retransmits) {
				printf(FORMAT_STATS_64bit, "Retransmissions:", (unsigned long long int)p_ring_stats->n_tx_retransmits, post_fix);
			}

			if (p_ring_stats->n_type == RING_TAP) {
				printf(FORMAT_STATS_32bit, "Rx Buffers:", p_ring_stats->tap.n_rx_buffers);
				if (p_ring_stats->tap.n_vf_plugouts) {
					printf(FORMAT_STATS_32bit, "VF Plugouts:", p_ring_stats->tap.n_vf_plugouts);
				}
				printf(FORMAT_STATS_32bit, "Tap fd:", p_ring_stats->tap.n_tap_fd);
				printf(FORMAT_RING_TAP_NAME, "Tap Device:", p_ring_stats->tap.s_tap_name);
			} else {
				if (p_ring_stats->simple.n_rx_interrupt_requests || p_ring_stats->simple.n_rx_interrupt_received) {
					printf(FORMAT_RING_INTERRUPT, "Interrupts:", p_ring_stats->simple.n_rx_interrupt_requests, p_ring_stats->simple.n_rx_interrupt_received, post_fix);
				}
				if (p_ring_stats->simple.n_rx_cq_moderation_count || p_ring_stats->simple.n_rx_cq_moderation_period) {
					printf(FORMAT_RING_MODERATION, "Moderation:", p_ring_stats->simple.n_rx_cq_moderation_count, p_ring_stats->simple.n_rx_cq_moderation_period, post_fix);
				}
				if (p_ring_stats->simple.n_tx_dev_mem_allocated) {
					printf(FORMAT_STATS_32bit, "Dev Mem Alloc:", p_ring_stats->simple.n_tx_dev_mem_allocated);
					printf(FORMAT_RING_DM_STATS, "Dev Mem Stats:", p_ring_stats->simple.n_tx_dev_mem_byte_count/BYTES_TRAFFIC_UNIT,  p_ring_stats->simple.n_tx_dev_mem_pkt_count, p_ring_stats->simple.n_tx_dev_mem_oob, post_fix);
				}
			}
		}
	}
	printf("======================================================\n");
}

void print_cq_stats(cq_instance_block_t* p_cq_inst_arr)
{
	cq_stats_t* p_cq_stats = NULL;
	char post_fix[3] = "";
		
	if (user_params.print_details_mode == e_deltas)
		strcpy(post_fix, "/s");
	
	for (int i = 0; i < NUM_OF_SUPPORTED_CQS; i++) {
		if (p_cq_inst_arr[i].b_enabled) {
			p_cq_stats = &p_cq_inst_arr[i].cq_stats;
			printf("======================================================\n");
			printf("\tCQ=[%u]\n", i);
			printf(FORMAT_STATS_64bit, "Packets dropped:", (unsigned long long int)p_cq_stats->n_rx_pkt_drop, post_fix);
			printf(FORMAT_STATS_32bit, "Packets queue len:",p_cq_stats->n_rx_sw_queue_len);
			printf(FORMAT_STATS_32bit, "Drained max:", p_cq_stats->n_rx_drained_at_once_max);
			printf(FORMAT_STATS_32bit, "Buffer pool size:",p_cq_stats->n_buffer_pool_len);
		}
	}
	printf("======================================================\n");
}

void print_bpool_stats(bpool_instance_block_t* p_bpool_inst_arr)
{
	bpool_stats_t* p_bpool_stats = NULL;
	char post_fix[3] = "";

	if (user_params.print_details_mode == e_deltas)
		strcpy(post_fix, "/s");

	for (int i = 0; i < NUM_OF_SUPPORTED_BPOOLS; i++) {
		if (p_bpool_inst_arr && p_bpool_inst_arr[i].b_enabled) {
			p_bpool_stats = &p_bpool_inst_arr[i].bpool_stats;
			printf("======================================================\n");
			if (p_bpool_stats->is_rx)
				printf("\tBUFFER_POOL(RX)=[%u]\n", i);
			else if (p_bpool_stats->is_tx)
				printf("\tBUFFER_POOL(TX)=[%u]\n", i);
			else
				printf("\tBUFFER_POOL=[%u]\n", i);
			printf(FORMAT_STATS_32bit, "Size:", p_bpool_stats->n_buffer_pool_size);
			printf(FORMAT_STATS_32bit, "No buffers error:", p_bpool_stats->n_buffer_pool_no_bufs);
		}
	}
	printf("======================================================\n");
}

void print_basic_stats(socket_stats_t* p_stats)
{
	// 
	// Socket statistics
	//
	double rx_poll_hit_percentage = 0;
	
	if (p_stats->counters.n_rx_poll_hit) {
		double rx_poll_hit = (double)p_stats->counters.n_rx_poll_hit;
		rx_poll_hit_percentage = (rx_poll_hit / (rx_poll_hit + (double)p_stats->counters.n_rx_poll_miss)) * 100;
	}
	printf(RX_SHORT_VIEW,p_stats->fd,"Rx:",p_stats->counters.n_rx_packets,
			p_stats->counters.n_rx_bytes/BYTES_TRAFFIC_UNIT,p_stats->counters.n_rx_eagain,
			p_stats->counters.n_rx_errors,rx_poll_hit_percentage,
			p_stats->counters.n_rx_os_packets,p_stats->counters.n_rx_os_bytes / BYTES_TRAFFIC_UNIT,
			p_stats->counters.n_rx_os_eagain,p_stats->counters.n_rx_os_errors);
	
	printf(TX_SHORT_VIEW," ", "Tx:",p_stats->counters.n_tx_sent_pkt_count,
			p_stats->counters.n_tx_sent_byte_count/BYTES_TRAFFIC_UNIT,p_stats->counters.n_tx_drops,
			p_stats->counters.n_tx_errors," ",
			p_stats->counters.n_tx_os_packets,p_stats->counters.n_tx_os_bytes / BYTES_TRAFFIC_UNIT,
			p_stats->counters.n_tx_os_eagain,p_stats->counters.n_tx_os_errors);
	
}

void print_medium_total_stats(socket_stats_t* p_stats)
{
	// 
	// Socket statistics
	//
	double rx_poll_hit_percentage = 0;
	
	if (p_stats->counters.n_rx_poll_hit) {
		double rx_poll_hit = (double)p_stats->counters.n_rx_poll_hit;
		rx_poll_hit_percentage = (rx_poll_hit / (rx_poll_hit + (double)p_stats->counters.n_rx_poll_miss)) * 100;
	}
	printf(RX_MEDIUM_VIEW,p_stats->fd,"Rx:",p_stats->counters.n_rx_packets,
			p_stats->counters.n_rx_bytes/BYTES_TRAFFIC_UNIT,p_stats->counters.n_rx_eagain,
			p_stats->counters.n_rx_errors,rx_poll_hit_percentage,
			p_stats->n_rx_ready_pkt_count, p_stats->counters.n_rx_ready_pkt_max,
			p_stats->counters.n_rx_ready_pkt_drop,p_stats->counters.n_rx_os_packets,p_stats->counters.n_rx_os_bytes / BYTES_TRAFFIC_UNIT,
			p_stats->counters.n_rx_os_eagain,p_stats->counters.n_rx_os_errors);
	
	printf(TX_MEDIUM_VIEW," ", "Tx:",p_stats->counters.n_tx_sent_pkt_count,
			p_stats->counters.n_tx_sent_byte_count/BYTES_TRAFFIC_UNIT,p_stats->counters.n_tx_drops,
			p_stats->counters.n_tx_errors," ",
			p_stats->counters.n_tx_os_packets,p_stats->counters.n_tx_os_bytes / BYTES_TRAFFIC_UNIT,
			p_stats->counters.n_tx_os_eagain,p_stats->counters.n_tx_os_errors);
}

void print_basic_delta_stats(socket_stats_t* p_curr_stat, socket_stats_t* p_prev_stat)
{
	update_delta_stat(p_curr_stat, p_prev_stat);
	print_basic_stats(p_prev_stat);
}

void print_medium_delta_stats(socket_stats_t* p_curr_stat, socket_stats_t* p_prev_stat)
{
	update_delta_stat(p_curr_stat, p_prev_stat);
	print_medium_total_stats(p_prev_stat);
}

void print_full_delta_stats(socket_stats_t* p_curr_stat, socket_stats_t* p_prev_stat, mc_grp_info_t* p_mc_grp_info)
{
	update_delta_stat(p_curr_stat, p_prev_stat);
	print_full_stats(p_prev_stat, p_mc_grp_info, g_stats_file);
}

void print_basic_mode_headers()
{
	switch (user_params.print_details_mode) {
		case e_totals:
			printf(UPPER_SHORT_VIEW_HEADER,"fd","------------ total offloaded -------------","--------- total os ----------");
			printf(LOWER_SHORT_VIEW_HEADER," ","pkt","Kbyte","eagain","error","poll%","pkt","Kbyte","eagain","error");
			break;
		case e_deltas:
			printf(UPPER_SHORT_VIEW_HEADER,"fd","--------------- offloaded ----------------","---------- os ---------");
			printf(LOWER_SHORT_VIEW_HEADER," ","pkt/s","Kbyte/s","eagain/s","error/s","poll%","pkt/s","Kbyte/s","eagain/s","error/s");
			break;
		default:
			break;
	}
}

void print_medium_mode_headers()
{
	switch (user_params.print_details_mode) {
		case e_totals:
			printf(UPPER_MEDIUM_VIEW_HEADER,"fd", "----------------------- total offloaded -------------------------", "--------- total os ----------");
			printf(MIDDLE_MEDIUM_VIEW_HEADER," ","pkt","Kbyte","eagain","error","poll%","---- queue pkt -----", "pkt", "Kbyte","eagain", "error");
			printf(LOWER_MEDIUM_VIEW_HEADER," ", "cur","max","drop");
			break;
		case e_deltas:
			printf(UPPER_MEDIUM_VIEW_HEADER,"fd", "---------------------------- offloaded --------------------------", "---------- os ---------");
			printf(MIDDLE_MEDIUM_VIEW_HEADER," ","pkt/s","Kbyte/s","eagain/s","error/s","poll%","----- queue pkt ------", "pkt/s", "Kbyte/s", "eagain/s", "error/s");
			printf(LOWER_MEDIUM_VIEW_HEADER," ", "cur","max","drop/s");
			break;
		default:
			break;
	}	
}

void print_headers()
{
	switch (user_params.view_mode) {
		case e_basic:
			print_basic_mode_headers();
			break;
		case e_medium:
			print_medium_mode_headers();
			break;
		case e_netstat_like:
			print_netstat_like_headers(g_stats_file);
			break;
		default:
			break;	
	}
}

void show_basic_stats(socket_instance_block_t* p_instance,socket_instance_block_t* p_prev_instance_block) 
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_basic_stats(&p_instance->skt_stats);
			break;
		case e_deltas:
			print_basic_delta_stats(&p_instance->skt_stats, &p_prev_instance_block->skt_stats);
			break;
		default:
			break;
	}
}

void print_medium_stats(socket_instance_block_t* p_instance, socket_instance_block_t* p_prev_instance_block) 
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_medium_total_stats(&p_instance->skt_stats);
			break;
		case e_deltas:
			print_medium_delta_stats(&p_instance->skt_stats, &p_prev_instance_block->skt_stats);
			break;
		default:
			break;
	}
}

void show_full_stats(socket_instance_block_t* p_instance, socket_instance_block_t* p_prev_instance_block, mc_grp_info_t* p_mc_grp_info) 
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_full_stats(&p_instance->skt_stats, p_mc_grp_info, g_stats_file);
			break;
		case e_deltas:
			print_full_delta_stats(&p_instance->skt_stats, &p_prev_instance_block->skt_stats, p_mc_grp_info);
			break;
		default:
			break;
	}
}

int show_socket_stats(socket_instance_block_t* p_instance, socket_instance_block_t* p_prev_instance_block,uint32_t num_of_obj, int* p_printed_lines_num, mc_grp_info_t* p_mc_grp_info, int pid)
{
	int num_act_inst = 0;
	
	if (*p_printed_lines_num >= SCREEN_SIZE && user_params.view_mode != e_full) {
		print_headers();
		switch (user_params.view_mode) {
		case e_basic: 
			*p_printed_lines_num = BASE_HEADERS_NUM;
			break;
		case e_medium:
			*p_printed_lines_num = MEDIUM_HEADERS_NUM;
			break;
		default:
			break;
		}			
	}

	for (uint32_t i=0; i < num_of_obj; i++) {
		size_t fd = (size_t)p_instance[i].skt_stats.fd;
		if (p_instance[i].b_enabled && g_fd_mask[fd]) {
			num_act_inst++;			
			switch (user_params.view_mode) {
				case e_basic: 
					show_basic_stats(&p_instance[i], &p_prev_instance_block[i]);
					*p_printed_lines_num += BASIC_STATS_LINES_NUM;
					break;
				case e_medium:
					print_medium_stats(&p_instance[i], &p_prev_instance_block[i]);
					*p_printed_lines_num += MEDIUM_STATS_LINES_NUM;
					break;
				case e_full:
					show_full_stats(&p_instance[i], &p_prev_instance_block[i], p_mc_grp_info);	
					break;
				case e_netstat_like:
					print_netstat_like(&p_instance[i].skt_stats, p_mc_grp_info, g_stats_file, pid);
					break;
				default:
					break;
			}			
		}		
	}
	return num_act_inst;
}

// Print statistics for select(), poll(), epoll()
void print_full_iomux_stats(const char* func_name, iomux_func_stats_t* p_iomux_stats)
{
       char post_fix[3] = "";

       if (user_params.print_details_mode == e_deltas)
               strcpy(post_fix, "/s");

       if (p_iomux_stats && (p_iomux_stats->n_iomux_os_rx_ready || p_iomux_stats->n_iomux_rx_ready ||
           p_iomux_stats->n_iomux_timeouts || p_iomux_stats->n_iomux_errors ||
           p_iomux_stats->n_iomux_poll_miss || p_iomux_stats->n_iomux_poll_hit)) {

               printf("======================================================\n");
               printf("\t%s\n", func_name);
               printf("Polling CPU%s:%d%%\n", post_fix, p_iomux_stats->n_iomux_polling_time);
               if (p_iomux_stats->threadid_last != 0)
                       printf("- Thread Id: %5u\n", p_iomux_stats->threadid_last);
               if (p_iomux_stats->n_iomux_os_rx_ready || p_iomux_stats->n_iomux_rx_ready)
                       printf("Rx fds ready: %u / %u [os/offload]%s\n", p_iomux_stats->n_iomux_os_rx_ready, p_iomux_stats->n_iomux_rx_ready, post_fix);
               if (p_iomux_stats->n_iomux_poll_miss + p_iomux_stats->n_iomux_poll_hit) {
                       double iomux_poll_hit = (double)p_iomux_stats->n_iomux_poll_hit;
                       double iomux_poll_hit_percentage = (iomux_poll_hit / (iomux_poll_hit + (double)p_iomux_stats->n_iomux_poll_miss)) * 100;
                       printf("Polls [miss/hit]%s: %u / %u (%2.2f%%)\n", post_fix,p_iomux_stats->n_iomux_poll_miss, p_iomux_stats->n_iomux_poll_hit, iomux_poll_hit_percentage);
                       if (p_iomux_stats->n_iomux_timeouts)
                               printf("Timeouts%s: %u\n",post_fix, p_iomux_stats->n_iomux_timeouts);
                       if (p_iomux_stats->n_iomux_errors)
                               printf("Errors%s: %u\n", post_fix, p_iomux_stats->n_iomux_errors);
                       printf("======================================================\n");
               }
       }
}

void print_basic_iomux_stats(const char* func_name, iomux_func_stats_t* p_iomux_stats, int* p_printed_lines_num)
{
       double rx_poll_hit_percentage = 0;
       char post_fix[3] = "";

       if (user_params.print_details_mode == e_deltas)
               strcpy(post_fix, "/s");

       if (p_iomux_stats->n_iomux_poll_hit) {
               double iomux_poll_hit = (double)p_iomux_stats->n_iomux_poll_hit;
               rx_poll_hit_percentage = (iomux_poll_hit / (iomux_poll_hit + (double)p_iomux_stats->n_iomux_poll_miss)) * 100;
       }

       if (p_iomux_stats->n_iomux_os_rx_ready || p_iomux_stats->n_iomux_rx_ready ||
           p_iomux_stats->n_iomux_timeouts || p_iomux_stats->n_iomux_errors ||
           p_iomux_stats->n_iomux_poll_miss || p_iomux_stats->n_iomux_poll_hit) {
               printf(IOMUX_FORMAT,func_name, post_fix,"Rx Ready:", p_iomux_stats->n_iomux_os_rx_ready,
                      "/", p_iomux_stats->n_iomux_rx_ready,
                      "[os/offload]", "Timeouts:", p_iomux_stats->n_iomux_timeouts,
                      "Errors:", p_iomux_stats->n_iomux_errors,
                      "Poll:", rx_poll_hit_percentage, "%",
                      "Polling CPU:", p_iomux_stats->n_iomux_polling_time, "%");
               (*p_printed_lines_num)++;
       }
}

void print_iomux_totals(iomux_stats_t* p_iomux_stats, int* p_printed_lines_num)
{
	if (p_printed_lines_num) {
		print_basic_iomux_stats("poll", &p_iomux_stats->poll, p_printed_lines_num);
		print_basic_iomux_stats("select", &p_iomux_stats->select, p_printed_lines_num);
	} else {
		print_full_iomux_stats("poll", &p_iomux_stats->poll);
		print_full_iomux_stats("select", &p_iomux_stats->select);
	}
	for (int i = 0; i < NUM_OF_SUPPORTED_EPFDS; i++) {
		epoll_stats_t *ep_stats = &p_iomux_stats->epoll[i];
		if (ep_stats->enabled) {
			char epfd_name[20];
			snprintf(epfd_name, sizeof(epfd_name), "epoll[%d]", ep_stats->epfd);
			if (p_printed_lines_num) {
				print_basic_iomux_stats(epfd_name, &ep_stats->stats, p_printed_lines_num);
			} else {
				print_full_iomux_stats(epfd_name, &ep_stats->stats);
			}
		}
	}
}

void update_iomux_deltas(iomux_stats_t* p_curr_iomux_stats, iomux_stats_t* p_prev_iomux_stats)
{
	update_delta_iomux_stat(&p_curr_iomux_stats->poll, &p_prev_iomux_stats->poll);
	update_delta_iomux_stat(&p_curr_iomux_stats->select, &p_prev_iomux_stats->select);
	for (int i = 0; i < NUM_OF_SUPPORTED_EPFDS; i++) {
		if (p_curr_iomux_stats->epoll[i].enabled && p_prev_iomux_stats->epoll[i].enabled) {
			update_delta_iomux_stat(&p_curr_iomux_stats->epoll[i].stats,
			                        &p_prev_iomux_stats->epoll[i].stats);
		}
	}
}

void print_full_iomux_deltas(iomux_stats_t* p_curr_iomux_stats, iomux_stats_t* p_prev_iomux_stats)
{
	update_iomux_deltas(p_curr_iomux_stats, p_prev_iomux_stats);
	print_iomux_totals(p_prev_iomux_stats, NULL);
}

void print_basic_iomux_deltas(iomux_stats_t* p_curr_stats, iomux_stats_t* p_prev_stats, int* p_printed_lines_num)
{
	update_iomux_deltas(p_curr_stats, p_prev_stats);
	print_iomux_totals(p_prev_stats, p_printed_lines_num);
}

void print_full_iomux_stats(iomux_stats_t* p_curr_stats, iomux_stats_t* p_prev_stats)
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_iomux_totals(p_curr_stats, NULL);
			break;
		default:
			print_full_iomux_deltas(p_curr_stats, p_prev_stats);
			break;
	}
}

void print_ring_deltas(ring_instance_block_t* p_curr_ring_stats, ring_instance_block_t* p_prev_ring_stats)
{
	for (int i = 0; i < NUM_OF_SUPPORTED_RINGS; i++) {
		update_delta_ring_stat(&p_curr_ring_stats[i].ring_stats,&p_prev_ring_stats[i].ring_stats);
	}
	print_ring_stats(p_prev_ring_stats);
}

void print_cq_deltas(cq_instance_block_t* p_curr_cq_stats, cq_instance_block_t* p_prev_cq_stats)
{
	for (int i = 0; i < NUM_OF_SUPPORTED_CQS; i++) {
		update_delta_cq_stat(&p_curr_cq_stats[i].cq_stats,&p_prev_cq_stats[i].cq_stats);
	}
	print_cq_stats(p_prev_cq_stats);
}

void print_bpool_deltas(bpool_instance_block_t* p_curr_bpool_stats, bpool_instance_block_t* p_prev_bpool_stats)
{
	for (int i = 0; i < NUM_OF_SUPPORTED_BPOOLS; i++) {
		update_delta_bpool_stat(&p_curr_bpool_stats[i].bpool_stats,&p_prev_bpool_stats[i].bpool_stats);
	}
	print_bpool_stats(p_prev_bpool_stats);
}

void show_ring_stats(ring_instance_block_t* p_curr_ring_blocks, ring_instance_block_t* p_prev_ring_blocks)
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_ring_stats(p_curr_ring_blocks);
			break;
		default:
			print_ring_deltas(p_curr_ring_blocks, p_prev_ring_blocks);
			break;
	}
}

void show_cq_stats(cq_instance_block_t* p_curr_cq_blocks, cq_instance_block_t* p_prev_cq_blocks)
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_cq_stats(p_curr_cq_blocks);
			break;
		default:
			print_cq_deltas(p_curr_cq_blocks, p_prev_cq_blocks);
			break;
	}
}

void show_bpool_stats(bpool_instance_block_t* p_curr_bpool_blocks, bpool_instance_block_t* p_prev_bpool_blocks)
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_bpool_stats(p_curr_bpool_blocks);
			break;
		default:
			print_bpool_deltas(p_curr_bpool_blocks, p_prev_bpool_blocks);
			break;
	}
}

void show_basic_iomux_stats(iomux_stats_t* p_curr_stats, iomux_stats_t* p_prev_stats, int* p_printed_lines_num)
{
	switch (user_params.print_details_mode) {
		case e_totals:
			print_iomux_totals(p_curr_stats, p_printed_lines_num);
			break;
		default:
			print_basic_iomux_deltas(p_curr_stats, p_prev_stats, p_printed_lines_num);
			break;
	}
}

void show_iomux_stats(iomux_stats_t* p_curr_stats, iomux_stats_t* p_prev_stats, int* p_printed_lines_num)
{
	switch (user_params.view_mode) {
		case e_basic:
		case e_medium:	
			show_basic_iomux_stats(p_curr_stats, p_prev_stats, p_printed_lines_num);
			break;
		case e_full:
			print_full_iomux_stats(p_curr_stats, p_prev_stats);
			break;
		default:
			break;
	}
}

// Find mc_grp in mc_group_fds array. 
// if exist: add the fd to the list. 
// if not: add the mc group to the array and the fd to the list
void add_fd_to_array(int fd, in_addr_t mc_grp, mc_group_fds_t * mc_group_fds, int * array_size)
{
	// Go over the mc_group_fds array
	int i=0;
	for (i=0; i < *array_size; i++) {
		if (mc_grp == mc_group_fds[i].mc_grp) {
			//add fd to the list
			mc_group_fds[i].fd_list.push_back(fd);
			return;
		}
	}
	// the mc_group wasnt found
	// Add this mc group to the array
	mc_group_fds[i].mc_grp=mc_grp;
	int fd1=fd;
	mc_group_fds[i].fd_list.push_back(fd1);
	(*array_size)++;
}

void print_mc_group_fds(mc_group_fds_t * mc_group_fds, int array_size)
{
	printf("\n");
	printf("VMA Group Memberships Information\n");
	printf("Group                fd number\n");
	printf("------------------------------\n");
	for (int i=0; i< array_size; i++) {
		char mcg_str[256];
		/* cppcheck-suppress wrongPrintfScanfArgNum */
		sprintf(mcg_str, "[%d.%d.%d.%d]", NIPQUAD(mc_group_fds[i].mc_grp));
		printf("%-22s", mcg_str);
		for (fd_list_t::iterator iter = mc_group_fds[i].fd_list.begin(); iter != mc_group_fds[i].fd_list.end(); iter++) {
			printf("%d ", *iter);
		}
		printf("\n");
	}
}

void show_mc_group_stats(mc_grp_info_t* p_mc_grp_info , socket_instance_block_t* p_instance, uint32_t num_of_obj)
{
	// keep array for all the mc addresses and their fds.
	int array_size=0;
	mc_group_fds_t *mc_group_fds = new mc_group_fds_t[num_of_obj*MC_TABLE_SIZE];
	if (!mc_group_fds) {
		printf(CYCLES_SEPARATOR);
		printf("Could not allocate enough memory\n");
		printf(CYCLES_SEPARATOR);
		return;
	}
	// go over all the fds and fill the array
	for (uint32_t i=0; i < num_of_obj; i++) {
		size_t fd = (size_t)p_instance[i].skt_stats.fd;
		if (p_instance[i].b_enabled && g_fd_mask[fd]) {
			socket_stats_t* p_si_stats = &p_instance[i].skt_stats; 
			for (int grp_idx = 0; grp_idx < p_mc_grp_info->max_grp_num; grp_idx++) {
				if (p_si_stats->mc_grp_map.test(grp_idx)) {
					//printf("fd %d Member of = [%d.%d.%d.%d]\n",p_si_stats->fd, NIPQUAD(p_si_stats->mc_grp[grp_idx]));
					add_fd_to_array(p_si_stats->fd, p_mc_grp_info->mc_grp_tbl[grp_idx].mc_grp, mc_group_fds, &array_size);
				}
			}
		}
	}
	if (array_size > 0)
		print_mc_group_fds(mc_group_fds, array_size);
	printf(CYCLES_SEPARATOR);

	delete [] mc_group_fds;
}

void print_command_line(int argc, char** argv)
{
	if (argv != NULL)
	{	printf("Cmd Line: ");
		for (int i = 0; i < argc; i++)
			printf("%s ",argv[i]);
	}
	printf("\n");
}

int print_app_name(int pid)
{
	char app_base_name[FILE_NAME_MAX_SIZE];

	if (get_procname(pid, app_base_name, sizeof(app_base_name)) < 0) {
		return -1;
	}
	printf("application: %s ", app_base_name);

	return 0;
}

void print_version(int pid)
{
	if (pid == -1) {
		log_msg("Linked with VMA version: %d.%d.%d.%d", VMA_LIBRARY_MAJOR, VMA_LIBRARY_MINOR, VMA_LIBRARY_REVISION, VMA_LIBRARY_RELEASE);
		#ifdef VMA_SVN_REVISION
		log_msg("Revision: %d", VMA_SVN_REVISION);
		#endif
		#ifdef VMA_DATE_TIME
		log_msg("Build Date: %s", VMA_DATE_TIME);
		#endif
	}
	else {
		printf(MODULE_NAME ": stats for ");
		if (print_app_name(pid) < 0)
			printf("proccess ");
		printf("with pid: %d\n", pid);
	}
}

int check_vma_ver_compatability(version_info_t* p_stat_ver_info)
{
	return (p_stat_ver_info->vma_lib_maj == VMA_LIBRARY_MAJOR &&
		p_stat_ver_info->vma_lib_min == VMA_LIBRARY_MINOR &&
		p_stat_ver_info->vma_lib_rel == VMA_LIBRARY_RELEASE &&
		p_stat_ver_info->vma_lib_rev == VMA_LIBRARY_REVISION);	
}

void cleanup(sh_mem_info* p_sh_mem_info)
{
	if (p_sh_mem_info == NULL)
		return;
	if (p_sh_mem_info->p_sh_stats != MAP_FAILED)
	{
		if (munmap(p_sh_mem_info->p_sh_stats, p_sh_mem_info->shmem_size) != 0) {
			log_system_err("file='%s' sh_mem_info.fd_sh_stats=%d; error while munmap shared memory at [%p]\n", p_sh_mem_info->filename_sh_stats, p_sh_mem_info->fd_sh_stats, p_sh_mem_info->p_sh_stats);
		}
	}
	close(p_sh_mem_info->fd_sh_stats);
}

void stats_reader_sig_handler(int signum)
{
	switch (signum) {
	case SIGINT:
		log_msg("Got Ctrl-C (interrupted by user)");
		break;
	default:
		log_msg("Got signal %d - exiting", signum);
		break;
	}
	g_b_exit = true;
}

void set_signal_action()
{
	g_sigact.sa_handler = stats_reader_sig_handler;
	sigemptyset(&g_sigact.sa_mask);
	g_sigact.sa_flags = 0;	

	sigaction(SIGINT, &g_sigact, NULL);
}

void alloc_fd_mask()
{
	struct rlimit rlim;
	if ((getrlimit(RLIMIT_NOFILE, &rlim) == 0) && ((uint32_t)rlim.rlim_max > g_fd_map_size))
		g_fd_map_size = rlim.rlim_max;
	g_fd_mask = (uint8_t*)malloc(g_fd_map_size * sizeof(uint8_t));
	if (!g_fd_mask)
		log_err("Failed to malloc g_fd_mask var\n");
}

void inc_read_counter(sh_mem_t* p_sh_mem)
{
	p_sh_mem->reader_counter++;
}

void set_defaults()
{
	user_params.interval = DEFAULT_DELAY_SEC;
	user_params.view_mode = DEFAULT_VIEW_MODE;
	user_params.print_details_mode = DEFAULT_DETAILS_MODE;
	user_params.proc_ident_mode = DEFAULT_PROC_IDENT_MODE;
	user_params.vma_log_level = VLOG_INIT;
	user_params.vma_details_level = INIT_VMA_LOG_DETAILS;
	user_params.forbid_cleaning = false;	
	user_params.zero_counters = false;
	user_params.write_auth = true; //needed to set read flag on
	user_params.cycles = DEFAULT_CYCLES;
	user_params.fd_dump = STATS_FD_STATISTICS_DISABLED;
	user_params.fd_dump_log_level = STATS_FD_STATISTICS_LOG_LEVEL_DEFAULT;
	user_params.vma_stats_path = MCE_DEFAULT_STATS_SHMEM_DIR;
	
	alloc_fd_mask();
	if (g_fd_mask)
		memset((void*)g_fd_mask, 1, sizeof(uint8_t) * g_fd_map_size);
}

bool check_if_process_running(char* pid_str)
{
	char proccess_proc_dir[FILE_NAME_MAX_SIZE] = {0};
	struct stat st;
	int n = -1;
	
	n = snprintf(proccess_proc_dir, sizeof(proccess_proc_dir), "/proc/%s", pid_str);
	if (likely((0 < n) && (n < (int)sizeof(proccess_proc_dir)))) {
		return stat(proccess_proc_dir, &st) == 0;
	}
	return false;
}

bool check_if_process_running(int pid)
{
	char pid_str[MAX_BUFF_SIZE] = {0};
	int n = -1;

	n = snprintf(pid_str, sizeof(pid_str), "%d", pid);
	if (likely((0 < n) && (n < (int)sizeof(pid_str)))) {
		return check_if_process_running(pid_str);
	}
	return false;
}

void stats_reader_handler(sh_mem_t* p_sh_mem, int pid)
{
	int ret;
	int num_act_inst = 0;
	int cycles = user_params.cycles ? user_params.cycles : -1;
	int printed_line_num = SCREEN_SIZE;
	struct timespec start, end;	
	bool proc_running = true;
	socket_instance_block_t *prev_instance_blocks;
	socket_instance_block_t *curr_instance_blocks;
	cq_instance_block_t prev_cq_blocks[NUM_OF_SUPPORTED_CQS];
	cq_instance_block_t curr_cq_blocks[NUM_OF_SUPPORTED_CQS];
	ring_instance_block_t prev_ring_blocks[NUM_OF_SUPPORTED_RINGS];
	ring_instance_block_t curr_ring_blocks[NUM_OF_SUPPORTED_RINGS];
	bpool_instance_block_t prev_bpool_blocks[NUM_OF_SUPPORTED_BPOOLS];
	bpool_instance_block_t curr_bpool_blocks[NUM_OF_SUPPORTED_BPOOLS];
	iomux_stats_t prev_iomux_blocks;
	iomux_stats_t curr_iomux_blocks;

	if (user_params.fd_dump != STATS_FD_STATISTICS_DISABLED) {
		if (user_params.fd_dump)
			log_msg("Dumping Fd %d to VMA using log level = %s...", user_params.fd_dump, log_level::to_str(user_params.fd_dump_log_level));
		else
			log_msg("Dumping all Fd's to VMA using log level = %s...", log_level::to_str(user_params.fd_dump_log_level));
		return;
	}

	prev_instance_blocks = (socket_instance_block_t*)malloc(sizeof(*prev_instance_blocks) * p_sh_mem->max_skt_inst_num);
	if (NULL == prev_instance_blocks) {
		return ;
	}
        curr_instance_blocks = (socket_instance_block_t*)malloc(sizeof(*curr_instance_blocks) * p_sh_mem->max_skt_inst_num);
        if (NULL == curr_instance_blocks) {
		free(prev_instance_blocks);
                return ;
        }

	memset((void*)prev_instance_blocks,0, sizeof(socket_instance_block_t) * p_sh_mem->max_skt_inst_num);
	memset((void*)curr_instance_blocks,0, sizeof(socket_instance_block_t) * p_sh_mem->max_skt_inst_num);
	memset((void*)prev_cq_blocks,0, sizeof(cq_instance_block_t) * NUM_OF_SUPPORTED_CQS);
	memset((void*)curr_cq_blocks,0, sizeof(cq_instance_block_t) * NUM_OF_SUPPORTED_CQS);
	memset((void*)prev_ring_blocks,0, sizeof(ring_instance_block_t) * NUM_OF_SUPPORTED_RINGS);
	memset((void*)curr_ring_blocks,0, sizeof(ring_instance_block_t) * NUM_OF_SUPPORTED_RINGS);
	memset((void*)prev_bpool_blocks,0, sizeof(bpool_instance_block_t) * NUM_OF_SUPPORTED_BPOOLS);
	memset((void*)curr_bpool_blocks,0, sizeof(bpool_instance_block_t) * NUM_OF_SUPPORTED_BPOOLS);
	memset(&prev_iomux_blocks,0, sizeof(prev_iomux_blocks));
	memset(&curr_iomux_blocks,0, sizeof(curr_iomux_blocks));
	
	if (user_params.print_details_mode == e_deltas) {
		memcpy((void*)prev_instance_blocks,(void*)p_sh_mem->skt_inst_arr, p_sh_mem->max_skt_inst_num * sizeof(socket_instance_block_t));
		memcpy((void*)prev_cq_blocks,(void*)p_sh_mem->cq_inst_arr, NUM_OF_SUPPORTED_CQS * sizeof(cq_instance_block_t));
		memcpy((void*)prev_ring_blocks,(void*)p_sh_mem->ring_inst_arr, NUM_OF_SUPPORTED_RINGS * sizeof(ring_instance_block_t));
		memcpy((void*)prev_bpool_blocks,(void*)p_sh_mem->bpool_inst_arr, NUM_OF_SUPPORTED_BPOOLS * sizeof(bpool_instance_block_t));
		prev_iomux_blocks = curr_iomux_blocks;
		uint64_t delay_int_micro = SEC_TO_MICRO(user_params.interval);
		if (!g_b_exit && check_if_process_running(pid)){
			usleep(delay_int_micro);
		}
	}
	
	set_signal_action();
	
	while (!g_b_exit && proc_running && cycles)
	{
		--cycles;

		if (gettime(&start)) {
			log_system_err("gettime()");
			goto out;
		}
		
		if (user_params.print_details_mode == e_deltas) {
			memcpy((void*)curr_instance_blocks,(void*)p_sh_mem->skt_inst_arr, p_sh_mem->max_skt_inst_num * sizeof(socket_instance_block_t));
			memcpy((void*)curr_cq_blocks,(void*)p_sh_mem->cq_inst_arr, NUM_OF_SUPPORTED_CQS * sizeof(cq_instance_block_t));
			memcpy((void*)curr_ring_blocks,(void*)p_sh_mem->ring_inst_arr, NUM_OF_SUPPORTED_RINGS * sizeof(ring_instance_block_t));
			memcpy((void*)curr_bpool_blocks,(void*)p_sh_mem->bpool_inst_arr, NUM_OF_SUPPORTED_BPOOLS * sizeof(bpool_instance_block_t));
			curr_iomux_blocks = p_sh_mem->iomux;
		}
		switch (user_params.view_mode) {
			case e_full:
				ret = system("clear");
				NOT_IN_USE(ret);
				break;
			case e_mc_groups:
				show_mc_group_stats(&p_sh_mem->mc_info, p_sh_mem->skt_inst_arr, p_sh_mem->max_skt_inst_num);
				goto out;
				break;
			default:
				break;
		}
		switch (user_params.print_details_mode) {
			case e_totals:
				num_act_inst = show_socket_stats(p_sh_mem->skt_inst_arr, NULL, p_sh_mem->max_skt_inst_num, &printed_line_num, &p_sh_mem->mc_info, pid);
				show_iomux_stats(&p_sh_mem->iomux, NULL, &printed_line_num);
				if (user_params.view_mode == e_full) {
					show_cq_stats(p_sh_mem->cq_inst_arr,NULL);
					show_ring_stats(p_sh_mem->ring_inst_arr,NULL);
					show_bpool_stats(p_sh_mem->bpool_inst_arr,NULL);
				}
				break;
			case e_deltas:
				num_act_inst = show_socket_stats(curr_instance_blocks, prev_instance_blocks, p_sh_mem->max_skt_inst_num, &printed_line_num, &p_sh_mem->mc_info, pid);
				show_iomux_stats(&curr_iomux_blocks, &prev_iomux_blocks, &printed_line_num);
				if (user_params.view_mode == e_full) {
					show_cq_stats(curr_cq_blocks, prev_cq_blocks);
					show_ring_stats(curr_ring_blocks, prev_ring_blocks);
					show_bpool_stats(curr_bpool_blocks, prev_bpool_blocks);
				}
				memcpy((void*)prev_instance_blocks,(void*)curr_instance_blocks, p_sh_mem->max_skt_inst_num * sizeof(socket_instance_block_t));
				memcpy((void*)prev_cq_blocks,(void*)curr_cq_blocks, NUM_OF_SUPPORTED_CQS * sizeof(cq_instance_block_t));
				memcpy((void*)prev_ring_blocks,(void*)curr_ring_blocks, NUM_OF_SUPPORTED_RINGS * sizeof(ring_instance_block_t));
				prev_iomux_blocks = curr_iomux_blocks;
				break;
			default:
				break;
		}
		if (user_params.view_mode == e_netstat_like)
			break;
		if (num_act_inst) {
			printf(CYCLES_SEPARATOR);
			printed_line_num++;
		}
		if (gettime(&end)) {
			log_system_err("gettime()");
			goto out;
		}
		uint64_t delay_int_micro = SEC_TO_MICRO(user_params.interval);
		uint64_t adjasted_delay = delay_int_micro - TIME_DIFF_in_MICRO(start, end);
		if (!g_b_exit && proc_running){
			if (cycles) {
				usleep(adjasted_delay);
			}
            inc_read_counter(p_sh_mem);
		}
		proc_running = check_if_process_running(pid);
	}
	if (!proc_running)
		log_msg("Proccess %d ended - exiting", pid);

out:
	free(prev_instance_blocks);
	free(curr_instance_blocks);
}

bool check_if_app_match(char* app_name, char* pid_str)
{
	char app_full_name[PATH_MAX] = {0};
	char proccess_proc_dir[FILE_NAME_MAX_SIZE] = {0};
	char* app_base_name = NULL;
	int n = -1;
	
	n = snprintf(proccess_proc_dir, sizeof(proccess_proc_dir), "/proc/%s/exe", pid_str);
	if (likely((0 < n) && (n < (int)sizeof(proccess_proc_dir)))) {
		n = readlink(proccess_proc_dir, app_full_name, sizeof(app_full_name) - 1);
		if (n > 0) {
			app_full_name[n] = '\0';
			app_base_name = strrchr(app_full_name, '/');
			if (app_base_name) {
				return strcmp((app_base_name + 1), app_name) == 0;
			}
		}
	}
	
	return false;
}

void clean_inactive_sh_ibj()
{
	DIR *dir;
	struct dirent *dirent;
	int module_name_size = strlen(MODULE_NAME);
	int pid_offset = module_name_size + 1;
	
	dir = opendir(user_params.vma_stats_path.c_str());
	if (dir == NULL){ 
		log_system_err("opendir %s failed\n", user_params.vma_stats_path.c_str());
		return;
	}
	dirent = readdir(dir);
	while (dirent != NULL && !user_params.forbid_cleaning) {
		if(!strncmp("vmastat.", dirent->d_name, module_name_size)) {
			bool proccess_running = false;
			proccess_running = check_if_process_running(dirent->d_name + pid_offset);
			if (!proccess_running) {
				char to_delete[PATH_MAX + 1] = {0};
				int n = -1;

				n = snprintf(to_delete, sizeof(to_delete), "%s/%s", user_params.vma_stats_path.c_str(), dirent->d_name);
				if (likely((0 < n) && (n < (int)sizeof(to_delete)))) {
					unlink(to_delete);
				}
			}		
		}
		dirent = readdir(dir);
	}
	closedir(dir);
}

char* look_for_vma_stat_active_sh_obj(char* app_name)
{
	DIR *dir;
	struct dirent *dirent;
	bool found = false;
	char* sh_file_name = NULL;
	int module_name_size = strlen(MODULE_NAME);
	int pid_offset = module_name_size + 1;
	
	dir = opendir(user_params.vma_stats_path.c_str());
	if (dir == NULL){ 
		log_system_err("opendir %s failed\n", user_params.vma_stats_path.c_str());
		return NULL;
	}
	dirent = readdir(dir);
	
	while (dirent != NULL && !found) {
		if(!strncmp("vmastat.", dirent->d_name, module_name_size)) {
			found = check_if_process_running(dirent->d_name + pid_offset);
			if (app_name && found)
				found = check_if_app_match(app_name, dirent->d_name + pid_offset);
			if (found) {
				sh_file_name = (char*)calloc(FILE_NAME_MAX_SIZE,sizeof(char));
				if (!sh_file_name) {
					log_err("Failed to malloc sh_file_name var\n");
					closedir(dir);
					return NULL;
				}
				strcpy(sh_file_name,dirent->d_name + pid_offset);
			}			
		}
		dirent = readdir(dir);
	}	
	closedir(dir);
	return sh_file_name; 
}

int update_range_of_fds(char* left_str, char* right_str)
{
	int left = 0;
	int right = 0;
	
	errno = 0;
	left = strtol(left_str, NULL, 0);
	if (errno != 0  || left < 0 || (uint32_t)left > g_fd_map_size) {
		log_err("Invalid fd val: %s", left_str);
		return 1;
	}
	
	if (right_str) {
		right = strtol(right_str, NULL, 0);
		if (errno != 0  || right < 0 || (uint32_t)right > g_fd_map_size) {
			log_err("Invalid fd val: %s", right_str);
			return 1;
		}
	}
	else {
		right = left;
	}
	
	if ( right < left) {
		swap(right, left);
	}
	
	for (int i = left; i <= right; i++)
		g_fd_mask[i] = 1;
		
	return 0;
}

int analize_fds_range(char* range)
{
	char* left = range;
	char* right = NULL;
	char* delim_loc = NULL;
	char range_copy[101];
	
	if (strlen(range) + 1 > sizeof(range_copy)) {
		log_err("Invalid fd val size : %zu, cannot exceed %zu", strlen(range), sizeof(range_copy) - 1);
		return 1;
	}

	strncpy(range_copy, range, sizeof(range_copy) - 1);
	range_copy[sizeof(range_copy) - 1] = '\0';
	delim_loc = strchr(range_copy, '-');
	
	if (delim_loc != NULL) {
		right = delim_loc + 1;
		*delim_loc = '\0';
		left = range;
	}
	return update_range_of_fds(left, right);
}

int update_fds_mask(char* fds_list)
{
	memset((void*)g_fd_mask, 0 , sizeof(uint8_t) * g_fd_map_size);
	char delims[] = ",";
	char *curr_fds_range = NULL;
	curr_fds_range = strtok(fds_list, delims);
	while( curr_fds_range != NULL ) {
		if (analize_fds_range(curr_fds_range))
			return 1;
		curr_fds_range = strtok(NULL, delims);
	}	
	return 0;
}

void zero_socket_stats(socket_stats_t* p_socket_stats)
{
	memset((void*)&p_socket_stats->counters, 0, sizeof(socket_counters_t));
}

void zero_iomux_stats(iomux_stats_t* p_iomux_stats)
{
	memset(&p_iomux_stats->select, 0, sizeof(iomux_func_stats_t));
	memset(&p_iomux_stats->poll, 0, sizeof(iomux_func_stats_t));
	for (int i=0; i<NUM_OF_SUPPORTED_EPFDS; i++)
	{
		if(p_iomux_stats->epoll[i].enabled)
			memset((&p_iomux_stats->epoll[i].stats), 0, sizeof(iomux_func_stats_t));
	}

	//memset(p_iomux_stats, 0, sizeof(*p_iomux_stats));
}

void zero_ring_stats(ring_stats_t* p_ring_stats)
{
	p_ring_stats->n_rx_pkt_count = 0;
	p_ring_stats->n_rx_byte_count = 0;
	p_ring_stats->n_tx_pkt_count = 0;
	p_ring_stats->n_tx_byte_count = 0;
	p_ring_stats->n_tx_retransmits = 0;
	if (p_ring_stats->n_type == RING_TAP) {
		p_ring_stats->tap.n_vf_plugouts = 0;
	}
	else {
		p_ring_stats->simple.n_rx_interrupt_received = 0;
		p_ring_stats->simple.n_rx_interrupt_requests = 0;
		p_ring_stats->simple.n_tx_dev_mem_byte_count = 0;
		p_ring_stats->simple.n_tx_dev_mem_pkt_count = 0;
		p_ring_stats->simple.n_tx_dev_mem_oob = 0;
	}
}

void zero_cq_stats(cq_stats_t* p_cq_stats)
{
	p_cq_stats->n_rx_pkt_drop = 0;
	p_cq_stats->n_rx_drained_at_once_max = 0;
}

void zero_bpool_stats(bpool_stats_t* p_bpool_stats)
{
	p_bpool_stats->n_buffer_pool_size = 0;
	p_bpool_stats->n_buffer_pool_no_bufs = 0;
}

void zero_counters(sh_mem_t* p_sh_mem)
{
	log_msg("Zero counters...");
	for (size_t i=0; i < p_sh_mem->max_skt_inst_num; i++) {
		size_t fd = (size_t)p_sh_mem->skt_inst_arr[i].skt_stats.fd;
		if (p_sh_mem->skt_inst_arr[i].b_enabled && g_fd_mask[fd]){
			zero_socket_stats(&p_sh_mem->skt_inst_arr[i].skt_stats);
		}
	}	
	zero_iomux_stats(&p_sh_mem->iomux);

	for (int i = 0; i < NUM_OF_SUPPORTED_CQS; i++) {
		zero_cq_stats(&p_sh_mem->cq_inst_arr[i].cq_stats);
	}
	for (int i = 0; i < NUM_OF_SUPPORTED_RINGS; i++) {
		zero_ring_stats(&p_sh_mem->ring_inst_arr[i].ring_stats);
	}
	for (int i = 0; i < NUM_OF_SUPPORTED_BPOOLS; i++) {
		zero_bpool_stats(&p_sh_mem->bpool_inst_arr[i].bpool_stats);
	}
}

int get_pid(char* proc_desc, char* argv0)
{
	char* app_name = NULL;
	int pid = -1;

	if (NULL == proc_desc) {
		return -1;
	}

	if (user_params.proc_ident_mode == e_by_pid_str) {
		errno = 0;
		pid = strtol(proc_desc, NULL, 0);
		if (errno != 0 || pid < 0) {
			log_err("'-p' Invalid pid val: %s", proc_desc);
			usage(argv0);
			cleanup(NULL);
			pid = -1;
		}
	}
	else {
		if (user_params.proc_ident_mode == e_by_app_name)
			app_name = proc_desc;

		char* pid_str = look_for_vma_stat_active_sh_obj(app_name);
		if (pid_str) {
			errno = 0;
			pid = strtol(pid_str, NULL, 0);
			if (errno != 0) {
				log_system_err("Failed to convert:%s", pid_str);
				cleanup(NULL);
				pid = -1;
			};
			free(pid_str);
		}
		else {
			log_err("Failed to identify process please provide pid of active proccess...\n");
		}
	}

	return pid;
}

void set_dumping_data(sh_mem_t* p_sh_mem)
{
	p_sh_mem->fd_dump = user_params.fd_dump;
	p_sh_mem->fd_dump_log_level = user_params.fd_dump_log_level;
}

void set_vma_log_level(sh_mem_t* p_sh_mem)
{
	p_sh_mem->log_level = user_params.vma_log_level;
}


void set_vma_log_details_level(sh_mem_t* p_sh_mem)
{
	p_sh_mem->log_details_level = (int)user_params.vma_details_level;
}

//////////////////forward declarations /////////////////////////////
void get_all_processes_pids(std::vector<int> &pids);
int print_processes_stats(const std::vector<int> &pids);

////////////////////////////////////////////////////////////////////
int main (int argc, char **argv)
{
	char proc_desc[MAX_BUFF_SIZE] = {0};

	set_defaults();	
	if (!g_fd_mask) return 1;

	while (1) {
		int c = 0;
		
		static struct option long_options[] = {
			{"interval",		1,	NULL,	'i'},
			{"cycles",		1,	NULL,	'c'},
			{"view",		1,	NULL,	'v'},
			{"details",		1,	NULL,	'd'},
			{"pid",			1,	NULL,	'p'},
			{"directory",		1,	NULL,	'k'},
			{"sockets",		1,	NULL,	's'},
			{"version",		0,	NULL,	'V'},
			{"zero",		0,	NULL,	'z'},
			{"log_level",		1,	NULL,	'l'},
			{"fd_dump",		1,	NULL,	'S'},
			{"details_level",	1,	NULL,	'D'},
			{"name",		1,	NULL,	'n'},
			{"find_pid",		0,	NULL,	'f'},
			{"forbid_clean",	0,	NULL,	'F'},
			{"help",		0,	NULL,	'h'},
			{0,0,0,0}
		};
		
		if ((c = getopt_long(argc, argv, "i:c:v:d:p:k:s:Vzl:S:D:n:fFh?", long_options, NULL)) == -1)
			break;

		switch (c) {
		case 'i': {
			errno = 0;
			int interval = strtol(optarg, NULL, 0);
			if (errno != 0  || interval < 0) {
				log_err("'-%c' Invalid interval val: %s", c,optarg);
				usage(argv[0]); 
				cleanup(NULL);
				return 1;
			}
			user_params.interval = interval;    
		}
			break;
		case 'c': {
			errno = 0;
			int cycles = strtol(optarg, NULL, 0);
			if (errno != 0  || cycles < 0) {
				log_err("'-%c' Invalid cycles val: %s", c,optarg);
				usage(argv[0]);
				cleanup(NULL);
				return 1;
			}
			user_params.cycles = cycles;
		}
			break;
		case 'v': {			
			errno = 0;
			int view_mod = 0;
			view_mod = strtol(optarg, NULL, 0);
			if (errno != 0  || view_mod < 1 || view_mod > VIEW_MODES_NUM) {
				log_err("'-%c' Invalid view val: %s", c,optarg);
				usage(argv[0]);  
				cleanup(NULL);
				return 1;
			}
			user_params.view_mode = (view_mode_t)view_mod; 
		}
			break;
		case 'd': {
			errno = 0;
			int detail_mode = strtol(optarg, NULL, 0);
			if (errno != 0  || detail_mode < 1 || detail_mode > PRINT_DETAILS_MODES_NUM) {
				log_err("'-%c' Invalid details val: %s", c,optarg);
				usage(argv[0]); 
				cleanup(NULL);
				return 1;
			}		
			user_params.print_details_mode = (print_details_mode_t)detail_mode;  
		}
			break;			
		case 'p': 
			user_params.proc_ident_mode = e_by_pid_str;
			strncpy(proc_desc, optarg, sizeof(proc_desc) - 1);
			proc_desc[sizeof(proc_desc) - 1] = '\0';
			break;	
		case 'k': 
			user_params.vma_stats_path = std::string((char*)optarg);
			break;			
		case 's': {
			if (update_fds_mask(optarg)) {
				usage(argv[0]); 
				cleanup(NULL);
				return 1;
			}
		}
			break;	
		case 'V':
			print_version(-1);
			cleanup(NULL);
			return 0;
		case 'z':
			user_params.write_auth = true;
			user_params.zero_counters = true;
			break;
		case 'l': {			
			vlog_levels_t log_level = log_level::from_str(optarg, VLOG_INIT);
			if (log_level == VLOG_INIT) {
				log_err("'-%c' Invalid log level val: %s", c,optarg);
				usage(argv[0]);
				cleanup(NULL);
				return 1;
			}
			user_params.write_auth = true;
			user_params.vma_log_level = log_level;
		}
			break;
		case 'S': {
			errno = 0;
			optind--;
			int fd_to_dump = strtol(argv[optind], NULL, 0);
			if (errno != 0  || fd_to_dump < 0) {
				log_err("'-%c' Invalid fd val: %s", c, argv[optind]);
				usage(argv[0]);
				cleanup(NULL);
				return 1;
			}
			user_params.fd_dump = fd_to_dump;
			if (++optind < argc && *argv[optind] != '-') {
				vlog_levels_t dump_log_level = log_level::from_str(argv[optind], VLOG_INIT);
				if (dump_log_level == VLOG_INIT) {
					log_err("'-%c' Invalid log level val: %s", c, argv[optind]);
					usage(argv[0]);
					cleanup(NULL);
					return 1;
				}
				user_params.fd_dump_log_level = dump_log_level;
			}
		}
			break;
		case 'D': {			
			errno = 0;
			int details_level = 0;
			details_level = strtol(optarg, NULL, 0);
			if (errno != 0  || details_level < 0 || details_level >= VLOG_DETAILS_NUM) {
				log_err("'-%c' Invalid details level val: %s", c,optarg);
				usage(argv[0]);  
				cleanup(NULL);
				return 1;
			}
			user_params.write_auth = true;
			user_params.vma_details_level = details_level; 
		}
			break;
		case 'n':		
			user_params.proc_ident_mode = e_by_app_name;
			strncpy(proc_desc, optarg, sizeof(proc_desc) - 1);
			proc_desc[sizeof(proc_desc) - 1] = '\0';
			break;
		case 'f': 
			user_params.proc_ident_mode = e_by_runn_proccess;
			break;
		case 'F':
			user_params.forbid_cleaning = true;
			break;
		case '?':
		case 'h':
			usage(argv[0]);
			return 0;
			break;
		default:
			usage(argv[0]);
			cleanup(NULL);
			return 1;
		}
	}

	clean_inactive_sh_ibj();

	std::vector<int> pids;
	if(user_params.view_mode == e_netstat_like) {
		get_all_processes_pids(pids);
	}
	else {
		int pid = get_pid(proc_desc, argv[0]);
		if (pid != -1) pids.push_back(pid);
	}

	if ( pids.size() == 0 ){
		free(g_fd_mask);
		if(user_params.view_mode == e_netstat_like) {
			print_headers();
			return 0;
		}
		else {
			usage(argv[0]);
			return 1;
		}
	}

	if(user_params.view_mode == e_netstat_like)
		user_params.cycles =1;// print once and exit

	int ret = print_processes_stats(pids);

	free(g_fd_mask);
	return ret;
}

/////////////////////////////////
int  init_print_process_stats(sh_mem_info_t & sh_mem_info)
{
	sh_mem_t* sh_mem;
	int pid = sh_mem_info.pid;

	sprintf(sh_mem_info.filename_sh_stats, "%s/vmastat.%d", user_params.vma_stats_path.c_str(), pid);
	
	if (user_params.write_auth)//S_IRUSR | S_IWUSR | S_IRGRP
		sh_mem_info.fd_sh_stats = open(sh_mem_info.filename_sh_stats,
						O_RDWR, __S_IREAD | __S_IWRITE| S_IROTH);
	else
		sh_mem_info.fd_sh_stats = open(sh_mem_info.filename_sh_stats, O_RDONLY);
	
	if (sh_mem_info.fd_sh_stats < 0) {
		log_err("VMA statistics data for process id %d not found\n", pid);
		return 1;
	}
	sh_mem_info.p_sh_stats = mmap(0, sizeof(sh_mem_t), PROT_READ, MAP_SHARED, sh_mem_info.fd_sh_stats, 0);
	MAP_SH_MEM(sh_mem,sh_mem_info.p_sh_stats);
	if (sh_mem_info.p_sh_stats == MAP_FAILED) {
		log_system_err("MAP_FAILED - %s\n", strerror (errno));
		close(sh_mem_info.fd_sh_stats);
		return 1;
	}

	int version_check = 1;
	if (sizeof(STATS_PROTOCOL_VER) > 1) {
		if (memcmp(sh_mem->stats_protocol_ver, STATS_PROTOCOL_VER, min(sizeof(sh_mem->stats_protocol_ver), sizeof(STATS_PROTOCOL_VER)))) {
			log_err("Version %s is not compatible with stats protocol version %s\n",
					STATS_PROTOCOL_VER, sh_mem->stats_protocol_ver);
			version_check = 0;
		}
	} else {
		if (!check_vma_ver_compatability(&sh_mem->ver_info)) {
			log_err("Version %d.%d.%d.%d is not compatible with VMA version %d.%d.%d.%d\n",
					VMA_LIBRARY_MAJOR, VMA_LIBRARY_MINOR,
					VMA_LIBRARY_REVISION, VMA_LIBRARY_RELEASE,
					sh_mem->ver_info.vma_lib_maj, sh_mem->ver_info.vma_lib_min,
					sh_mem->ver_info.vma_lib_rev, sh_mem->ver_info.vma_lib_rel);
			version_check = 0;
		}
	}
	if (!version_check) {
		if (munmap(sh_mem_info.p_sh_stats, sizeof(sh_mem_t)) != 0) {
				log_system_err("file='%s' sh_mem_info.fd_sh_stats=%d; error while munmap shared memory at [%p]\n", sh_mem_info.filename_sh_stats, sh_mem_info.fd_sh_stats, sh_mem_info.p_sh_stats);
		}
		close(sh_mem_info.fd_sh_stats);
		return 1;
	}

	sh_mem_info.shmem_size = SHMEM_STATS_SIZE(sh_mem->max_skt_inst_num);
	if (munmap(sh_mem_info.p_sh_stats, sizeof(sh_mem_t)) != 0) {
		log_system_err("file='%s' sh_mem_info.fd_sh_stats=%d; error while munmap shared memory at [%p]\n", sh_mem_info.filename_sh_stats, sh_mem_info.fd_sh_stats, sh_mem_info.p_sh_stats);
	}
	if (user_params.write_auth)
		sh_mem_info.p_sh_stats = mmap(0, sh_mem_info.shmem_size, PROT_WRITE|PROT_READ, MAP_SHARED, sh_mem_info.fd_sh_stats, 0);
	else
		sh_mem_info.p_sh_stats = mmap(0, sh_mem_info.shmem_size, PROT_READ, MAP_SHARED, sh_mem_info.fd_sh_stats, 0);
	
	if (sh_mem_info.p_sh_stats == MAP_FAILED) {
		log_system_err("MAP_FAILED - %s\n", strerror (errno));
		close(sh_mem_info.fd_sh_stats);
		return 1;
	}
	MAP_SH_MEM(sh_mem,sh_mem_info.p_sh_stats);
	if(user_params.view_mode != e_netstat_like)
		print_version(pid);
	if (user_params.zero_counters == true)
		zero_counters(sh_mem);
	if (user_params.vma_log_level != VLOG_INIT)
		set_vma_log_level(sh_mem);
	if (user_params.vma_details_level != INIT_VMA_LOG_DETAILS)
		set_vma_log_details_level(sh_mem);
	if (user_params.fd_dump != STATS_FD_STATISTICS_DISABLED)
		set_dumping_data(sh_mem);


	// here we indicate VMA to write to shmem
	inc_read_counter(sh_mem);
	return 0;
}

////////////////////////////////////////////////////////////////////
int  complete_print_process_stats(sh_mem_info_t & sh_mem_info)
{
	sh_mem_t* sh_mem;
	MAP_SH_MEM(sh_mem,sh_mem_info.p_sh_stats);

	stats_reader_handler(sh_mem, sh_mem_info.pid);
	cleanup(&sh_mem_info);
	return 0;
}


///////////////////////////
void get_all_processes_pids(std::vector<int> &pids)
{
	const int MODULE_NAME_SIZE = strlen(MODULE_NAME);
	const int PID_OFFSET = MODULE_NAME_SIZE + 1;

	DIR *dir = opendir(user_params.vma_stats_path.c_str());
	if (dir == NULL){
		log_system_err("opendir %s failed\n", user_params.vma_stats_path.c_str());
		return;
	}

	for( struct dirent *dirent = readdir(dir); dirent != NULL ; dirent = readdir(dir) ) {
		if(!strncmp("vmastat.", dirent->d_name, MODULE_NAME_SIZE)) {
			char* pid_str = dirent->d_name + PID_OFFSET;
			if (check_if_process_running(pid_str)) {
				errno = 0;
				int pid = strtol(pid_str, NULL, 0);
				if (errno == 0) {
					pids.push_back(pid);
				}
				else {
					log_system_err("Failed to convert:%s", pid_str);
				}
			}
		}
	}
	closedir(dir);
}

///////////////////////////
int print_processes_stats(const std::vector<int> &pids)
{
	const int SIZE = pids.size();

	int num_instances = 0;
	sh_mem_info_t sh_mem_info[SIZE];

	// 1. N * prepare shmem and indicate VMA to update shmem
	for (int i = 0; i < SIZE; ++i){
		sh_mem_info[num_instances].pid = pids[i];
		if (0 == init_print_process_stats(sh_mem_info[num_instances]))
			++num_instances;
	}

	// 2. one sleep to rule them all
	usleep(STATS_READER_DELAY * 1000);// After 'init_print_process_stats' we wait for VMA publisher to recognize
	                                  // that we asked for statistics, otherwise, the first read will be zero

	// 3. N * read from shmem, write to user, and shmem cleanup
	for (int i = 0; i < num_instances; ++i)
		complete_print_process_stats(sh_mem_info[i]);

	return 0;
}
