sysperfana/memspyext/com.nokia.s60tools.swmtanalyser/src/com/nokia/s60tools/swmtanalyser/analysers/LinearAnalyser.java
author Matti Laitinen <matti.t.laitinen@nokia.com>
Wed, 21 Apr 2010 20:01:08 +0300
changeset 7 8e12a575a9b5
permissions -rw-r--r--
MemSpy Carbide Extension 1.6.0

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
package com.nokia.s60tools.swmtanalyser.analysers;

import java.text.DecimalFormat;
import java.util.ArrayList;

import com.nokia.s60tools.swmtanalyser.data.DiskOverview;
import com.nokia.s60tools.swmtanalyser.data.KernelElements;
import com.nokia.s60tools.swmtanalyser.data.ParsedData;
import com.nokia.s60tools.swmtanalyser.data.SystemData;
import com.nokia.s60tools.swmtanalyser.model.SWMTLogReaderUtils;

/**
 * 
 * Defines Enum constants for all Kernel events to be analysed.
 */
enum KERNEL_EVENTS{
	NO_OF_THREADS("Number of Threads"), NO_OF_PROCESSES("Number of Processes"),
	NO_OF_TIMERS("Number of Timers"), NO_OF_SEMAPHORES("Number of Semaphores"),
	NO_OF_SERVERS("Number of Servers"), NO_OF_MSGQUEUES("Number of Msg. Queues"),
	NO_OF_SESSIONS("Number of Sessions"), NO_OF_CHUNKS("Number of Chunks");
	
	private String event_name;
	private String event_cateogory = "System Data";
	
	private KERNEL_EVENTS(String eventName) {
		this.event_name = eventName;
	}
	
	/**
	 * Returns event string
	 * @return name of the event
	 */
	public String getEventName()
	{
		return event_name;
	}
	
	/**
	 * Returns event category
	 * @return category of the event
	 */
	public String getEventCategory()
	{
		return event_cateogory;
	}
}

/**
 * Analyses RAM and DISK events and Kernal events. It implements interface IAnalyser.
 * 
 */
public class LinearAnalyser implements IAnalyser {

	/**
	 * {@link #RAM_AND_DISK_TITLE} and {@link #KERNEL_ELEMS_TITLE} issues
	 */
	ArrayList<ResultsParentNodes> allIssues = new ArrayList<ResultsParentNodes>();
	
	protected static DecimalFormat Bytes_Format = new DecimalFormat("#####.##");
	protected int [] time_intervals = null;
	
	private static final String RAM_AND_DISK_TITLE = "RAM and Disk Memory";
	private static final String KERNEL_ELEMS_TITLE = "Kernel Elements";
	
	private ParsedData logData = null;
	

	/* (non-Javadoc)
	 * @see com.nokia.s60tools.swmtanalyser.analysers.IAnalyser#analyse(com.nokia.s60tools.swmtanalyser.data.ParsedData)
	 */
	public void analyse(ParsedData logData) {
		
		this.logData = logData;
		allIssues.clear();
		
		//List to store RAM DISK issues
		ArrayList<ResultElements> ram_disk_issues = new ArrayList<ResultElements>();

		//Analyse RAM events using the logData and store the results in given list
		analyseUsedRam(logData, ram_disk_issues);
		//Analyse DISK events using the logData and store the results in given list
		analyseDiskSizes(logData, ram_disk_issues);
		
		//Create parent for all RAM and Disks issues.
		ResultsParentNodes ram_and_disk_node = new ResultsParentNodes(RAM_AND_DISK_TITLE);
		ram_and_disk_node.setChildren(ram_disk_issues);
		allIssues.add(ram_and_disk_node);
		
		//List to store Kernel issues
		ArrayList<ResultElements> kernel_issues = new ArrayList<ResultElements>();
		//Analyse Kernel events using the logData and store the results in given list		
		analyseKernelHandles(logData, kernel_issues);
	
		//Create parent for all kernel issues found
		ResultsParentNodes kernel_analysis_node = new ResultsParentNodes(KERNEL_ELEMS_TITLE);
		kernel_analysis_node.setChildren(kernel_issues);
		allIssues.add(kernel_analysis_node);
	}


	/* (non-Javadoc)
	 * @see com.nokia.s60tools.swmtanalyser.analysers.IAnalyser#getResults()
	 */
	public Object[] getResults() {
		return allIssues.toArray();
	}
	

	/* (non-Javadoc)
	 * @see com.nokia.s60tools.swmtanalyser.analysers.IAnalyser#getChildren(java.lang.Object)
	 */
	public Object[] getChildren(Object parent)
	{
		if(parent instanceof ResultsParentNodes && allIssues.contains(parent))
		{
			return ((ResultsParentNodes)(parent)).getChildren();
		}
		else
			return null;
	}
	
