Added tracing support to Protobuf Editor. Change-Id: Ie4be4fb412b3bf7e9d77d74beecc91a255286960
diff --git a/com.google.eclipse.protobuf.ui/.options b/com.google.eclipse.protobuf.ui/.options new file mode 100644 index 0000000..e05b026 --- /dev/null +++ b/com.google.eclipse.protobuf.ui/.options
@@ -0,0 +1,7 @@ +# Debugging options for the com.google.eclipse.protobuf plugin. + +# Turns on debugging for the com.google.eclipse.protobuf plugin. +com.google.eclipse.protobuf.ui/debug=false + +# Turns on debugging for com.google.eclipse.protobuf.scoping. +com.google.eclipse.protobuf.ui/scoping=false
diff --git a/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF b/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF index 09ab499..096c09d 100644 --- a/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF +++ b/com.google.eclipse.protobuf.ui/META-INF/MANIFEST.MF
@@ -3,7 +3,7 @@ Bundle-Name: %Bundle-Name Bundle-Vendor: %Bundle-Vendor Bundle-Version: 2.2.1.qualifier -Bundle-Activator: com.google.eclipse.protobuf.ui.internal.ProtobufActivator +Bundle-Activator: com.google.eclipse.protobuf.ui.internal.Activator Bundle-SymbolicName: com.google.eclipse.protobuf.ui; singleton:=true Bundle-ActivationPolicy: lazy Require-Bundle: com.google.eclipse.protobuf,
diff --git a/com.google.eclipse.protobuf.ui/plugin.xml b/com.google.eclipse.protobuf.ui/plugin.xml index 3570fe6..4f55fd7 100644 --- a/com.google.eclipse.protobuf.ui/plugin.xml +++ b/com.google.eclipse.protobuf.ui/plugin.xml
@@ -278,4 +278,15 @@ <extension point="org.eclipse.xtext.ui.shared.overridingGuiceModule"> <module class="com.google.eclipse.protobuf.ui.SharedModuleOverrides"/> </extension> + <extension + point="org.eclipse.ui.trace.traceComponents"> + <component + id="com.google.eclipse.protobuf.ui.trace" + label="%trace.component.label"> + <bundle + consumed="false" + name="com.google.eclipse.protobuf.ui"> + </bundle> + </component> + </extension> </plugin>
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/internal/Activator.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/internal/Activator.java new file mode 100644 index 0000000..21da6ee --- /dev/null +++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/internal/Activator.java
@@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Google Inc. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.google.eclipse.protobuf.ui.internal; + +import java.util.Hashtable; + +import org.eclipse.osgi.service.debug.DebugOptions; +import org.eclipse.osgi.service.debug.DebugOptionsListener; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import com.google.eclipse.protobuf.util.Tracer; + +/** + * The activator class controls the plug-in life cycle. + * + * @author atrookey@google.com (Alexander Rookey) + */ +public class Activator extends ProtobufActivator { + private ServiceRegistration<DebugOptionsListener> debugOptionsListener; + + @Override + public void start(BundleContext bundleContext) throws Exception { + Hashtable<String, String> props = new Hashtable<>(4); + props.put(DebugOptions.LISTENER_SYMBOLICNAME, "com.google.eclipse.protobuf.ui"); + debugOptionsListener = + bundleContext.registerService( + DebugOptionsListener.class, Tracer.RESOURCES_DEBUG_OPTIONS_LISTENER, props); + super.start(bundleContext); + } + + @Override + public void stop(BundleContext bundleContext) throws Exception { + try { + debugOptionsListener.unregister(); + } finally { + super.stop(bundleContext); + } + } +}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java index e9d8556..3b82c45 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
@@ -1,14 +1,16 @@ /* - * Copyright (c) 2011 Google Inc. + * Copyright (c) 2016 Google Inc. * - * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse - * Public License v1.0 which accompanies this distribution, and is available at - * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package com.google.eclipse.protobuf.scoping; import static com.google.eclipse.protobuf.scoping.OptionType.typeOf; +import static com.google.eclipse.protobuf.util.Tracer.DEBUG_SCOPING; +import static com.google.eclipse.protobuf.validation.ProtobufResourceValidator.getScopeProviderTimingCollector; import static java.util.Collections.emptySet; import com.google.eclipse.protobuf.model.util.MessageFields; @@ -62,9 +64,11 @@ * * @author alruiz@google.com (Alex Ruiz) * - * @see <a href="http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping">Xtext Scoping</a> + * @see <a href="http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping">Xtext + * Scoping</a> */ -public class ProtobufScopeProvider extends AbstractDeclarativeScopeProvider implements ScopeProvider { +public class ProtobufScopeProvider extends AbstractDeclarativeScopeProvider + implements ScopeProvider { private static final boolean DO_NOT_IGNORE_CASE = false; @Inject private ComplexTypeFinderStrategy complexTypeFinderDelegate; @@ -83,6 +87,18 @@ @Inject private NativeOptionDescriptions nativeOptionDescriptions; @Inject private Options options; + @Override + public IScope getScope(EObject context, EReference reference) { + if (DEBUG_SCOPING) { + getScopeProviderTimingCollector().startTimer(); + } + IScope scope = super.getScope(context, reference); + if (DEBUG_SCOPING) { + getScopeProviderTimingCollector().stopTimer(); + } + return scope; + } + @SuppressWarnings("unused") public IScope scope_ComplexTypeLink_target(ComplexTypeLink link, EReference r) { EObject c = link.eContainer(); @@ -100,7 +116,8 @@ return createEmptyScope(); } - @Override public Collection<IEObjectDescription> potentialComplexTypesFor(MessageField field) { + @Override + public Collection<IEObjectDescription> potentialComplexTypesFor(MessageField field) { return modelElementFinder.find(field, complexTypeFinderDelegate, ComplexType.class); } @@ -112,7 +129,8 @@ return createScope(extensibleTypes); } - @Override public Collection<IEObjectDescription> potentialExtensibleTypesFor(TypeExtension extension) { + @Override + public Collection<IEObjectDescription> potentialExtensibleTypesFor(TypeExtension extension) { Protobuf root = modelObjects.rootOf(extension); return modelElementFinder.find(root, complexTypeFinderDelegate, ExtensibleType.class); } @@ -124,12 +142,14 @@ return createScope(messages); } - @Override public Collection<IEObjectDescription> potentialMessagesFor(Rpc rpc) { + @Override + public Collection<IEObjectDescription> potentialMessagesFor(Rpc rpc) { Protobuf root = modelObjects.rootOf(rpc); return allMessages(root); } - @Override public Collection<IEObjectDescription> potentialMessagesFor(Stream stream) { + @Override + public Collection<IEObjectDescription> potentialMessagesFor(Stream stream) { Protobuf root = modelObjects.rootOf(stream); return allMessages(root); } @@ -184,9 +204,10 @@ EObject container = c.eContainer(); if (container instanceof Group) { OptionType optionType = OptionType.findOptionTypeForLevelOf(container.eContainer()); - return createScope(optionType != null - ? modelElementFinder.find(option, customOptionFinderDelegate, optionType) - : Collections.<IEObjectDescription>emptySet()); + return createScope( + optionType != null + ? modelElementFinder.find(option, customOptionFinderDelegate, optionType) + : Collections.<IEObjectDescription>emptySet()); } } @@ -196,7 +217,8 @@ return createScope(descriptions); } - @Override public Collection<IEObjectDescription> potentialSourcesFor(AbstractCustomOption option) { + @Override + public Collection<IEObjectDescription> potentialSourcesFor(AbstractCustomOption option) { OptionType optionType = typeOf((AbstractOption) option); Collection<IEObjectDescription> descriptions = emptySet(); if (optionType != null) { @@ -225,11 +247,13 @@ return emptySet(); } - @Override public Collection<IEObjectDescription> potentialMessageFieldsFor(AbstractCustomOption option) { + @Override + public Collection<IEObjectDescription> potentialMessageFieldsFor(AbstractCustomOption option) { return customOptionFieldFinder.findOptionFields(option, messageFieldFinderDelegate); } - @Override public Collection<IEObjectDescription> potentialExtensionFieldsFor(AbstractCustomOption option) { + @Override + public Collection<IEObjectDescription> potentialExtensionFieldsFor(AbstractCustomOption option) { return customOptionFieldFinder.findOptionFields(option, extensionFieldFinderDelegate); } @@ -260,12 +284,15 @@ return null; } - @Override public Collection<IEObjectDescription> potentialNormalFieldNames(ComplexValue value) { + @Override + public Collection<IEObjectDescription> potentialNormalFieldNames(ComplexValue value) { return customOptionFieldNameFinder.findFieldNamesSources(value, normalFieldNameFinderDelegate); } - @Override public Collection<IEObjectDescription> potentialExtensionFieldNames(ComplexValue value) { - return customOptionFieldNameFinder.findFieldNamesSources(value, extensionFieldNameFinderDelegate); + @Override + public Collection<IEObjectDescription> potentialExtensionFieldNames(ComplexValue value) { + return customOptionFieldNameFinder.findFieldNamesSources( + value, extensionFieldNameFinderDelegate); } private static IScope createEmptyScope() {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/TimingCollector.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/TimingCollector.java new file mode 100644 index 0000000..3f79477 --- /dev/null +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/TimingCollector.java
@@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016 Google Inc. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.google.eclipse.protobuf.util; + +public class TimingCollector { + private long invocationCount; + private long sum; + private long time; + + public double getAverageInMilliseconds() { + return sum * 1.e-6 / invocationCount; + } + + public long getInvocationCount() { + return invocationCount; + } + + public void startTimer() { + time = System.nanoTime(); + } + + public void stopTimer() { + sum += System.nanoTime() - time; + invocationCount++; + } + + public void clear() { + invocationCount = 0; + sum = 0; + } + + @Override + public String toString() { + return String.format( + "Invocation Count: %1$d Mean Duration: %2$fms", + getInvocationCount(), + getAverageInMilliseconds()); + } +}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Tracer.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Tracer.java new file mode 100644 index 0000000..474b1bd --- /dev/null +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Tracer.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Google Inc. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package com.google.eclipse.protobuf.util; + +import org.eclipse.osgi.service.debug.DebugOptions; +import org.eclipse.osgi.service.debug.DebugOptionsListener; +import org.eclipse.osgi.service.debug.DebugTrace; + +/** + * The debugging related arguments for the protobuf editor. + * + * @author atrookey@google.com (Alexander Rookey) + */ +public class Tracer { + public static DebugTrace trace; + public static boolean DEBUG_SCOPING = false; + public static final String TRACE_PREFIX = "[Google Protobuf Editor] "; + + public static final DebugOptionsListener RESOURCES_DEBUG_OPTIONS_LISTENER = + new DebugOptionsListener() { + @Override + public void optionsChanged(DebugOptions options) { + if (trace == null) { + trace = options.newDebugTrace("com.google.eclipse.protobuf.ui"); + } + boolean debug = options.getBooleanOption("com.google.eclipse.protobuf.ui/debug", false); + DEBUG_SCOPING = + debug && options.getBooleanOption("com.google.eclipse.protobuf.ui/scoping", false); + } + }; + + /** + * Prints a trace message to standard output. + */ + public static void trace(String message) { + System.out.println(TRACE_PREFIX + message); + } +}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufResourceValidator.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufResourceValidator.java index a33d0ec..969e2ba 100644 --- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufResourceValidator.java +++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufResourceValidator.java
@@ -1,9 +1,9 @@ /* - * Copyright (c) 2011 Google Inc. + * Copyright (c) 2016 Google Inc. * - * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse - * Public License v1.0 which accompanies this distribution, and is available at - * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package com.google.eclipse.protobuf.validation; @@ -15,6 +15,8 @@ import static org.eclipse.xtext.validation.CheckMode.KEY; import static org.eclipse.xtext.validation.CheckType.FAST; import static org.eclipse.xtext.validation.impl.ConcreteSyntaxEValidator.DISABLE_CONCRETE_SYNTAX_EVALIDATOR; +import static com.google.eclipse.protobuf.util.Tracer.DEBUG_SCOPING; +import static com.google.eclipse.protobuf.util.Tracer.trace; import static com.google.common.collect.Lists.newArrayListWithExpectedSize; import static com.google.common.collect.Maps.newHashMap; @@ -38,6 +40,7 @@ import org.eclipse.xtext.validation.ResourceValidatorImpl; import com.google.eclipse.protobuf.linking.ProtobufDiagnostic; +import com.google.eclipse.protobuf.util.TimingCollector; /** * Adds support for converting scoping errors into warnings if non-proto2 files are imported. @@ -46,14 +49,32 @@ */ public class ProtobufResourceValidator extends ResourceValidatorImpl { private static final Logger log = Logger.getLogger(ProtobufResourceValidator.class); + private static final ThreadLocal<TimingCollector> scopeProviderTimingCollector = + new ThreadLocal<TimingCollector>() { + @Override + public TimingCollector initialValue() { + return new TimingCollector(); + } + }; - @Override public List<Issue> validate(Resource resource, CheckMode mode, CancelIndicator indicator) { + @Override + public List<Issue> validate(Resource resource, CheckMode mode, CancelIndicator indicator) { CancelIndicator monitor = indicator == null ? CancelIndicator.NullImpl : indicator; + getScopeProviderTimingCollector().clear(); resolveProxies(resource, monitor); + if (DEBUG_SCOPING) { + trace("Debugging AbstractDeclarativeScopeProvider.getScope() " + + getScopeProviderTimingCollector().toString()); + } + return handleIssues(resource, mode, monitor); + } + + private List<Issue> handleIssues(Resource resource, CheckMode mode, CancelIndicator monitor) { if (monitor.isCanceled()) { return null; } - List<Issue> result = newArrayListWithExpectedSize(resource.getErrors().size() + resource.getWarnings().size()); + List<Issue> result = + newArrayListWithExpectedSize(resource.getErrors().size() + resource.getWarnings().size()); try { IAcceptor<Issue> acceptor = createAcceptor(result); Status status = delegateValidationToDiagnostician(resource, mode, monitor, acceptor); @@ -76,14 +97,15 @@ return result; } - private Status delegateValidationToDiagnostician(Resource resource, CheckMode mode, - CancelIndicator monitor, IAcceptor<Issue> acceptor) { + private Status delegateValidationToDiagnostician( + Resource resource, CheckMode mode, CancelIndicator monitor, IAcceptor<Issue> acceptor) { Status hasNonProto2Import = Status.OK; for (EObject element : resource.getContents()) { if (monitor.isCanceled()) { return Status.CANCELED; } - Diagnostic diagnostic = getDiagnostician().validate(element, validationOptions(resource, mode, monitor)); + Diagnostic diagnostic = + getDiagnostician().validate(element, validationOptions(resource, mode, monitor)); if (convertIssuesToMarkers(acceptor, diagnostic) == Status.PROTO1_IMPORTS_FOUND) { hasNonProto2Import = Status.PROTO1_IMPORTS_FOUND; } @@ -91,7 +113,8 @@ return hasNonProto2Import; } - private Map<Object, Object> validationOptions(Resource resource, CheckMode mode, CancelIndicator monitor) { + private Map<Object, Object> validationOptions( + Resource resource, CheckMode mode, CancelIndicator monitor) { Map<Object, Object> options = newHashMap(); options.put(KEY, mode); options.put(CANCEL_INDICATOR, monitor); @@ -118,7 +141,10 @@ return hasNonProto2Import; } - private Status createErrors(Resource resource, boolean proto1ImportsFound, IAcceptor<Issue> acceptor, + private Status createErrors( + Resource resource, + boolean proto1ImportsFound, + IAcceptor<Issue> acceptor, CancelIndicator monitor) { for (Resource.Diagnostic error : resource.getErrors()) { if (monitor.isCanceled()) { @@ -154,7 +180,8 @@ return false; } - private Status createWarnings(Resource resource, IAcceptor<Issue> acceptor, CancelIndicator monitor) { + private Status createWarnings( + Resource resource, IAcceptor<Issue> acceptor, CancelIndicator monitor) { for (Resource.Diagnostic warning : resource.getWarnings()) { if (monitor.isCanceled()) { return Status.CANCELED; @@ -165,7 +192,9 @@ } private static enum Status { - OK, CANCELED, PROTO1_IMPORTS_FOUND; + OK, + CANCELED, + PROTO1_IMPORTS_FOUND; boolean hasProto1Imports() { return (this == PROTO1_IMPORTS_FOUND); @@ -175,4 +204,8 @@ return (this == CANCELED); } } -} \ No newline at end of file + + public static TimingCollector getScopeProviderTimingCollector() { + return scopeProviderTimingCollector.get(); + } +}