diff -r a151135b0cf9 -r aa2539c91954 tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/TraceLocationConverter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tracesrv/tracecompiler/src/com.nokia.tracecompiler/src/com/nokia/tracecompiler/engine/TraceLocationConverter.java Fri Oct 08 14:56:39 2010 +0300 @@ -0,0 +1,679 @@ +/* + * Copyright (c) 2008-2010 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: + * + * Location converter monitors locations and converts them to traces if necessary. + * + */ +package com.nokia.tracecompiler.engine; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +import com.nokia.tracecompiler.TraceCompilerConstants; +import com.nokia.tracecompiler.TraceCompilerLogger; +import com.nokia.tracecompiler.engine.TraceCompilerEngineErrorCodes.TraceCompilerErrorCode; +import com.nokia.tracecompiler.engine.project.ProjectConstants; +import com.nokia.tracecompiler.engine.project.ProjectEngine; +import com.nokia.tracecompiler.engine.project.SortedProperties; +import com.nokia.tracecompiler.engine.project.TraceIDCache; +import com.nokia.tracecompiler.engine.rules.PerformanceEventRuleBase; +import com.nokia.tracecompiler.engine.rules.StateTraceRule; +import com.nokia.tracecompiler.engine.source.SourceParserRule; +import com.nokia.tracecompiler.engine.source.SourceProperties; +import com.nokia.tracecompiler.engine.source.SourceParserRule.ParameterConversionResult; +import com.nokia.tracecompiler.engine.source.SourceParserRule.TraceConversionResult; +import com.nokia.tracecompiler.model.Trace; +import com.nokia.tracecompiler.model.TraceCompilerException; +import com.nokia.tracecompiler.model.TraceGroup; +import com.nokia.tracecompiler.model.TraceModel; +import com.nokia.tracecompiler.model.TraceModelExtension; +import com.nokia.tracecompiler.model.TraceModelPersistentExtension; +import com.nokia.tracecompiler.model.TraceParameter; +import com.nokia.tracecompiler.project.FormattingUtils; +import com.nokia.tracecompiler.project.GroupNames; +import com.nokia.tracecompiler.project.ProjectUtils; +import com.nokia.tracecompiler.source.SourceConstants; + +/** + * Location converter monitors locations and converts them to traces if + * necessary. + * + */ +public final class TraceLocationConverter { + + /** + * Number of deprecated groups + */ + final static int NUMBER_OF_DEPRECATED_GROUPS = 3; + + /** + * Deprecated group name map + */ + Map deprecatedGroupNameMap = new HashMap( + NUMBER_OF_DEPRECATED_GROUPS); + + /** + * Trace model + */ + private TraceModel model; + + /** + * Constructor + * + * @param model + * the trace model + */ + TraceLocationConverter(TraceModel model) { + this.model = model; + initializeDeprecatedGroupNameMap(); + } + + /** + * Initialize deprecated group name map + */ + private void initializeDeprecatedGroupNameMap() { + deprecatedGroupNameMap.put(GroupNames.TRACE_API, + GroupNames.TRACE_BORDER); + deprecatedGroupNameMap.put(GroupNames.TRACE_DEBUG, + GroupNames.TRACE_DUMP); + deprecatedGroupNameMap.put(GroupNames.TRACE_DETAILED, + GroupNames.TRACE_INTERNALS); + } + + /** + * Source opened notification + * + * @param properties + * the source properties + * @throws TraceCompilerException + */ + void sourceOpened(SourceProperties properties) throws TraceCompilerException { + //make sure all errors are reported AFTER processing a file + TraceCompilerEngineGlobals.getSourceContextManager() + .setConverting(true); + model.startProcessing(); + try { + // Load fixed group and trace ids from definition file to model + //no errors are reported here + if (model.getFixedIds() == null) { + loadFixedIdsFromDefinitionFileToModel(); + } + + for (TraceLocation loc : properties) { + autoConvertLocation(loc); + } + // If there are duplicates or unused traces, they are removed + // Note that this will work across source files although this + // function is processing only one file. + // If a trace is created, all locations from all open source files + // are linked to that trace and thus it will be removed as + // duplicate. + removeDuplicateTraces(); + + } finally { + model.processingComplete(); + SourceContextManager manager = TraceCompilerEngineGlobals + .getSourceContextManager(); + manager.setConverting(false); + manager.setContext(null); + } + } + + /** + * Load fixed Ids from definiton file to properties + * + */ + private void loadFixedIdsFromDefinitionFileToModel() { + TraceIDCache cache = model.getExtension(TraceIDCache.class); + + // Create trace Id cache if it does not exist + try { + if (cache == null) { + String path; + path = ProjectUtils.getLocationForFile(model, + ProjectEngine.traceFolderName, + ProjectConstants.FIXED_ID_DEFINITIONS_FILE_NAME, false); + if (path != null) { + cache = new TraceIDCache(new File(path).getParent()); + model.addExtension(cache); + } + } + if (cache != null) { + File cacheFile = new File(cache.getAbsolutePath()); + // Try to load Ids from trace Id cache file + try { + SortedProperties fixedIds = new SortedProperties(); + if (cacheFile.exists()) { + String thisLine; + boolean allOldFixedIdsAreValid = true; + String group = model.GROUP_PROPERTY_PREFIX; + FileInputStream fis = new FileInputStream(cacheFile); + BufferedReader myInput = new BufferedReader( + new InputStreamReader(fis)); + // Read the old fixed id definition file + while ((thisLine = myInput.readLine()) != null) { + if (thisLine.indexOf(group) != -1) { + thisLine = thisLine.substring(group.length(), + thisLine.length()); + if (!searchForOldGroupIds(thisLine)) { + String msg = Messages.getString("TraceLocationConverter.GroupIdValueError"); //$NON-NLS-1$ + TraceCompilerEngineGlobals.getEvents().postWarningMessage(msg, null); + // We need only one old invalid id to make all of them invalid + allOldFixedIdsAreValid = false; + break; + } + } + } + fis.close(); + + if (allOldFixedIdsAreValid) { + // Create file input stream again + FileInputStream newFis = new FileInputStream(cacheFile); + fixedIds.load(newFis); + // Load fixed Ids from properties to model + model.setFixedIds(fixedIds); + newFis.close(); + } + + } + } catch (IOException e) { + // If there is a problem with the cache file, just delete it + // make sure the model fixed ids is null + model.setFixedIds(null); + cacheFile.delete(); + // We do not raise an exception but we need to report a info + TraceCompilerLogger.printWarning(Messages.getString("TraceLocationConverter.FixedIdProblemWarningBeginText") + cacheFile.toString() + Messages.getString("TraceLocationConverter.FixedIdProblemWarningMiddleText") + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } catch (TraceCompilerException e) { + // This exception can probably be ignored + TraceCompilerLogger.printWarning("Could not create ID cache : " + e.getMessage()); //$NON-NLS-1$ + } + } + + /** + * Searches the fixed_id file to see if there are any old definitions + * + * @param string + * of text from the fixed_id files + * @return boolean + */ + public static boolean searchForOldGroupIds(String str) { + + boolean oldGroupIdValid = true; + + //get group name from the string + int index = str.indexOf(SourceConstants.ASSIGN_WITH_OUT_SPACES); + if (index == -1) { + //not valid string, so ignore it, just return success + return true; + } + + String groupName = str.substring(0, index); + + //get group id + str = str.substring(str.indexOf(SourceConstants.HEX_PREFIX) + 2, str + .length()); + + int groupId = -1; + + try { + groupId = Integer.parseInt(str.trim(), + TraceCompilerConstants.HEX_RADIX); + } catch (NumberFormatException e) { + //it's not an hex number so fail + oldGroupIdValid = false; + } + + //get group id from ost header file + int fileGroupId = GroupNames.getIdByName(groupName); + + if (fileGroupId != 0) { + //found 1 + if (fileGroupId != groupId) //group id has changed, so old one can't be used + oldGroupIdValid = false; + } else { + if ((GroupNames.USER_GROUP_ID_FIRST > groupId) + || (groupId > GroupNames.USER_GROUP_ID_LAST)) //not within new user defined Ids + oldGroupIdValid = false; + } + + return oldGroupIdValid; + } + + /** + * Converts the given location to trace if parser supports auto-conversion + * + * @param location + * the location + */ + private void autoConvertLocation(TraceLocation location) { + // Stores the context of the location to the context manager. + TraceCompilerEngineGlobals.getSourceContextManager().setContext( + location.getParser().getContext(location.getOffset())); + Trace trace = location.getTrace(); + if (trace == null) { + // If the trace does not exist, the parser determines if the + // location can be converted + if (location.getParserRule().getLocationParser() + .isLocationConverted(location)) { + try { + convertLocation(location, null, true); + } catch (TraceCompilerException e) { + // If converter fails, the error code is stored into the + // location. The location notifies all validity listeners + // about the change + location.setConverterErrorCode((TraceCompilerErrorCode) e + .getErrorCode(), e.getErrorParameters()); + } + } + } else { + // If the trace already exists in the model, it is updated + // based on the source file contents + updateLocation(location); + } + } + + /** + * Recreates the trace from changed location when source is saved + * + * @param location + * the location to be checked + */ + private void updateLocation(TraceLocation location) { + // Parser determines if the location can be converted + if (location.getParserRule().getLocationParser().isLocationConverted( + location)) { + try { + Trace trace = location.getTrace(); + // If a location has changed, the old trace is removed + // and a new one created. Persistent extensions are moved to the + // new trace + Iterator extensions = null; + if (trace != null) { + extensions = trace + .getExtensions(TraceModelPersistentExtension.class); + trace.getGroup().removeTrace(trace); + } + convertLocation(location, extensions, true); + + // Check that the location is inside a function. Otherwise throw + // an error because the code is unreachable + if (location.getFunctionName() == null) { + throw new TraceCompilerException( + TraceCompilerErrorCode.UNREACHABLE_TRACE_LOCATION); + } + + } catch (TraceCompilerException e) { + // If converter fails, the error code is stored into the + // location. The location notifies all validity listeners about + // the change + location.setConverterErrorCode((TraceCompilerErrorCode) e + .getErrorCode(), e.getErrorParameters()); + } + } + } + + /** + * Removes all duplicate traces from the model + * @throws TraceCompilerException + */ + private void removeDuplicateTraces() throws TraceCompilerException { + boolean groupRemoved = true; + while (groupRemoved) { + groupRemoved = false; + for (TraceGroup group : model) { + removeDuplicateTracesFromGroup(group); + if (!group.hasTraces()) { + model.removeGroup(group); + groupRemoved = true; + break; + } + } + } + } + + /** + * Removes duplicate traces from a trace group + * + * @param group + * the group + * @throws TraceCompilerException + */ + private void removeDuplicateTracesFromGroup(TraceGroup group) throws TraceCompilerException { + boolean traceRemoved = true; + while (traceRemoved) { + traceRemoved = false; + for (Trace trace : group) { + TraceLocationList list = trace + .getExtension(TraceLocationList.class); + if (list != null) { + if (list.getLocationCount() > 1) { + // All the locations are marked as duplicates and the + // trace is deleted + TraceCompilerErrorCode code = TraceCompilerErrorCode.TRACE_HAS_MULTIPLE_LOCATIONS; + for (LocationProperties loc : list) { + ((TraceLocation) loc).setConverterErrorCode(code, + null); + } + group.removeTrace(trace); + traceRemoved = true; + break; + } + } + } + } + } + + /** + * Converts a location to a Trace object. + * + * @param location + * the location to be converted + * @param extensions + * persistent extensions to be added to the new trace + * @param autoConvert + * true if converting without user interaction + * @return the new trace + * @throws TraceCompilerException + * if conversion fails + */ + private Trace convertLocation(TraceLocation location, + Iterator extensions, + boolean autoConvert) throws TraceCompilerException { + Trace trace = null; + // If the parser has failed, the validity code is not OK and the + // location cannot be converted. Traces marked with no-trace error code + // have not yet been converted, so that is OK. Traces that have + // duplicate ID's error code can be parsed, since the duplicates might + // no longer exist. + if (!autoConvert + || location.getValidityCode() == TraceCompilerErrorCode.OK + || location.getValidityCode() == TraceCompilerErrorCode.TRACE_DOES_NOT_EXIST + || location.getValidityCode() == TraceCompilerErrorCode.TRACE_HAS_MULTIPLE_LOCATIONS) { + // The parser does the actual conversion + SourceParserRule rule = location.getParserRule(); + TraceConversionResult result = rule.getLocationParser() + .convertLocation(location); + // After parser has finished, the trace is created. + trace = convertWithoutUI(result, extensions); + + if (trace != null) { + model.startProcessing(); + try { + createParametersFromConversionResult(location, result, + trace); + // Runs a location validity check and notifies listeners + // that location is now OK + location.setConverterErrorCode(TraceCompilerErrorCode.OK, + null); + } catch (TraceCompilerException e) { + // If parameters cannot be created, the trace is removed + TraceGroup group = trace.getGroup(); + trace.getGroup().removeTrace(trace); + if (!group.hasTraces()) { + group.getModel().removeGroup(group); + } + throw e; + } finally { + model.processingComplete(); + } + } + } + return trace; + } + + /** + * Converts a location to trace without UI + * + * @param result + * the conversion result from parser + * @param extensions + * persistent extensions to be added to the new trace + * @return the converted trace + * @throws TraceCompilerException + * if location properties are not valid + */ + private Trace convertWithoutUI(TraceConversionResult result, + Iterator extensions) + throws TraceCompilerException { + Trace trace = null; + if (result.group != null) { + String groupName = result.group; + TraceGroup group = handleGroup(groupName); + trace = handleTrace(result, extensions, group); + } else { + throw new TraceCompilerException( + TraceCompilerErrorCode.GROUP_NOT_SELECTED); + } + return trace; + } + + /** + * Handle trace + * + * @param result + * the conversion result from parser + * @param extensions + * persistent extensions to be added to the new trace + * @param group + * the group where trace belongs to + * @return the trace + * @throws TraceCompilerException + */ + private Trace handleTrace(TraceConversionResult result, + Iterator extensions, TraceGroup group) + throws TraceCompilerException { + // First try to find Id to trace from fixed Ids + Trace trace = null; + Properties fixedIds = model.getFixedIds(); + String groupName = result.group; + String traceName = result.name; + int groupId = group.getID(); + String groupIdString = SourceConstants.HEX_PREFIX + + Integer.toString(groupId, model.HEX_RADIX).toUpperCase(); + int traceId = 0; + if (fixedIds != null) { + String tracePropertyName = groupName + model.GROUP_ID_PREFIX + + groupIdString + model.GROUP_ID_SUFFIX + + SourceConstants.UNDERSCORE + traceName; + String value = fixedIds.getProperty(model.TRACE_PROPERTY_PREFIX + + tracePropertyName); + if (value != null) { + try { + traceId = Integer.decode(value).intValue(); + } catch (NumberFormatException e) { + // Corrupted, assign a proper Id later on + traceId = 0; + } + } + } + // If there was no fixed Id to this trace, get Id from model + if (traceId == 0) { + traceId = model.getNextTraceId(group); + } + + String text = result.text; + model.getVerifier().checkTraceProperties(group, null, traceId, + traceName, text); + TraceModelExtension[] extArray = createExtensionArray(result, + extensions); + trace = model.getFactory().createTrace(group, traceId, traceName, text, + extArray); + + if ((trace.getExtension(StateTraceRule.class) == null) + && (group.getName() + .equals("TRACE_STATE"))) { //$NON-NLS-1$ + throw new TraceCompilerException( + TraceCompilerErrorCode.INVALID_USAGE_OF_TRACE_STATE_GROUP_NAME); + } else if ((trace.getExtension(PerformanceEventRuleBase.class) == null) + && (group.getName() + .equals("TRACE_PERFORMANCE"))) { //$NON-NLS-1$ + throw new TraceCompilerException( + TraceCompilerErrorCode.INVALID_USAGE_OF_TRACE_PERFORMACE_GROUP_NAME); + } + + return trace; + } + + /** + * Handle group. Try to fnd group from model. If it does not exist then + * create new group. + * + * @param groupName + * the name of the group + * @return the handled group + * @throws TraceCompilerException + */ + private TraceGroup handleGroup(String groupName) + throws TraceCompilerException { + String deprecatedGroupName = null; + + // Convert deprecated group name to valid group name if needed + if (deprecatedGroupNameMap.containsKey(groupName)) { + deprecatedGroupName = groupName; + groupName = deprecatedGroupNameMap.get(groupName); + } + + // If auto-convert flag is set, the location is converted without + // user interaction. A new trace group is created if not found + TraceGroup group = model.findGroupByName(groupName); + if (group == null) { + + // Print warning related to deprecated group name change if needed + if (deprecatedGroupName != null) { + String message = Messages + .getString("TraceLocationConverter.DeprecatedGroupIdWarningStart");//$NON-NLS-1$ + message += SourceConstants.SPACE + deprecatedGroupName + + SourceConstants.SPACE; + message += Messages + .getString("TraceLocationConverter.DeprecatedGroupIdWarningMiddle");//$NON-NLS-1$ + message += SourceConstants.SPACE + groupName + + SourceConstants.SPACE; + message += Messages + .getString("TraceLocationConverter.DeprecatedGroupIdWarningEnd");//$NON-NLS-1$ + TraceCompilerLogger.printWarning(message); + } + + int groupId = 0; + Properties fixedIds = model.getFixedIds(); + + // First try to find Id to group from fixed Ids + if (fixedIds != null) { + String value = fixedIds.getProperty(model.GROUP_PROPERTY_PREFIX + + groupName); + if (value != null) { + try { + groupId = Integer.decode(value).intValue(); + } catch (NumberFormatException e) { + // Corrupted, assign a proper Id later on + groupId = 0; + } + } + } + // If there was no fixed Id to this group, get Id from model + if (groupId == 0) { + groupId = FormattingUtils.getGroupID(model, groupName); + } + model.getVerifier().checkTraceGroupProperties(model, null, groupId, + groupName); + group = model.getFactory().createTraceGroup(groupId, groupName, + null); + } + + return group; + } + + /** + * Combines extensions into one array + * + * @param result + * the conversion result + * @param extensions + * the persistent extensions from old trace + * @return the combined array of extensions + */ + private TraceModelExtension[] createExtensionArray( + TraceConversionResult result, + Iterator extensions) { + TraceModelExtension[] extArray = null; + ArrayList ext = null; + if (result.extensions != null) { + ext = new ArrayList(); + ext.addAll(result.extensions); + } + if (extensions != null) { + if (ext == null) { + ext = new ArrayList(); + } + while (extensions.hasNext()) { + ext.add(extensions.next()); + } + } + if (ext != null) { + extArray = new TraceModelExtension[ext.size()]; + ext.toArray(extArray); + } + return extArray; + } + + /** + * Creates the trace parameters based on trace conversion result + * + * @param converted + * the location that was converted + * @param result + * the conversion result + * @param trace + * the trace + * @throws TraceCompilerException + * if parameters cannot be created + */ + private void createParametersFromConversionResult(TraceLocation converted, + TraceConversionResult result, Trace trace) + throws TraceCompilerException { + if (result.parameters != null) { + for (int i = 0; i < result.parameters.size(); i++) { + int id = trace.getNextParameterID(); + ParameterConversionResult res = result.parameters.get(i); + boolean warning = false; + if (res.type == null) { + warning = true; + res.type = TraceParameter.HEX32; + } + model.getVerifier().checkTraceParameterProperties(trace, null, + id, res.name, res.type); + TraceModelExtension[] extArray = null; + if (res.extensions != null) { + extArray = new TraceModelExtension[res.extensions.size()]; + res.extensions.toArray(extArray); + } + TraceParameter param = model.getFactory().createTraceParameter( + trace, id, res.name, res.type, extArray); + if (warning) { + String msg = Messages + .getString("TraceCompiler.UnknownTypeWarning"); //$NON-NLS-1$ + TraceCompilerEngineGlobals.getEvents().postWarningMessage( + msg, param); + } + } + } + } + +}