	/**
	 * This method linearly analyses the variation of used Ram size.
	 * @param logData signifies data of all seleced cycles.
	 * @param results signifies the list to which all issues will be added to.
	 */
	private void analyseUsedRam(ParsedData logData, ArrayList<ResultElements> results)
	{
		SWMTLogReaderUtils utils = new SWMTLogReaderUtils();
		
		ArrayList<SystemData> systemData = utils.getSystemDataFromAllCycles(logData);
		
		//Store used memory values
		long [] usedRamValues = new long[logData.getNumberOfCycles()];
		
		//Calculate used memory using the total and free memroy
		for(int i=0; i<logData.getNumberOfCycles(); i++)
		{
			long totalMem = systemData.get(i).getTotalMemory();
			long freeMem = systemData.get(i).getFreeMemory();
			
			if(totalMem == -1 || freeMem == -1){
				usedRamValues[i] = 0;
			}
			else{
				long usedMemory = totalMem - freeMem; 
				usedRamValues[i] = usedMemory;
			}
		}
		
		//Get the delta value.
		long usedMemChange = utils.calculateDeltaForGivenSet(usedRamValues);
			
		ResultElements res_elem = new ResultElements("RAM", "RAM used", getFormattedBytes(usedMemChange), usedMemChange, AnalyserConstants.DeltaType.SIZE);
		res_elem.setEventValues(usedRamValues);
		
		calculateGrowinessAndPriority(usedRamValues, res_elem);
			
		//Add if the element priority is CRITICAL or HIGH or NORMAL
		if(res_elem.getPriority() != AnalyserConstants.Priority.NEGLIGIBLE)
			results.add(res_elem);
			
	}
	
	/**
	 * This method linearly analyses the variation of used size for all disks.
	 * @param logData signifies data of all seleced cycles.
	 * @param results signifies the list to which all issues will be added to.
	 */
	private void analyseDiskSizes(ParsedData logData, ArrayList<ResultElements> results)
	{
		SWMTLogReaderUtils utils = new SWMTLogReaderUtils();
		ArrayList<String> diskNames = utils.getAllDiskNames(logData);
		for(String disk:diskNames)	{
			ArrayList<DiskOverview> diskData = utils.getUsedMemoryAndSizesForDisk(disk, logData);

			//Store used memory values
			long [] diskUsedValues = new long[logData.getNumberOfCycles()];
			for(int i=0; i<logData.getNumberOfCycles(); i++){
				diskUsedValues[i] = diskData.get(i).getUsedSize();
			}
			long delta = utils.calculateDeltaForGivenSet(diskUsedValues);
					
			ResultElements res_elem = new ResultElements(disk, "Disk used", getFormattedBytes(delta), delta, AnalyserConstants.DeltaType.SIZE);
			res_elem.setEventValues(diskUsedValues);
			
			calculateGrowinessAndPriority(diskUsedValues, res_elem);
			//Add if the element priority is CRITICAL or HIGH or NORMAL			
			if(res_elem.getPriority() != AnalyserConstants.Priority.NEGLIGIBLE)
				results.add(res_elem);			
		}	
	}
	
	/**
	 * This method analyses variation of various kernel handles.
	 * @param logData signifies data of all seleced cycles.
	 * @param results signifies the list to which all issues will be added to.
	 */
	private void analyseKernelHandles(ParsedData logData, ArrayList<ResultElements> results)
	{
		SWMTLogReaderUtils utils = new SWMTLogReaderUtils();
		ArrayList<KernelElements> kernelElems = utils.getKerenelElemsFromAllCycles(logData);
		
		for(KERNEL_EVENTS event: KERNEL_EVENTS.values()){
			long [] event_values = new long[kernelElems.size()];
			for(int i=0; i<kernelElems.size(); i++)
				event_values[i] = getKernelEventValue(kernelElems.get(i),event);
			long delta = utils.calculateDeltaForGivenSet(event_values);
			
			ResultElements result_elem = new ResultElements(event.getEventName(), event.getEventCategory(), Long.toString(delta), delta, AnalyserConstants.DeltaType.COUNT);
			result_elem.setEventValues(event_values);
			
			calculateGrowinessAndPriority(event_values,result_elem);
			//Add if the element priority is CRITICAL or HIGH or NORMAL
			if(result_elem.getPriority() != AnalyserConstants.Priority.NEGLIGIBLE)
				results.add(result_elem);
		}		
	}
	
