C/C++ Reverse Engineering

How to cope with existing legacy code when switching to model driven software development.

C/C++ Reverse Engineering

Postby reto » Wed Feb 09, 2011 9:08 am

Henrik asked us the following question:

Impressive tool.
Just being curious, can this be used to reverse engineer for example a C or C++ application?
Evt with modifications ?

kind regards
Henrik


Dear Henrik

Thank you for using actifsource.

Reverse engineering is always a subject of discussion in our lab.

The short answer: we do not support reverse engineering yet.

The longer answer: MDSD captures the meta model of a business domain. Having not explicitly stated such a meta model before, business objects are not strongly structured even if you take great care.

The compiler always ensures that code is syntactically correct. But business object structures are kind of additional semantics which is done by convention.

Automatically reverse engineering legacy code would mean to have found the grail: finding the underlying business object structure in thousands of lines of code.

Anyway: if your code is structured well we could write a small antlr parser which extracts the business domain structure from existing code and instantiates the business object model automatically.

Please let us know if you need some help.

Kind regards
Reto
reto
 
Posts: 75
Joined: Thu Aug 05, 2010 2:45 pm
Location: Switzerland

Re: C/C++ Reverse Engineering

Postby hsorensen » Wed Feb 09, 2011 5:57 pm

Could you post an example of an ANTLR parser that instantiates a business object model ?
This would probably be a staged approach, where first as much as possible information about the legacy system is captured. Once the information is complete an import can be done.
hsorensen
 
Posts: 5
Joined: Wed Feb 09, 2011 5:51 pm

Re: C/C++ Reverse Engineering

Postby Heiko Böttger » Thu Feb 10, 2011 1:21 pm

Dear Henrik,

writing our own parser would be really tough stuff. Instead we suggest to reuse the eclipse cdt core plugin, which provides access to it's generated AST. The following example is an eclipse command handler implementation that shows how to access the cdt-AST, get it's source filename and create an actifsource class using the filename as it's name.

Code: Select all
package ch.actifsource.environment.cdt;

import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.model.*;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.core.commands.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.viewers.*;
import org.eclipse.ui.handlers.HandlerUtil;

import ch.actifsource.core.*;
import ch.actifsource.core.Package;
import ch.actifsource.core.job.*;
import ch.actifsource.core.session.ISession;
import ch.actifsource.core.update.IModifiable;
import ch.actifsource.environment.EnvironmentPlugin;
import ch.actifsource.environment.scope.ProjectScope;
import ch.actifsource.environment.util.EnvironmentUtil;
import ch.actifsource.util.NoResult;

public class ImportCppHandler extends AbstractHandler {
 
  /**
   * Executed when the command is invoked.
   *
   * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
   */
  @Override
  public Object execute(ExecutionEvent event) throws ExecutionException {
    log("ImportCppHandler");
    IFile file = getSelectedFile(event);
    log("File", file);
    if (file == null) return null;
    importCppFile(file);
    return null;
  }
 
  /**
   * @param event
   * @return the selected file or null if no file is selected
   */
  private IFile getSelectedFile(ExecutionEvent event) {
    ISelection selection = HandlerUtil.getCurrentSelection(event);
    log("Selection", selection);
    if (selection instanceof IStructuredSelection) {
      IStructuredSelection structuredSelection = (IStructuredSelection)selection;
      return EnvironmentUtil.adaptToObjectOrNull(structuredSelection.getFirstElement(), IFile.class);
    }
    return null; //not an IStructuredSelection or null
  }
 
  public void importCppFile(IFile file) {
    final IASTTranslationUnit ast = getAstOrNull(file);
    if (ast == null) return;
   
    final ProjectScope scope = getOutputScope();
    //session is need to modify actifsource resource in the scope
    ISession session = EnvironmentPlugin.getResourcePool().getSession(scope);
   
    //update actifsource
    session.executeUndoableAndSave(createImportWriteJob(ast, scope));
  }

