/*
 * (c) Copyright IBM Corp. 2005 All Rights Reserved
 *
 * Physical Memory Information Module
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * at your option any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/**
 * @file base_parser.c
 *
 * @brief Basic data analyzers for general process information.
 *
 * This part provides the source for the analysis of the various
 * processes. This can be either process global information, in
 * which the total information for a given process is analyzed.
 * Or it could be per VMA analysis within a given process.
 *
 * @author International Business Machines
 * @author Paul Movall <movall@us.ibm.com>
 *
 * @version Current Version: 1.0
 *
 * @date Current Date: 1/2005
 *
 * @version 0.1, 11/2003: File created by Paul Movall <movall@us.ibm.com>
 * @version 1.0, 01/2005: Miscellaneous cleanup for publish
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>

#include "td.h"

/**
 * @brief Print out the mapping array for one VMA.
 *
 * @param outf The file to which to output the data.
 * @param cur_vma The current VMA descriptor.
 * @return None.
 */
void print_vma_map(FILE *outf, vma_t *cur_vma) {
	map_t *cur_map;
	int pages;
	char *map_str;
	int cur_indx;
	char save_ch;

	pages = (cur_vma->end - cur_vma->start + 1) / PAGE_SIZE;
	map_str = malloc((sizeof(char) * pages) + 1);
	if (map_str == NULL) {
		perror("Error allocating printable VMA map buffer. Exiting!\n");
		exit(-1);
	}

	memset(map_str, '.', sizeof(char) * pages);
	map_str[sizeof(char) * pages] = '\0';

	for (cur_map = cur_vma->map_head;
	     cur_map != NULL;
	     cur_map = cur_map->next) {
		cur_indx = (cur_map->virt - cur_vma->start) / PAGE_SIZE;
		if ( (cur_map->count > 0) &&
		     (cur_map->count < 10) ) {
			map_str[cur_indx] = '0' +
			  cur_map->count;
		} else if (cur_map->count > 10) {
			map_str[cur_indx] = '#';
		}
	}

	if (terse) {
		fprintf(outf, "[%-s]\n", map_str);
	} else {
		cur_indx = 0;
		do {
			fprintf(outf, "[%-64.64s]\n", &(map_str[cur_indx]));
			cur_indx += 64;
		} while (cur_indx < pages);
	}
}

/**
 * @brief Output the basic per process information.
 *
 * This function will walk through all of the processes and format for
 * output the per process information. It can output either the entire
 * process summary or the per VMA information by process.
 *
 * @pre The input parser has already filtered out threads vs. processes
 * and has built the global task list.
 *
 * @param outf The file descriptor for the output data.
 * @param vma_detail Input flag to determine if the per process
 * VMA detail is to be included in the output. If the flag is TRUE,
 * then the detail is included.
 *
 * @return None.
 */
void base_info_shared(FILE *outf, int vma_detail) {
	task_t *cur_task;
	vma_t *cur_vma;
	int pg_count;
	int count;
	int max_pages = 0;
	int num_tasks = 0;
	FILE *postf;
	int max_lines;

	if (terse) {
		fprintf(outf,
			"Name, RW Count, RX Count, Total, Name, "
			"Stack, .Data, .Text, Heap, Lib .bss, Lib .data, "
			"Lib .text, Single, Multi");
		if (vma_detail) {
			fprintf(outf, ", Flags, Start, End, Map\n");
		} else {
			fprintf(outf, "\n");
		}
	}

	if ((post_parm) && (!vma_detail)) {
		/* calculate num of tasks and max number of pages in a task */
		for (cur_task = head_task.head;
		     cur_task != NULL;
		     cur_task = cur_task->next) {
			num_tasks++;
			pg_count = 0;

			for (cur_vma = cur_task->vma_head;
			     cur_vma != NULL;
			     cur_vma = cur_vma->next) {
				if (virtual) {
					count =
					  cur_vma->vlen / PAGE_SIZE;
				} else {
					count =
					  cur_vma->pte_info->user_space_count;
				}

				switch (cur_vma->vma_flags & VMA_FLAGS_MASK_RWX)
				{
				case (VMA_FLAGS_RD | VMA_FLAGS_EXEC):
				case (VMA_FLAGS_RD | VMA_FLAGS_WR):
				case (VMA_FLAGS_RWX):
					pg_count += count;
					break;
				default:
					break;
				}
			}

			if (pg_count > max_pages) {
				max_pages = pg_count;
			}
		}

		if ((postf = fopen(post_file, "w")) == NULL) {
			perror("Error opening PostScript file!");
			return;
		}

		max_lines =
		  WritePostScriptPreface(postf, num_tasks, max_pages, FALSE);
		WritePostScriptStartPage(postf, FALSE);
	}

	count = 0;
	for (cur_task = head_task.head;
	     cur_task != NULL;
	     cur_task = cur_task->next) {
		task_summary(cur_task, outf, vma_detail, postf);

		if ((++count > max_lines) && (post_parm) && (!vma_detail) &&
		    (cur_task->next != NULL)) {
			WritePostScriptEndPage(postf, FALSE);
			count = 0;
			WritePostScriptStartPage(postf, FALSE);
		}
	}

	if ((post_parm) && (!vma_detail)) {
		WritePostScriptEndPage(postf, FALSE);
		WritePostScriptEpilog(postf, num_tasks, max_pages, FALSE);
		fclose(postf);
	}
}

/**
 * @brief The basic parser for the per process basic information.
 *
 * @param outf The file descriptor for the output file.
 * @return None.
 *
 * @note This function is dependent upon the @c base_info_shared()
 * function to perform the real work.
 */ 
void base_info(FILE *outf) {
	base_info_shared(outf, FALSE);
}

/**
 * @brief The basic parser for the VMA per process basic information.
 *
 * @param outf The file descriptor for the output file.
 * @return None.
 *
 * @note This function is dependent upon the @c base_info_shared()
 * function to perform the real work.
 */ 
void base_vma(FILE *outf) {
	base_info_shared(outf, TRUE);
}