	/** 
	 * 
	 * @param kernelData -- structure which holds info about all kernel handles in one cycle.
	 * @param event -- name of the kernel event whose value must be read from the structure.
	 * @return value of the event.
	 */
	private long getKernelEventValue(KernelElements kernelData, KERNEL_EVENTS event)
	{
		long event_value = 0;
		
		switch(event)
		{
			case NO_OF_PROCESSES:
				event_value = kernelData.getNumberOfProcesses();
				break;
			case NO_OF_THREADS:
				event_value = kernelData.getNumberOfThreads();
				break;
			case NO_OF_CHUNKS:
				event_value = kernelData.getNumberOfChunks();
				break;
			case NO_OF_SEMAPHORES:
				event_value = kernelData.getNumberOfSemaphores();
				break;
			case NO_OF_SERVERS:
				event_value = kernelData.getNumberOfServers();
				break;
			case NO_OF_SESSIONS:
				event_value = kernelData.getNumberOfSessions();
				break;
			case NO_OF_TIMERS:
				event_value = kernelData.getNumberOfTimers();
				break;
			case NO_OF_MSGQUEUES:
				event_value = kernelData.getNumberOfMsgQueues();
				break;
		}
		
		return event_value;
	}
	
	/**
	 * The method calculates growiness factor based on given event 
	 * values and given time intervals.
	 * @param values values of the event to be analysed
	 * @param time time intervals 
	 * @return growiness factor.
	 */
	private double calculateGrowiness(long [] values, int [] time)
	{
		if(values.length != time.length)
			return 0;
		
		long [] time_differences = new long[time.length];
		
		for(int i=0; i<time.length; i++)
		{
			if(i == 0)
				time_differences[i] = 0;
			else
				time_differences[i] = time[i] - time[i-1];
		}
		long [] normalizedRoCs = calculateNormalizedRoC(values);
		
		double [] normalizedRocWithTime = new double [normalizedRoCs.length];
		
		for(int i=0; i<normalizedRocWithTime.length; i++)
		{
			if(i == 0)
				normalizedRocWithTime[i] = 0;
			else
			{
				//Assuming time difference is greater than zero.
									
				normalizedRocWithTime[i] = (double)(normalizedRoCs[i])/Math.log(time_differences[i]);
			}
		}
		
		double [] overallNormalizedRoc = new double [normalizedRocWithTime.length];
		
		overallNormalizedRoc[0] = 0;
		double totalNormalizedRoc = 0.0;
		
		for(int i=1; i < normalizedRocWithTime.length; i++)
		{
			if(normalizedRocWithTime[i] > 0)
				overallNormalizedRoc[i] =  overallNormalizedRoc[i-1] + normalizedRocWithTime[i];
			else if(normalizedRocWithTime[i] < 0)
				overallNormalizedRoc[i] =  -Math.log(-normalizedRocWithTime[i])+ overallNormalizedRoc[i-1];
			else
				overallNormalizedRoc[i] = overallNormalizedRoc[i-1];
		}
		
		totalNormalizedRoc = overallNormalizedRoc[overallNormalizedRoc.length - 1];
		int growth_index = calculateStableGrowth(values);
		double growiness_factor = Math.exp(growth_index);
		
		double log_of_first = 0;
		double log_of_last = 0;
		
		if(values[values.length -1] != 0)
			log_of_last = Math.log(values[values.length -1]);
		
		if(values[0] != 0)
			log_of_first = Math.log(values[0]);
		
		double log_diff = log_of_last - log_of_first;
		
		double tmp = Math.round(Math.exp(totalNormalizedRoc));
		double final_growth_factor = tmp * log_diff + growiness_factor;
		
		return final_growth_factor;
	}
	
	protected long [] calculateNormalizedRoC(long [] values)
	{
		long [] deltas = new long[values.length];
		long [] valueVsBaseline = new long[values.length];
		
		deltas[0] = 0;
		valueVsBaseline[0] = 0;
		
		for(int i=1; i<values.length; i++)
		{
			deltas[i] = values[i] - values[i-1];
			valueVsBaseline[i] = values[i] - values[0];
		}
		
		long [] normalizedRoC = new long[values.length];
		normalizedRoC[0] = 0;
		
		for(int i=1; i < values.length; i++)
		{
			if(deltas[i] > 0)
			{
				if(valueVsBaseline[i] > 0)
					normalizedRoC[i] = (long)Math.log(deltas[i]);
					
				else if(valueVsBaseline[i] < 0)
					normalizedRoC[i] = (long)(Math.log(deltas[i] + valueVsBaseline[i]));
				else
					normalizedRoC[i] = 0;
			}
			else if(deltas[i] < 0)
				normalizedRoC[i] = -(long)Math.log(-deltas[i]);
			else
				normalizedRoC[i] = 0;
		}
		
		return normalizedRoC;
	}
	
