1

Recently, my Eclipse editor started becoming really sluggish when editing Xtend files. I am using Xtend in the context of the Xtext framework (which provides certain interfaces using Xtend). I thought it had something to do with my Eclipse installation, so I deleted it entirely off my MacBook and re-installed the newest version (Eclipse 2019-09). However, it was still really sluggish when editing Xtend files. It does this every few seconds (loading figure appears, it hangs for a few seconds after which it repeats this sequence). I notice that the Progress tab in my Eclipse editor shows the message 'updating editor state (sleeping)' after it stops hanging. It only happens with Xtend files (other files like java work just fine in Eclipse without hanging). The Xtend file I am working in is currently about 500 Lines of Codes. Not a small file, but could this be the reason my Eclipse IDE keeps hanging? Things I already tried to fix this hanging problem:

  • Fully delete and re-install Eclipse on my MacBook (only installing the necessary plug-ins that I need: The Palladio PCM framework, and the Xtext Framework).
  • Provide extra memory (up to 6000 MB) for the Eclipse environment and its VMs (using the memory parameters (XmX e.g.) in the eclipse.ini file).
  • Suspend all validators in the Eclipse environment (disabling the option to override this).

The weird thing is that it keeps telling 'updating editor state (sleeping)' after it stops hanging. This message appears for about 0.1 to 2 seconds. I think that disabling the 'editor state update' might resolve this problem, but I don't know where I can suspend this option. Does anyone have a clue on why my Eclipse IDE keeps hanging every few seconds when editing Xtend files?

EDIT 9 December 2019:

/*
 * generated by Xtext 2.17.1
 */
package org.xtext.example.mydsl.generator

import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.AbstractGenerator
import org.eclipse.xtext.generator.IFileSystemAccess2
import org.eclipse.xtext.generator.IGeneratorContext
import org.xtext.example.mydsl.finalDsl.UserProfile
import org.xtext.example.mydsl.finalDsl.Model
import org.xtext.example.mydsl.finalDsl.ConceptualMethod
import org.xtext.example.mydsl.finalDsl.Expression
import org.xtext.example.mydsl.finalDsl.AdditionalExpressions
import org.xtext.example.mydsl.finalDsl.RelationalOperator
import org.xtext.example.mydsl.finalDsl.SingleLibraryBusinessMethodStatement
import org.xtext.example.mydsl.finalDsl.SingleLibraryPersistenceMethodStatement
import org.xtext.example.mydsl.finalDsl.SingleLibraryInterFaceMethodStatement
import org.xtext.example.mydsl.finalDsl.IfStatements
import org.xtext.example.mydsl.finalDsl.IfElseStatements
import org.eclipse.emf.ecore.EcoreFactory
import org.eclipse.emf.ecore.EPackage
import org.eclipse.xtext.ISetup
import org.eclipse.emf.ecore.EStructuralFeature
import java.util.TreeMap
import java.util.HashMap
import java.util.Set
import org.palladiosimulator.pcm.util.*
import org.palladiosimulator.pcm.repository.impl.*
import org.palladiosimulator.pcm.repository.BasicComponent
import org.eclipse.emf.ecore.EClass
import org.palladiosimulator.pcm.core.CoreFactory
import org.palladiosimulator.pcm.repository.RepositoryFactory
import org.palladiosimulator.pcm.seff.SeffFactory
import org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl
import org.eclipse.emf.common.util.URI
import com.google.inject.Injector
import org.xtext.example.mydsl.FinalDslStandaloneSetup
import org.eclipse.emf.ecore.resource.ResourceSet
import com.google.inject.Injector;
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
import org.palladiosimulator.pcm.seff.seff_performance.ParametricResourceDemand
import org.palladiosimulator.pcm.seff.seff_performance.impl.ParametricResourceDemandImpl
import org.palladiosimulator.pcm.seff.seff_performance.SeffPerformanceFactory
import java.util.Collections
import org.eclipse.emf.ecore.EObject
import org.xtext.example.mydsl.finalDsl.ElseStatement
import java.util.ArrayList
import java.math.BigDecimal
import org.xtext.example.mydsl.finalDsl.Statement
import org.xtext.example.mydsl.finalDsl.RelationalOperator
import org.xtext.example.mydsl.finalDsl.AdditionOperator
import org.xtext.example.mydsl.finalDsl.MultiplicationOperator
import org.xtext.example.mydsl.finalDsl.libraryInterFaceMethodStatementEnum