  /**
   * Creates a writejob generating actifsource resource from the ast
   *
   * @param ast the ast to read
   * @param outputScope the outputscope where the resource are written
   * @return the writejob ready for execution
   */
  private WriteJob<NoResult> createImportWriteJob(final IASTTranslationUnit ast, final ProjectScope outputScope) {
    return new WriteJob<NoResult>() {
     
      @Override
      protected NoResult doExecuteOn(IModifiable modifiable) {
        Package pkg = new Package(outputScope, "asrc", "cppImport");
       
        //create an instance of an actifsource "Class" with the name of the source file from which the ast was generated
        Resource resourceNode = Update.createResourceWithDefaults(modifiable, pkg, ch.actifsource.core.CoreModel.Class, ast.getContainingFilename(),
            null);
       
        //add a statement to the actifsource class, this may be done bases on the content of the ast
        boolean isAbstract = true;
        Update.createStatement(modifiable, pkg, resourceNode, ch.actifsource.core.CoreModel.Class_modifier, getModifier(isAbstract));
       
        //TODO add more code to create and modify resources base on ast
       
        return NoResult.NO_RESULT;
      }
     
      private Resource getModifier(boolean isAbstract) {
        return isAbstract ? ch.actifsource.core.CoreModel.InheritanceModifier_Abstract : ch.actifsource.core.CoreModel.InheritanceModifier_Final;
      }
    };
  }
 
  /**
   * @return the actifsource scope where new resources have to be written
   */
  private ProjectScope getOutputScope() {
    IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject("importProject");
    return new ProjectScope(project);
  }
 
  /**
   * Gets the cdt-AST for the given file or null if it isn't a cpp-file handled by cdt or the ast is not available.
   *
   * @param file any eclipse file
   * @return the ast or null if not available
   */
  private IASTTranslationUnit getAstOrNull(IFile file) {
    ICElement cppElement = CoreModel.getDefault().create(file);
    log("CElement", cppElement);
    if (cppElement == null) return null;
    ITranslationUnit translationUnit = (ITranslationUnit)cppElement.getAdapter(ITranslationUnit.class);
    log("TranslationUnit", translationUnit);
    if (translationUnit == null) return null;
    final IASTTranslationUnit ast;
    try {
      ast = translationUnit.getAST();
    } catch (CoreException e) {
      log(e.getMessage());
      return null;
    }
    return ast;
  }
 
  /**
   * @see #log(String)
   */
  private void log(String title, Object message) {
    log(title + " " + String.valueOf(message));
  }
 
  /**
   * Logs the message to the eclipse "Error Log" as information
   *
   * @param message
   */
  private void log(String message) {
    EnvironmentPlugin.getDefault().logInfo(message);
  }
 
}



To make this example working, it must be registered put into a plugin which setups the dependencies to the required plugins and registeres this commandhandler using the org.eclipse.command.ui-extension point.

The commandhandler implementation uses both the cdt-core to read the ast and the actifsource core to write actifsource-resources. You need to know the structure of the cdt-ast and the actifsource core. In actifsource each resource basicaly has statements which are simply triples containig the resource as subject, the property (in the example the class name) and the object (the value of the property). Working with the actifsource core directly means working on a very abstract level. The reason for this is, that the actifsource core has to deal with all models and must also deal with invalid resources.

If you wish further information and support please contact us to discuss how we can work together.

Best regards

Heiko Böttger
Heiko Böttger
 
Posts: 21
Joined: Fri Aug 06, 2010 7:27 am

Re: C/C++ Reverse Engineering

Postby hsorensen » Fri Feb 11, 2011 7:57 pm

How about a different approach.
Typically the Legacy code compilers can generate a vast amount of cross-reference information. By using the cross reference information you would not have to create the language parsers, but you could define an import format. It should be trivial (compared to writing a parser) to transform the cross reference lists to another text format.
Can you imagine what the import format should be looking like ?
hsorensen
 
Posts: 5
Joined: Wed Feb 09, 2011 5:51 pm

Re: C/C++ Reverse Engineering

Postby Heiko Böttger » Mon Feb 14, 2011 1:26 pm

Dear Henrik

excellent idea, you may create a csv-file and import the rows as actifsource resource. First you have to name each column in your csv-file. The column names are defined in the first row. As an second step create a new actifsource class with an attribute for each columnname. Using the same name for the columns will associated all these columns to the same attribute. In a final step import the cvs-file using the Import CSV Wizard by select the csv-file and actifsource class. During the import Actifsource will create a new instance for each row storing the column values using the mapped attributes.

However at the moment it's not possible to import data from multiple csv-files at the same time. In other words it's not possible to define references between resources based on csv-files. If you really need such an import feature, I suggest to contact us via mail or phone and get informed about our professional support offers.

Best regards

Heiko Böttger
Heiko Böttger
 
Posts: 21
Joined: Fri Aug 06, 2010 7:27 am


Return to Legacy Code

Who is online

Users browsing this forum: No registered users and 1 guest

cron