	private int calculateStableGrowth(long [] values)
	{
		int [] stableGrowiness = new int[values.length];
		
		for(int i=0; i<stableGrowiness.length; i++)
		{
			if(i >0)
			{
				long diff = values[i] - values[i-1];
				
				if(diff > 0)
				{
					stableGrowiness[i] = stableGrowiness[i-1] + 1;
				}
				else if(diff == 0)
						stableGrowiness[i] = stableGrowiness[i-1];
				else
				{
					if(values[i] > values[0])
						stableGrowiness[i] = stableGrowiness[i-1] - 1;
					else
						stableGrowiness[i] = 0;
				}
			}
		}
		
		return stableGrowiness[stableGrowiness.length-1];
	}
	
	/**
	 * The method calculates priority of an issue, based on the values of
	 * growiness factor, delta factor and number of Cycles.
	 * @param growing_factor
	 * @param delta_factor
	 * @param noOfCycles
	 * @return priority of the issue.
	 */
	protected AnalyserConstants.Priority calculatePriorityFactor(double growing_factor, double delta_factor, int noOfCycles)
	{
		double high_threshold = Math.exp(noOfCycles - 1);
		int temp = noOfCycles/2;
		double normal_threshold = Math.exp(temp);
						
		int temp1 = noOfCycles/4;
		double low_threshold = Math.exp(temp1);
		
		if(growing_factor > high_threshold)
		{
			if(delta_factor > 0)
				return AnalyserConstants.Priority.CRITICAL;
		}
		if(growing_factor > normal_threshold)
		{
			if(delta_factor > 0.5)
				return AnalyserConstants.Priority.CRITICAL;
			else if(delta_factor > 0)
				return AnalyserConstants.Priority.HIGH;
		}
		if(growing_factor > low_threshold)
		{
			if(delta_factor > 3)
				return AnalyserConstants.Priority.CRITICAL;
			else if(delta_factor > 0.5)
				return AnalyserConstants.Priority.HIGH;
		}
		if(growing_factor > Math.exp(1))
		{
			if(delta_factor > 5)
				return AnalyserConstants.Priority.CRITICAL;
			else if(delta_factor > 3)
				return AnalyserConstants.Priority.HIGH;
		}
		if(growing_factor >0 && delta_factor > 0)
			return AnalyserConstants.Priority.NORMAL;
	
		return AnalyserConstants.Priority.NEGLIGIBLE;
	}
	
	/**
	 * The method formats the given value to Bytes, Kilo Bytes and Mega Bytes.
	 * @param bytes -- value to be formatted.
	 * @return formatted string in KB or MB
	 */
	protected String getFormattedBytes(long bytes)
	{
		String formatted_value = "";
		
		if (bytes < 1024)
		{
			formatted_value += Bytes_Format.format(bytes) + " B"; //$NON-NLS-1$
		}
		else if (bytes <= 500 * 1024)
		{
			formatted_value += Bytes_Format.format(bytes / 1024) + " KB"; //$NON-NLS-1$
		}
		else
		{
			formatted_value +=  Bytes_Format.format(((float) bytes / (1024 * 1024)))  + " MB"; //$NON-NLS-1$
		}
		
		return formatted_value;
	}
	
	/**
	 * The method calculates Growiness and priority based on given set of values.
	 * As no time intervals are provided, the method treats that the event is alive in all cycles (from first to last).
	 * @param event_values -- values of the event being analysed.
	 * @param result_elem -- structure to which calculated growiness and priority would be set to.
	 */
	protected void calculateGrowinessAndPriority(long [] event_values, ResultElements result_elem)
	{
		SWMTLogReaderUtils utils  = new SWMTLogReaderUtils();
		
		if(time_intervals == null)
			time_intervals = utils.getTimeIntervalsFromLogData(logData);
		
		calculateGrowinessAndPriority(event_values, time_intervals, result_elem);
		
	}
	
	/**
	 * The method calculates Growiness and priority based on given set of values and given time intervals
	 * @param event_values
	 * @param log_intervals
	 * @param result_elem
	 */
	protected void calculateGrowinessAndPriority(long [] event_values, int [] log_intervals, ResultElements result_elem)
	{
		double growiness = calculateGrowiness(event_values, log_intervals);
		
		long firstValue = event_values[0];
		long lastValue = event_values[event_values.length -1];
		
		double log_of_first = 0;
		double log_of_last = 0;
		
		if(firstValue != 0)
			log_of_first = Math.log(firstValue);
		if(lastValue != 0)
			log_of_last = Math.log(lastValue);
		
		double prioritization_factor = log_of_last - log_of_first;
		
		AnalyserConstants.Priority priority = calculatePriorityFactor(growiness, prioritization_factor, log_intervals.length);
	
		result_elem.setGrowingFactor(growiness);
		result_elem.setPriority(priority);
	
	}
}