/**
 * Generates code from your model files on save.
 * 
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#code-generation
 */
class FinalDslGenerator extends AbstractGenerator {


override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context ) {

    // Method call to build and store PCM Palladio repository. Right click on the repository file
    // to create a .repository_diagram file to 'graphically' view and edit the repository accordingly.
    RepositoryBuilder(resource,fsa)

    // Method call to calculate the resource demands for the If/Else statements in the model according
    // to the specified probabilities in the user profiles.
    UserProfileEstimator(resource,fsa)

    // Method call to build and store your modeled DSL instance accordingly. Mind that this has nothing
    // to do with the creation of the PCM Palladio repository above, but it might come in handy if you want
    // to export your DSL instances (for use outside of the xText IDE environment).
    ModelXMLBuilder(resource)   
    }

// Method for calculating the resource demands for the If/Else statements in the model according to the 
// specified probabilities in the user profiles. The declared probabilities are mapped to the If/Else Statements
// by using a set of for loops (see loops for elaboration documentation).
def UserProfileEstimator (Resource resource,IFileSystemAccess2 fsa) {

    // Treemap of  <String,Integer> pairs describing the set of library functions and their resource demands. These
    // resource demands can later be substituted for resource demand sample data in order to calculate the resource
    // demand estimations for the performance models. 
    var TreeMap<String,Integer> resourceTable = new TreeMap
        resourceTable.put("Hash",120)
        resourceTable.put("Concatenate",1000)
        resourceTable.put("Average",60)
        resourceTable.put("Count",80)
        resourceTable.put("Today",80)
        resourceTable.put("Time",80)
        resourceTable.put("Random",80)
        resourceTable.put("Max",43)
        resourceTable.put("Min",45)
        resourceTable.put("Root",80)
        resourceTable.put("Square",80)
        resourceTable.put("ReadDatabase",43)
        resourceTable.put("Message",123)
        resourceTable.put("ErrorMessage",100)
        resourceTable.put("WarningMessage",43)
        resourceTable.put("WriteDatabase",78)
        resourceTable.put("DeleteFromDatabase",67)
        resourceTable.put("Log",40)
        resourceTable.put('>',40)
        resourceTable.put('<>',40)
        resourceTable.put('<',40)
        resourceTable.put('=>',40)
        resourceTable.put('<=',40)
        resourceTable.put('==',40)
        resourceTable.put('in',40)
        resourceTable.put("+",40)
        resourceTable.put("-",40) 
        resourceTable.put("OR",40)
        resourceTable.put("*",40)
        resourceTable.put("/",40)
        resourceTable.put("AND",40)

    // check if statements
    var conceptualMethodDemandList = newArrayList
    var ifElseDemandList = newArrayList

    // TreeMap variable in order to store all the user profiles (indicated by the String variable in the list
    // and the corresponding resource demands (stored as Arraylist <String> arrays). The strings in this latter
    // Arraylist represent the probabilities for the If/Else statements in the model. Each of these probabilities
    // Contains the resource demand for the condition evaluation of the If-Statement, the If Statement body itself
    // and the Else statement. If one of these parts contains no Resource demand (if no library functions are present)
    // if an Else statement is not defined, the Resource Demand is zero.
    var TreeMap <String, ArrayList <String> > completeDemandList = new TreeMap

    // For loop to traverse through all declared user profiles in the model.
    for (userprofile : resource.allContents.toIterable.filter(UserProfile)) {   

        // For loop to traverse through all declared Conceptual Methods in the model. A conceptual method counter
        // variable is stored in order to keep track of which Conceptual Method is processed by the for loop. This
        // counter is used as the index for processing all user profile and resource demand data which are both stored
        // as arrays. 
        var conceptualMethodCounter = 0

        for (conceptualMethod : resource.allContents.toIterable.filter(ConceptualMethod)) {

        // For loop traversing through all If/Else statements in the corresponding conceptual methods declared in the model.
        // An ifElseStatementCounter variable is stored for the same reason as specified in the Conceptual Method for loop 
        // (see documentation above Conceptual Method for loop).
        var ifElseStatementCounter = 0
        for (ifElseStatement : conceptualMethod.eAllContents.toIterable.filter(IfElseStatements))  {
            var ifDemand = 0
            var elseDemand = 0
            var expressionDemand = 0

            // In the for-loop each if/Else statement is filtered for its If Statement part. This if Statement part is then filtered for
            // all three Library Function sorts. This happens for the functions in the expression part of the If Statement (containing the
            // condition that is evaluated), the actual If Statement part, and for the Else Statement part (in the case
            // an else statement is present in the If/Else statment).
            //  The dispatch method getDemand method is called for each library function statement together with the 
            // resourceTable variable containing all library functions and their resource demands. This dispatch method returns
            // the resource demand for the respective library function, after which this result is added to a 'Demand' variable
            // which contains the aggregate sum of all library functions for the calculated If/Else Statement part.
            for (ifStatement : ifElseStatement.eAllContents.toIterable.filter(IfStatements)){

                 for (expressionResourceStatement : ifStatement.expression.eAllContents.toIterable) {

                     switch(expressionResourceStatement){
                        SingleLibraryPersistenceMethodStatement,
                        SingleLibraryInterFaceMethodStatement,
                        SingleLibraryBusinessMethodStatement, 
                        RelationalOperator,
                        AdditionOperator,
                        MultiplicationOperator:                         
                             expressionDemand += getDemand(expressionResourceStatement,resourceTable)
                        }             
                    }

            for (innerIfStatements : ifStatement.statements){
                 for (ifResourceStatement : innerIfStatements.eAllContents.toIterable) {
                     switch(ifResourceStatement){
                        SingleLibraryPersistenceMethodStatement,
                        SingleLibraryInterFaceMethodStatement,
                        SingleLibraryBusinessMethodStatement, 
                        RelationalOperator,
                        AdditionOperator,
                        MultiplicationOperator:
                             ifDemand += getDemand(ifResourceStatement,resourceTable)
                        }
                    }
                }

            for (elseStatement : ifElseStatement.eAllContents.toIterable.filter(ElseStatement)){
                 for (elseResourceStatement : elseStatement.eAllContents.toIterable) {
                     switch(elseResourceStatement){
                        SingleLibraryPersistenceMethodStatement,
                        SingleLibraryInterFaceMethodStatement,
                        SingleLibraryBusinessMethodStatement, 
                        RelationalOperator,
                        AdditionOperator,
                        MultiplicationOperator:
                             elseDemand += getDemand(elseResourceStatement,resourceTable)
                        }
                    }
                }
                // end of IfStatements for loop
            }

        // Add the resource demands of the evaluationDemand variable to the ifElseDemandList variable containing the resource demands
        // for the If/Else statement that is currently processed in the for-loop.
        ifElseDemandList.add("EvaluationDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(expressionDemand))

        // Add the resource demands of the ifDemand and elseDemand variable to the ifElseDemandList variable containing the resource demands
        // for the If/Else statement that is currently processed in the for-loop. In the case the probability is declared in the user profile 
        // as zero (.0), the probability is assumed to be .50. The second if-statement below filters and handles these cases accordingly.
        if (userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter) != 0) {
            ifElseDemandList.add("IfStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(ifDemand*userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter)))
            ifElseDemandList.add("ElseStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(elseDemand*(1-userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter))))
            }

        if (userprofile.probabilitysets.get(conceptualMethodCounter).probability.get(ifElseStatementCounter) == 0) {            
            ifElseDemandList.add("IfStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(ifDemand*0.5))
            ifElseDemandList.add("ElseStatementDemands" + (conceptualMethodCounter+1).toString + "." + (ifElseStatementCounter+1).toString + ": " + BigDecimal.valueOf(elseDemand*0.5))
            }


        // Once all the parts of the If/Else statement in the for-loop are processed, the resulting list of resource demands for the If/Else statement (stored
        // in the ifElseDemandList variable) are added to the conceptualMethodDemandList variable containing the resource demands for all
        // If/Else statements in the conceptual method. Next the ifElseDemandList is cleared and the ifElseStatementCounter is incremented in order for the next for-loop
        // iteration to process the next If/Else statement of the conceptual method.
        conceptualMethodDemandList.add('''
                                    «ifElseDemandList.clone.toString»
                                    ''')
        ifElseDemandList.clear
        ifElseStatementCounter++
    }

    // Once all the If/Else statements in the for-loop are processed, the resulting list of resource demands for all If/Else statements (stored
    // in the conceptualMethodDemandList variable) are added to the completeDemandList variable containing the resource demands for all
    // conceptual methods and user profiles.
    conceptualMethodCounter++

    completeDemandList.put('''
                    «userprofile.name»
                    ''',conceptualMethodDemandList.clone() as ArrayList<String>)

    }

    conceptualMethodDemandList.clear
    }


    // The completeDemandList variable containing all resource demands according to all the specified user profiles, are
    // stored below in a text file.
    fsa.generateFile(
        "IfElseStatementsDemands.txt",
         completeDemandList.toString)
}

// Dispatch method for looking up the resource demand of a library business function in a TreeMap variable
// containing the resource demands for all library functions.
def dispatch getDemand (SingleLibraryBusinessMethodStatement statement, TreeMap<String,Integer> resourceTable) {
        return resourceTable.get(statement.libraryFunction.toString)
    }

// Dispatch method for looking up the resource demand of a library interface function in a TreeMap variable
// containing the resource demands for all library functions.
def dispatch getDemand (SingleLibraryInterFaceMethodStatement statement, TreeMap<String,Integer> resourceTable) {
        return resourceTable.get(statement.libraryFunction.toString)
    }

// Dispatch method for looking up the resource demand of a library persistence function in a TreeMap variable
// containing the resource demands for all library functions.
def dispatch getDemand (SingleLibraryPersistenceMethodStatement statement, TreeMap<String,Integer> resourceTable) {
        return resourceTable.get(statement.libraryFunction.toString)
    }

def dispatch getDemand (RelationalOperator statement, TreeMap<String,Integer> resourceTable) {
        return resourceTable.get(statement.operator.toString)
}

def dispatch getDemand (AdditionOperator statement, TreeMap<String,Integer> resourceTable) {
    return resourceTable.get(statement.operator.toString)
}

def dispatch getDemand (MultiplicationOperator statement, TreeMap<String,Integer> resourceTable) {
    return resourceTable.get(statement.operator.toString)
}

def RepositoryBuilder (Resource resource,IFileSystemAccess2 fsa) {

    var repository = RepositoryFactory.eINSTANCE.createRepository
    repository.setEntityName("Repository for Functional Model")

    var myInterface = RepositoryFactory.eINSTANCE.createOperationInterface()
    myInterface.setEntityName("My Interface")
    repository.getInterfaces__Repository().add(myInterface); //add first class entity

    var opProvRole = RepositoryFactory.eINSTANCE.createOperationProvidedRole()
    opProvRole.setEntityName("Provided Role of Basic Component")

    // set the interface for the role:
    opProvRole.setProvidedInterface__OperationProvidedRole(myInterface)

    for (element : resource.allContents.toIterable.filter(ConceptualMethod))
    {   
        var component = RepositoryFactory.eINSTANCE.createBasicComponent()
        component.setEntityName(element.name)
        ConceptualClass2SEFF(component, element)
        // set/add the role for basic component:
        component.getProvidedRoles_InterfaceProvidingEntity().add(opProvRole)
        repository.getComponents__Repository().add(component)  //add first class entity
    }

    var storeRepositoryURI =  URI.createURI(resource.URI.trimSegments(1).toString + "/src-gen/TestRepository.repository")
    var result = new XMLResourceImpl(storeRepositoryURI)
        result.getContents().add(repository)
        result.save(null)
    }

def ConceptualClass2SEFF (BasicComponent component, ConceptualMethod conceptualmethod) {
    for (statement : conceptualmethod.eAllContents.toIterable.filter(SingleLibraryPersistenceMethodStatement)) 
    { 
        var seff = SeffFactory.eINSTANCE.createResourceDemandingSEFF()
        var internalAction = SeffFactory.eINSTANCE.createInternalAction()
        internalAction.entityName = statement.libraryFunction.toString
        internalAction.resourceDemand_Action.add(SeffPerformanceFactory.eINSTANCE.createParametricResourceDemand)
        seff.steps_Behaviour.add(internalAction)
        component.serviceEffectSpecifications__BasicComponent.add(seff)    
    }

    for (statement : conceptualmethod.eAllContents.toIterable.filter(SingleLibraryInterFaceMethodStatement)) 
    { 
        var seff = SeffFactory.eINSTANCE.createResourceDemandingSEFF()
        var internalAction = SeffFactory.eINSTANCE.createInternalAction()
        internalAction.entityName = statement.libraryFunction.toString
        internalAction.resourceDemand_Action.add(SeffPerformanceFactory.eINSTANCE.createParametricResourceDemand)
        seff.steps_Behaviour.add(internalAction)
        component.serviceEffectSpecifications__BasicComponent.add(seff)    
    }

    for (statement : conceptualmethod.eAllContents.toIterable.filter(SingleLibraryBusinessMethodStatement)) 
    { 
        var seff = SeffFactory.eINSTANCE.createResourceDemandingSEFF()
        var internalAction = SeffFactory.eINSTANCE.createInternalAction()
        internalAction.entityName = statement.libraryFunction.toString
        internalAction.resourceDemand_Action.add(SeffPerformanceFactory.eINSTANCE.createParametricResourceDemand)
        seff.steps_Behaviour.add(internalAction)
        component.serviceEffectSpecifications__BasicComponent.add(seff)    
    }
}

def ModelXMLBuilder (Resource dslInstance) {
    var injector = new FinalDslStandaloneSetup().createInjectorAndDoEMFRegistration()
    var resourceSet = injector.getInstance(ResourceSet)

    dslInstance.load(null)
    EcoreUtil.resolveAll(resourceSet)

    var storeXmiURI =  URI.createURI(dslInstance.URI.trimSegments(1).toString + "/src-gen/DslModel.xmi")

    var xmiResource = resourceSet.createResource(storeXmiURI)
        xmiResource.getContents().add(dslInstance.getContents().get(0))
        xmiResource.save(null)
    }
}
Jos B.
  • 27
  • 5
  • 1
    Which version of Xtend do you use? – Sebastian Zarnekow Dec 03 '19 at 15:58
  • @SebastianZarnekow according to my Eclipse installation details I am currently using xTend IDE 2.19.0.v20190902-1322. Not sure if an update of the xTend IDE caused this problem, but that could well be the case (the problems started about 2 months ago I think). – Jos B. Dec 03 '19 at 16:13
  • I see that version 2.20 was released today. Updating it right now; I hope this will provide a fix for me. – Jos B. Dec 03 '19 at 16:18
  • 2
    @JosB. do you have a antivirus active? could you provide a yourkit or jvisualvm snapshot got the problem? – Christian Dietrich Dec 04 '19 at 16:00
  • 1
    @ChristianDietrich thanks for responding. I have Avira active, but even when shutting it down Xtend keeps lagging. I downgraded Xtend to 2.18 again (with a fresh Eclipse installation) but it keeps lagging. Xtend 2.20 does also not solve the problem. I created a jvisualvm snapshot (of about 10-20 second) for you of my Eclipse instance. The two massive cpu spikes you see were caused by typing in Xtend (just simply typing a few random characters in Xtend). See https://drive.google.com/drive/folders/1tG8TCLE56JBqAwasXGNz_AtcdIhyK5VX?usp=sharing – Jos B. Dec 06 '19 at 11:34
  • By the way, in the drive folder above you also see a file "Eclipse boot-up.txt". This contains the message that jvisualvm immediately gave during boot-up about Eclipse (it reported that there was a possible deadlock problem with Eclipse). – Jos B. Dec 06 '19 at 11:36
  • looks like you use constructs in your code that are slow in the typesystem (e.g. massive overloads). i can also see that there is quite some time spent on garbage collections. without any reproducing example its hard to tell what exactly causes this in your code – Christian Dietrich Dec 09 '19 at 06:27
  • @ChristianDietrich thanks a lot for your answer. Yeah I think that some lines in my code mess up the performance in my Eclipse IDE. I posted the code that I use in the code generator provided by Xtext (the code above is very messy, but it's what Im currently working on). If I delete the code and only have an empty 'FinalDslGenerator' class, the IDE just works fine. I will try splitting up the code in different files/delete source code line by line to see what is slowing performance down. Thanks a lot for your effort though; I know now that the problem is not with my system/IDE! – Jos B. Dec 09 '19 at 13:28
  • 1
    one thing might be that you dont have explicit method return types in the signatures – Christian Dietrich Dec 09 '19 at 13:50
  • 1
    Note: Checking now what causes the IDE to slow down. The number of types to filter in the switch statements in my code above seem to cause the problems. When I only fill in one type only in these switches, the editor runs smoothly again. So it probably doesn't really like switch statements with a lot of types to filter, or it has something to do with the fact that the types all have to be imported from my Xtext DSL definition. Not entirely sure, but at least I found the bottleneck right now. – Jos B. Dec 09 '19 at 14:06

0 Answers0