sysperfana/analyzetoolext/com.nokia.s60tools.analyzetool/src/com/nokia/s60tools/analyzetool/engine/CallstackDataParser.java
changeset 15 0367d2db2c06
parent 6 f65f740e69f9
equal deleted inserted replaced
14:bb339882c6e9 15:0367d2db2c06
     9  * Initial Contributors:
     9  * Initial Contributors:
    10  * Nokia Corporation - initial contribution.
    10  * Nokia Corporation - initial contribution.
    11  *
    11  *
    12  * Contributors:
    12  * Contributors:
    13  *
    13  *
    14  * Description:  Definitions for the class ParseAnalyzeData
    14  * Description:  Definitions for the class CallstackDataParser
    15  *
    15  *
    16  */
    16  */
    17 package com.nokia.s60tools.analyzetool.engine;
    17 package com.nokia.s60tools.analyzetool.engine;
    18 
    18 
    19 import java.util.ArrayList;
    19 import java.util.ArrayList;
    25 import com.nokia.s60tools.analyzetool.engine.statistic.BaseInfo;
    25 import com.nokia.s60tools.analyzetool.engine.statistic.BaseInfo;
    26 import com.nokia.s60tools.analyzetool.engine.statistic.DllLoad;
    26 import com.nokia.s60tools.analyzetool.engine.statistic.DllLoad;
    27 import com.nokia.s60tools.analyzetool.engine.statistic.FreeInfo;
    27 import com.nokia.s60tools.analyzetool.engine.statistic.FreeInfo;
    28 import com.nokia.s60tools.analyzetool.engine.statistic.ProcessInfo;
    28 import com.nokia.s60tools.analyzetool.engine.statistic.ProcessInfo;
    29 import com.nokia.s60tools.analyzetool.global.Constants;
    29 import com.nokia.s60tools.analyzetool.global.Constants;
       
    30 import com.nokia.s60tools.analyzetool.global.Util;
    30 import com.nokia.s60tools.analyzetool.global.Constants.Operation;
    31 import com.nokia.s60tools.analyzetool.global.Constants.Operation;
    31 
    32 
    32 /**
    33 /**
    33  * This class parses line of .dat file format and extracts
    34  * This class parses line of .dat file format and extracts callstack data. For
    34  * callstack data. For now this is a separate class but could
    35  * now this is a separate class but could eventually be moved into
    35  * eventually be moved into ParseAnalyzeData after some refactoring. 
    36  * ParseAnalyzeData after some re-factoring.
    36  *
       
    37  */
    37  */
    38 public class CallstackDataParser {
    38 public class CallstackDataParser {
    39 	/** true if callstack reading for this BaseInfo is now complete*/
    39 
       
    40 	/** True, if callstack reading for this BaseInfo is now complete. */
    40 	boolean complete = false;
    41 	boolean complete = false;
    41 	/** resulting callstack */
    42 
       
    43 	/** Resulting callstack. */
    42 	private List<AllocCallstack> callstack;
    44 	private List<AllocCallstack> callstack;
       
    45 
    43 	/** BaseInfo for the current alloc or free */
    46 	/** BaseInfo for the current alloc or free */
    44 	private BaseInfo baseInfo;
    47 	private BaseInfo baseInfo;
       
    48 
    45 	/** ProcessInfo for the current alloc or free */
    49 	/** ProcessInfo for the current alloc or free */
    46 	private ProcessInfo process;
    50 	private ProcessInfo process;
    47 	/** number of remaining callstack elements expected to be processed with ALLOCF or FREEF */
    51 
       
    52 	/**
       
    53 	 * Number of remaining callstack elements expected to be processed with ALF
       
    54 	 * or FRF.
       
    55 	 */
    48 	private int remainingSize;
    56 	private int remainingSize;
    49 	
    57 
    50 	/** Cache for callstack items. Used when allocation fragment is parsed in the wrong order. */
    58 	/**
       
    59 	 * Cache for callstack items. Used when allocation fragment is parsed in the
       
    60 	 * wrong order.
       
    61 	 */
    51 	private HashMap<Integer, List<AllocCallstack>> callstackCache = null;
    62 	private HashMap<Integer, List<AllocCallstack>> callstackCache = null;
    52 	
    63 
    53 	/**
    64 	/**
    54 	 * Constructor
    65 	 * Constructor.
    55 	 * @param baseInfo BaseInfo for the current alloc or free
    66 	 * 
    56 	 * @param p ProcessInfo for the current alloc or free
    67 	 * @param baseInfo
    57 	 */
    68 	 *            BaseInfo for the current alloc or free
    58 	public CallstackDataParser(BaseInfo baseInfo, ProcessInfo p) {
    69 	 * @param processInfo
    59 		if (p == null || baseInfo == null || p.getProcessID() != baseInfo.getProcessID()){
    70 	 *            ProcessInfo for the current alloc or free
    60 			throw new IllegalArgumentException("BaseInfo and ProcessInfo are mandatory and must not be null; and the process id of both must match."); //$NON-NLS-1$
    71 	 */
       
    72 	public CallstackDataParser(BaseInfo baseInfo, ProcessInfo processInfo) {
       
    73 		if (processInfo == null || baseInfo == null
       
    74 				|| processInfo.getProcessID() != baseInfo.getProcessID()) {
       
    75 			throw new IllegalArgumentException(
       
    76 					"BaseInfo and ProcessInfo are mandatory and must not be null; and the process ID of both must match."); //$NON-NLS-1$
    61 		}
    77 		}
    62 		this.baseInfo = baseInfo;
    78 		this.baseInfo = baseInfo;
    63 		this.process = p;
    79 		this.process = processInfo;
    64 		callstack = new ArrayList<AllocCallstack>();
    80 		callstack = new ArrayList<AllocCallstack>();
    65 	}
    81 	}
    66 
    82 
    67 	/**
    83 	/**
    68 	 * Parses one line of PCSS statement
    84 	 * Parses one line of &ltAT&gt statement.
       
    85 	 * 
    69 	 * @param aLine
    86 	 * @param aLine
    70 	 * @return true, if this callstack is now complete (all parts are available)
    87 	 * @return true, if this callstack is now complete (all parts are available)
    71 	 */
    88 	 */
    72 	public final boolean parseLine(final String aLine) {
    89 	public final boolean parseLine(final String aLine) {
    73 			
    90 
    74 		if (aLine.indexOf(Constants.PREFIX) == -1){
    91 		if (aLine.indexOf(Constants.PREFIX) == -1) {
    75 			//not a PCSS statement
    92 			// not a <AT> statement
    76 			return false;
    93 			return false;
    77 		}
    94 		}
    78 		
    95 
    79 		String[] lineFragments = getLineFragments(aLine);
    96 		// String[] lineFragments = getLineFragments(aLine);
    80 		if (lineFragments.length < 5){
    97 
    81 			//not a valid PCSS statment for callstack processing
    98 		int index = aLine.indexOf(Constants.PREFIX);
       
    99 
       
   100 		String time = aLine.substring(0, index).trim();
       
   101 
       
   102 		// // get logging time of line in microseconds
       
   103 		// long logTime = Util.getLogTime(time);
       
   104 
       
   105 		long logTime = Long.parseLong(time, 16);
       
   106 
       
   107 		String usedString = aLine.substring(index, aLine.length());
       
   108 
       
   109 		String[] lineFragments = usedString.split(" ");
       
   110 
       
   111 		if (lineFragments.length < 5) {
       
   112 			// not a valid <AT> statement for callstack processing
    82 			return false;
   113 			return false;
    83 		}
   114 		}
    84 		int processID = Integer.parseInt(lineFragments[1], 16);
   115 		int processID = Integer.parseInt(lineFragments[1], 16);
    85 		if (baseInfo.getProcessID() != processID || process.getProcessID() != processID){
   116 		if (baseInfo.getProcessID() != processID
    86 			//statement is not for current process id
   117 				|| process.getProcessID() != processID) {
    87 			return false;
   118 			// statement is not for current process id
    88 		}
   119 			return false;
    89 		
   120 		}
    90 		//the operation must match
   121 
    91 		Constants.Operation op = Constants.Operation.toOperation(lineFragments[2]);
   122 		// the operation must match
    92 		if (!verifyOperation(op, baseInfo)){
   123 		Constants.Operation op = Constants.Operation
    93 			return false;
   124 				.toOperation(lineFragments[2]);
    94 		}
   125 		if (!verifyOperation(op, baseInfo)) {
    95 		
   126 			return false;
    96 		//the memory address must match
   127 		}
    97 		long memoryAddress = Long.parseLong(lineFragments[3],16);
   128 
    98 		if (baseInfo.getMemoryAddress() != memoryAddress){
   129 		// the memory address must match
    99 			return false;
   130 		long memoryAddress = Long.parseLong(lineFragments[3], 16);
   100 		}
   131 		if (baseInfo.getMemoryAddress() != memoryAddress) {
   101 		
   132 			return false;
   102 		
   133 		}
       
   134 
   103 		boolean ret = false;
   135 		boolean ret = false;
   104 		
   136 
   105 		switch (op) {
   137 		switch (op) {
   106 		case ALLOC:
   138 		case ALH:
   107 			ret = parseAlloc(lineFragments);
       
   108 			break;
       
   109 		case ALLOCH:
       
   110 			ret = parseHeader(lineFragments, 6);
   139 			ret = parseHeader(lineFragments, 6);
   111 			break;
   140 			break;
   112 		case FREEH:
   141 		case FRH:
   113 			int traceFileVersion = process.getTraceDataVersion();
   142 			ret = parseHeader(lineFragments, 5);
   114 			ret = parseHeader(lineFragments, traceFileVersion > 1 ? 5 : 4);
       
   115 			break;
   143 			break;
   116 		case ALLOCF:
   144 		case ALF:
   117 			//fall through
   145 			// fall through
   118 		case FREEF:
   146 		case FRF:
   119 			ret = parseFragment(lineFragments);				
   147 			ret = parseFragment(lineFragments, logTime);
   120 			break;
   148 			break;
   121 		default:
   149 		default:
   122 			// ignore this line
   150 			// ignore this line
   123 			break;
   151 			break;
   124 		}
   152 		}
   125 		
       
   126 		return ret;
   153 		return ret;
   127 	}
   154 	}
   128 	
   155 
   129 	private static boolean verifyOperation(Operation op, BaseInfo aBaseInfo) {
   156 	private static boolean verifyOperation(Operation op, BaseInfo aBaseInfo) {
   130 		return ((aBaseInfo instanceof AllocInfo && (op == Constants.Operation.ALLOC
   157 		return ((aBaseInfo instanceof AllocInfo && (op == Constants.Operation.ALH || op == Constants.Operation.ALF)) || (aBaseInfo instanceof FreeInfo && (op == Constants.Operation.FRH || op == Constants.Operation.FRF)));
   131 				|| op == Constants.Operation.ALLOCH || op == Constants.Operation.ALLOCF)) 
   158 	}
   132 				|| (aBaseInfo instanceof FreeInfo && (op == Constants.Operation.FREEH 
   159 
   133 				|| op == Constants.Operation.FREEF)));
   160 	// /**
   134 	}
   161 	// * Returns the line fragments of the AT statement
   135 
   162 	// *
   136 	/**
   163 	// * @param aLine
   137 	 * Returns the line fragments of the PCSS statement
   164 	// * the AT statement
   138 	 * @param aLine the PCSS statement
   165 	// * @return the line fragments separated by space
   139 	 * @return the line fragments separated by space
   166 	// */
   140 	 */
   167 	// private String[] getLineFragments(final String aLine) {
   141 	private String[] getLineFragments(final String aLine){
   168 	// int index = aLine.indexOf(Constants.PREFIX); // lines should be preceded
   142 		int index = aLine.indexOf(Constants.PREFIX); // lines should be preceded
   169 	//
   143 		
   170 	// if (index == -1) {
   144 		if (index == -1){
   171 	// return new String[0];
   145 			return new String[0];
   172 	// }
   146 		}
   173 	//
   147 		
   174 	// String usedString = (index == 0) ? aLine : aLine.substring(index, aLine
   148 		String usedString = (index == 0) ? aLine : aLine.substring(index, aLine.length());
   175 	// .length());
   149 		return usedString.split(" "); //$NON-NLS-1$
   176 	//		return usedString.split(" "); //$NON-NLS-1$
   150 	}
   177 	// }
   151 	
   178 
   152 	/**
   179 	// /**
   153 	 * Parses an ALLOC statement. This statement contains a complete callstack.
   180 	// * Parses an ALC statement. This statement contains a complete callstack.
   154 	 * @param fragments the line fragments of the statement to process
   181 	// *
   155 	 * @return true if statement is complete (this method will always return true)
   182 	// * @param fragments
   156 	 */
   183 	// * the line fragments of the statement to process
   157 	private boolean parseAlloc(String[] fragments) {
   184 	// * @return true if statement is complete (this method will always return
   158 		
   185 	// * true)
   159 		if (fragments.length > 5) {
   186 	// */
   160 			createCallstack(fragments, process, callstack, 6);
   187 	// private boolean parseAlloc(String[] fragments) {
   161 		}
   188 	//
   162 		
   189 	// if (fragments.length > 5) {
   163 		complete = true;
   190 	// createCallstack(fragments, process, callstack, 6);
   164 		return true; //we are done; there are no fragments for this alloc
   191 	// }
   165 	}
   192 	//
   166 	
   193 	// complete = true;
   167 	/**
   194 	// return true; // we are done; there are no fragments for this alloc
   168 	 * Parses an ALLOCH or FREEH statement
   195 	// }
   169 	 * @param fragments the line fragments of the statement to process
   196 
   170 	 * @param startIndex index at which the callstack size is to be found
   197 	/**
   171 	 * @return true if callstack is complete, false if callstack fragment is expected
   198 	 * Parses an ALH or FRH statement
       
   199 	 * 
       
   200 	 * @param fragments
       
   201 	 *            the line fragments of the statement to process
       
   202 	 * @param startIndex
       
   203 	 *            index at which the callstack size is to be found
       
   204 	 * @return true if callstack is complete, false if callstack fragment is
       
   205 	 *         expected
   172 	 */
   206 	 */
   173 	private boolean parseHeader(String[] fragments, int startIndex) {
   207 	private boolean parseHeader(String[] fragments, int startIndex) {
   174 		
   208 
   175 		if (callstack.size() > 0 || remainingSize > 0) {
   209 		if (callstack.size() > 0 || remainingSize > 0) {
   176 			throw new IllegalStateException(
   210 			throw new IllegalStateException(
   177 					"Callstack list should still be empty when starting to process ALLOCH"); //$NON-NLS-1$
   211 					"Callstack list should still be empty when starting to process ALH or FRH"); //$NON-NLS-1$
   178 		}
   212 		}
   179 
   213 
   180 		if (fragments.length > startIndex) {
   214 		if (fragments.length > startIndex) {
   181 
   215 
   182 			int callstackSize = Integer.parseInt(fragments[startIndex], 16);
   216 			int callstackSize = Integer.parseInt(fragments[startIndex], 16);
   183 			startIndex ++;
   217 			startIndex++;
   184 
   218 
   185 			createCallstack(fragments, process, callstack, startIndex);
   219 			createCallstack(fragments, process, callstack, startIndex);
   186 
   220 
   187 			callstackSize -= callstack.size();
   221 			callstackSize -= callstack.size();
   188 			if (callstackSize > 0) {
   222 			if (callstackSize > 0) {
   190 				remainingSize = callstackSize;
   224 				remainingSize = callstackSize;
   191 			} else {
   225 			} else {
   192 				complete = true;
   226 				complete = true;
   193 			}
   227 			}
   194 		} else {
   228 		} else {
   195 			complete = true; // this header doesn't have callstacks - a bit strange
   229 			complete = true; // this header doesn't have callstacks - a bit
       
   230 			// strange
   196 		}
   231 		}
   197 		return complete;
   232 		return complete;
   198 	}
   233 	}
   199 	
   234 
   200 	/**
   235 	/**
   201 	 * Parses an ALLOCF or FREEF callstack fragment
   236 	 * Parses an ALF or FRF callstack fragment
   202 	 * @param fragments the line fragments of the statement to process
   237 	 * 
       
   238 	 * @param fragments
       
   239 	 *            the line fragments of the statement to process
   203 	 * @return true if callstack is now complete; false otherwise
   240 	 * @return true if callstack is now complete; false otherwise
   204 	 */
   241 	 */
   205 	private boolean parseFragment(String[] fragments) {
   242 	private boolean parseFragment(String[] fragments, long logTime) {
   206 		long time = Long.parseLong(fragments[4],16);
   243 		long time = process.getStartTime() + (logTime - process.getLogTime());
   207 		int packetNumber = Integer.parseInt(fragments[5], 16);
   244 		int packetNumber = Integer.parseInt(fragments[4], 16);
   208 	
   245 
   209 		if (baseInfo.getTime() == time) {
   246 		if (baseInfo.getTime() == time) {
   210 			List<AllocCallstack> tmpCallstack = new ArrayList<AllocCallstack>();
   247 			List<AllocCallstack> tmpCallstack = new ArrayList<AllocCallstack>();
   211 			createCallstack(fragments, process, tmpCallstack, 6);
   248 			createCallstack(fragments, process, tmpCallstack, 5);
   212 			updateFragment(tmpCallstack, packetNumber);
   249 			updateFragment(tmpCallstack, packetNumber);
   213 			
   250 
   214 			remainingSize -= tmpCallstack.size();
   251 			remainingSize -= tmpCallstack.size();
   215 			if (remainingSize <= 0){
   252 			if (remainingSize <= 0) {
   216 				complete = true;
   253 				complete = true;
   217 				finaliseCallstack();
   254 				finaliseCallstack();
   218 			}
   255 			}
   219 		}
   256 		}
   220 		return complete;
   257 		return complete;
   221 	}
   258 	}
   222 	
   259 
   223 	private DllLoad getDllForAddress(ProcessInfo p, Long memoryAddress,
   260 	private DllLoad getDllForAddress(ProcessInfo p, Long memoryAddress,
   224 			long time) {
   261 			long time) {
   225 		for (DllLoad oneLoad : p.getDllLoads()) {
   262 		for (DllLoad oneLoad : p.getDllLoads()) {
   226 			if (memoryAddress >= oneLoad.getStartAddress()
   263 			if (memoryAddress >= oneLoad.getStartAddress()
   227 					&& memoryAddress <= oneLoad.getEndAddress()
   264 					&& memoryAddress <= oneLoad.getEndAddress()
   231 				return oneLoad;
   268 				return oneLoad;
   232 			}
   269 			}
   233 		}
   270 		}
   234 		return null;
   271 		return null;
   235 	}
   272 	}
   236 	private void createCallstack(String[] fragments, ProcessInfo p, List<AllocCallstack> callstack, int startIndex) {
   273 
       
   274 	private void createCallstack(String[] fragments, ProcessInfo p,
       
   275 			List<AllocCallstack> callstack, int startIndex) {
   237 		for (int i = startIndex; i < fragments.length; i++) {
   276 		for (int i = startIndex; i < fragments.length; i++) {
   238 			AllocCallstack callstackElem = new AllocCallstack(fragments[i]);
   277 			AllocCallstack callstackElem = new AllocCallstack(fragments[i]);
   239 
   278 
   240 			// find matching dll
   279 			// find matching dll
   241 			DllLoad dllLoad = getDllForAddress(process, callstackElem.getMemoryAddress(), baseInfo.getTime());
   280 			DllLoad dllLoad = getDllForAddress(process, callstackElem
       
   281 					.getMemoryAddress(), baseInfo.getTime());
   242 			if (dllLoad != null) {
   282 			if (dllLoad != null) {
   243 				callstackElem.setDllLoad(dllLoad);
   283 				callstackElem.setDllLoad(dllLoad);
   244 			}
   284 			}
   245 			callstack.add(callstackElem);
   285 			callstack.add(callstackElem);
   246 		}
   286 		}
   247 	}
   287 	}
       
   288 
   248 	/**
   289 	/**
   249 	 * Updates allocation fragment. Means that given callstack is addition to
   290 	 * Updates allocation fragment. Means that given callstack is addition to
   250 	 * previous added alloc
   291 	 * previous added alloc
   251 	 *
   292 	 * 
   252 	 * @param callstack
   293 	 * @param callstack
   253 	 *            Addition tmpcallstack items
   294 	 *            Addition tmpcallstack items
   254 	 * @param packetNumber
   295 	 * @param packetNumber
   255 	 *            ordinal of callstack fragment
   296 	 *            ordinal of callstack fragment
   256 	 */
   297 	 */
   257 	private void updateFragment(List<AllocCallstack> tmpcallstack,
   298 	private void updateFragment(List<AllocCallstack> tmpcallstack,
   258 			int packetNumber) {
   299 			int packetNumber) {
   259 		if (packetNumber == 1){
   300 		if (packetNumber == 1) {
   260 			//special case; this can be added to the end of the list straight away
   301 			// special case; this can be added to the end of the list straight
       
   302 			// away
   261 			callstack.addAll(tmpcallstack);
   303 			callstack.addAll(tmpcallstack);
   262 		} else {
   304 		} else {
   263 			//packages may come out of order; this is managed in the callstackCache
   305 			// packages may come out of order; this is managed in the
   264 			if (callstackCache == null){
   306 			// callstackCache
   265 				callstackCache = new HashMap<Integer, List<AllocCallstack>>();			
   307 			if (callstackCache == null) {
   266 			}
   308 				callstackCache = new HashMap<Integer, List<AllocCallstack>>();
   267 			callstackCache.put(packetNumber, tmpcallstack);			
   309 			}
   268 		}
   310 			callstackCache.put(packetNumber, tmpcallstack);
   269 	}
   311 		}
   270 	/**
   312 	}
   271 	 * Optimises internal callstack data structures.
   313 
   272 	 * Should only be called after all data for this memory operation
   314 	/**
   273 	 * has been loaded (i.e. all fragments)
   315 	 * Optimises internal callstack data structures. Should only be called after
   274 	 */
   316 	 * all data for this memory operation has been loaded (i.e. all fragments)
   275 	public void finaliseCallstack(){
   317 	 */
   276 		if (callstackCache == null){
   318 	public void finaliseCallstack() {
   277 			//nothing to do
   319 		if (callstackCache == null) {
       
   320 			// nothing to do
   278 			return;
   321 			return;
   279 		}
   322 		}
   280 		
   323 
   281 		if (!complete){
   324 		if (!complete) {
   282 			throw new IllegalStateException("callstack processing is not yet complete."); //$NON-NLS-1$
   325 			throw new IllegalStateException(
   283 		}
   326 					"callstack processing is not yet complete."); //$NON-NLS-1$
   284 			
   327 		}
   285 		if (callstack == null && callstackCache != null){
   328 
   286 			throw new IllegalStateException(); //first set of callstacks should always be in callstacks
   329 		if (callstack == null && callstackCache != null) {
   287 		}
   330 			throw new IllegalStateException(); // first set of callstacks should
   288 		
   331 			// always be in callstacks
   289 		if (callstackCache != null){
   332 		}
       
   333 
       
   334 		if (callstackCache != null) {
   290 			int size = callstackCache.size();
   335 			int size = callstackCache.size();
   291 			int i = 2;
   336 			int i = 2;
   292 			while(size != 0){
   337 			while (size != 0) {
   293 				List<AllocCallstack> nextCallStacks = callstackCache.get(i);
   338 				List<AllocCallstack> nextCallStacks = callstackCache.get(i);
   294 				if (nextCallStacks != null){
   339 				if (nextCallStacks != null) {
   295 					size --;
   340 					size--;
   296 					callstack.addAll(nextCallStacks);
   341 					callstack.addAll(nextCallStacks);
   297 				} //TODO else: missing callstack: shall we report it or log it?
   342 				} // TODO else: missing callstack: shall we report it or log it?
   298 				i++;
   343 				i++;
   299 			}
   344 			}
   300 			callstackCache = null;
   345 			callstackCache = null;
   301 		}
   346 		}
   302 	}
   347 	}
   303 	
   348 
   304 	/**
   349 	/**
   305 	 * @return the completed callstack.
   350 	 * @return the completed callstack.
   306 	 */
   351 	 */
   307 	public List<AllocCallstack> getCallstack(){
   352 	public List<AllocCallstack> getCallstack() {
   308 		if (!complete){
   353 		if (!complete) {
   309 			throw new IllegalStateException("Callstack has not been completely processed."); //$NON-NLS-1$
   354 			throw new IllegalStateException(
   310 		}
   355 					"Callstack has not been completely processed."); //$NON-NLS-1$
   311 		
   356 		}
       
   357 
   312 		finaliseCallstack();
   358 		finaliseCallstack();
   313 		return callstack;
   359 		return callstack;
   314 	}
   360 	}
   315 	
   361 
   316 	/**
   362 	/**
   317 	 * Forces the callstack state to be set to complete. This should only be used when the end of file is encountered.
   363 	 * Forces the callstack state to be set to complete. This should only be
   318 	 */
   364 	 * used when the end of file is encountered.
   319 	public void forceComplete(){
   365 	 */
       
   366 	public void forceComplete() {
   320 		complete = true;
   367 		complete = true;
   321 		finaliseCallstack();
   368 		finaliseCallstack();
   322 	}
   369 	}
   323 	
       
   324 }
   370 }