Making a Plugin to Compute Block Timing

This tutorial is about creating a new plug-in in OTAWA and this plug-in substitutes itself to the way OTAWA computes time for blocks. The block is easily calculated by considering that all instructions spend 1 cycle.

The sources used in this tutorial can be downloaded from simple_time.tar.bz2.

A plug-in in OTAWA is a piece of code that may be installed inside OTAWA's framework and can be then widely used by other plug-ins, scripts and all facilities supporting plug-ins.

Writing the plug-in

The sources, in this tutorial, is only made of two files, time.cpp and timer.h, but it easy to extend this example to bigger plug-in projects made of several sources and header files. Even the header timer.h is optional as an registered OTAWA's processor can be called by its registered name. The plugin will be named simple_time: as we use the plug-in to create a C++ namespace, we conform to the C++ identifier naming rules.

The header is defined as below:

  1. /*
  2.  * simple_time -- OTAWA plug-in assigning 1 cycle/instruction.
  3.  */
  4. #ifndef SIMPLE_TIME_H
  5. #define SIMPLE_TIME_H
  6.  
  7. #include <otawa/proc/BBProcessor.h>
  8.  
  9. namespace simple_time {
  10.  
  11. using namespace otawa;
  12.  
  13. class TimeBuilder: public BBProcessor {
  14. public:
  15. static p::declare reg;
  16. TimeBuilder();
  17. protected:
  18. void processBB(WorkSpace *ws, CFG *g, Block *b) override;
  19. };
  20.  
  21. } // simple_time
  22.  
  23. #endif // SIMPLE_TIME_H

Basically, this header declares the class implementing the analysis, TimeBuilder, that is defined in the namespace simple_time (line 9) to prevent name clash with other symbols in OTAWA. To be considered as an OTAWA processor, and considering TimeBuilder analysis basic blocks, it inherits from BBProcessor (line 13). In order to be reachable by name when it is invoked later, a registration has to be statically declared, reg (line 15).

Now, the declaration has to be implemented in simple_time.cpp:

  1. /*
  2.  * simple_time -- OTAWA plug-in assigning 1 cycle/instruction.
  3.  */
  4.  
  5. #include "simple_time.h"
  6. #include <otawa/ipet/features.h>
  7. #include <otawa/proc/ProcessorPlugin.h>
  8.  
  9. namespace simple_time {
  10.  
  11. ///
  12. p::declare TimeBuilder::reg = p::init("simple_time::TimeBuilder", Version(1, 0, 0))
  13. .extend<BBProcessor>()
  14. .provide(ipet::BB_TIME_FEATURE)
  15. .make<TimeBuilder>();
  16.  
  17. ///
  18. TimeBuilder::TimeBuilder(): BBProcessor(reg) {
  19. }
  20.  
  21. ///
  22. void TimeBuilder::processBB(WorkSpace *ws, CFG *g, Block *b) {
  23. if(!b->isBasic())
  24. return;
  25. auto bb = b->toBasic();
  26. ipet::TIME(b) = bb->count();
  27. if(logFor(LOG_BLOCK))
  28. log << "\t\t\ttime: " << *ipet::TIME(b) << io::endl;
  29. }
  30.  
  31. class Plugin: public ProcessorPlugin {
  32. public:
  33. Plugin(void): ProcessorPlugin("simple_time", Version(1, 0, 0), OTAWA_PROC_VERSION) { }
  34. };
  35.  
  36. } // simple_time
  37.  
  38.  
  39. simple_time::Plugin simple_time_plugin;
  40. ELM_PLUGIN(simple_time_plugin, OTAWA_PROC_HOOK);

Lines 12-15 defines the registration of the analysis. This encompasses public name (used to retrieve symbolically the analysis) and version must always be provided. Then, as C++ does not provide automatically this information, we have to give the base processor class, BBProcessor and also, in make, the analysis class to build it. Line 14 is used to connect this analysis with OTAWA analysis flow: it says that TimeBuilder implements feature otawa::ipet::BB_TIME_FEATURE. This feature is used in IPET analysis to provide execution times for blocks, using otawa::ipet::TIME property. This feature is used, for instance, by the code processor otawa::ipet::WCET_FEATURE to build the objective function and compute the WCET.

Your class's constructor should not take more than p::reg as arguments. Otherwise, it would trigger a compile error because the template function make would not be able to register your class.

Lines 18-19 defines the analysis construction which main action is to pass the registration to the base class constructor (and automatically register the analysis to OTAWA's registration system.

The main work of the analysis is performed in processBB function (lines 22-29). Its content is very straight-forward: if the block b is a basic block (lines 23-25), the ipet::TIME property is assigned with a number of cycles corresponding to the number of instructions in the block (line 26). Lines 27-28 are only used to perform display in verbose mode.

The declaration of the plug-in itself is performed in lines 39-40. Line 39 with the plug-in name and version. OTAWA_PROC_VERSION must be kept as is as it declares the plug-in version used to compile this plug-in. Such an argument is used to prevent version incompatibilities of plug-in system.

The macro on line 40 performs some declaration to provide a hook to link the plug-in with OTAWA. The first argument is the name of the plug-in object declared on line 39. The second argument must be kept as is because it provides a symbol name used for the connection.

Compiling the plug-in

There are several ways to compile an OTAWA's plug-in but, in this tutorial, only shows the CMake way exposed in the file CMakeLists.txt:

  1.  
  2. # configuration
  3. set(PLUGIN "simple_time")
  4. set(NAMESPACE "")
  5. set(SOURCES "simple_time.cpp")
  6.  
  7.  
  8. # C++ flags
  9. if(CMAKE_BUILD_TYPE MATCHES Release)
  10. add_definitions(-DNDEBUG)
  11. add_compile_options(-Wall)
  12. if(CMAKE_VERSION LESS "3.1")
  13. add_compile_options(--std=c++11)
  14. message(STATUS "C++11 set using cflags")
  15. set(CMAKE_CXX_STANDARD 11)
  16. message(STATUS "C++ set using CMAKE_CXX_STANDARD")
  17.  
  18.  
  19. # script
  20. project("${PLUGIN}")
  21.  
  22.  
  23. # look for OTAWA
  24. if(NOT OTAWA_CONFIG)
  25. find_program(OTAWA_CONFIG "otawa-config" DOC "path to otawa-config")
  26. if(NOT OTAWA_CONFIG)
  27. message(FATAL_ERROR "ERROR: otawa-config is required !")
  28. message(STATUS "otawa-config at ${OTAWA_CONFIG}")
  29.  
  30. execute_process(COMMAND "${OTAWA_CONFIG}" --prefix
  31. OUTPUT_VARIABLE OTAWA_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE)
  32. execute_process(COMMAND "${OTAWA_CONFIG}" --plugdir
  33. OUTPUT_VARIABLE OTAWA_PLUGDIR OUTPUT_STRIP_TRAILING_WHITESPACE)
  34. execute_process(COMMAND "${OTAWA_CONFIG}" --make-plug "${PLUGIN}" --cflags
  35. OUTPUT_VARIABLE OTAWA_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
  36. execute_process(COMMAND "${OTAWA_CONFIG}" --make-plug "${PLUGIN}.eld" --libs -r
  37. OUTPUT_VARIABLE OTAWA_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
  38.  
  39.  
  40. # plugin definition
  41. include_directories ("${CMAKE_SOURCE_DIR}")
  42. add_library ("${PLUGIN}" SHARED ${SOURCES})
  43. set_property (TARGET "${PLUGIN}" PROPERTY PREFIX "")
  44. set_property (TARGET "${PLUGIN}" PROPERTY COMPILE_FLAGS "${OTAWA_CFLAGS}")
  45. target_link_libraries ("${PLUGIN}" "${TARGET_LIB}" "${OTAWA_LDFLAGS}")
  46.  
  47.  
  48. # installation
  49. install(TARGETS "${PLUGIN}" LIBRARY DESTINATION "${OTAWA_PLUGDIR}/${NAMESPACE}")
  50. install(FILES "${PLUGIN}.eld" DESTINATION "${OTAWA_PLUGDIR}/${NAMESPACE}")

Basically, you can use this CMakeLists.txt file as is and to adapt it to a different plug-ins just by changing lines 4-6. PLUGIN and SOURCES definitions are straight-forward. NAMESPACE is used for analysis contained in sub-namespaces (out of the basic namespace). It is not used for our plug-in.

Lines 10-20 are used to ensure C++-11 compatibility and to disable assertion in production release.

Lines 28-43 looks for otawa-config and uses it to get information required to build and install a plug-in (compilation and linkage flags, directories, etc).

Lines 47-51 configure the build of the shared library implementing the plug-in.

The installation is performed by lines 55-56. The first line install the shared library at the right location and the second line install a small file, .eld, is used by the OTAWA system to manage the plug-in. In our case, this file is very simple but a lot of options can be specified to configure the plug-in (refer to ELM plug-in documentation)

[elm-plugin]
name=simple_time

You will get an error message when executing cmake .: ERROR: command line error: file reading: No such file or directory

Once CMakeLists.txt and simple_time.eld have been created, we have just to prepare the building environment:

$ cmake .

And then to install it:

$ make install

You got some error like:

CMake Error at cmake_install.cmake:46 (file):

file INSTALL cannot copy file "/home/blanc/work/XDD/xdd/libxxxx.so" to
"/usr/local/lib/libxxxx.so": Permission denied.

There are mostly because of one of the two reasons below:

  1. cmake did not find the otawa-config. Normally, you get an error message when generating makefile with “cmake .” To be certain, you can find in the CMackCache.txt that the variable OTAWA_CONFIG is not initialized. You may want try to remove the CMakeCache.txt and give cmake another chance to find otawa-config it-self. Otherwise, you should define manually with -DOTAWA_CONFIG.
  2. cmake did find the otawa-config: if you get the message 'otawa-config at PATH_TO_OTAWA_CONFIG'. First, try

otawa-config –plugdir

if it gives the correct path, check your CMakeList.txt if you have set your library as SHARED:
 //add_library				("${PLUGIN}" SHARED ${SOURCES})//

Debugging and using the plug-in

Last steps consist in testing and using the plug-in.

Testing

operform is the tool you need to test it. The command below launch the plug-in and all its dependencies:

$ operform bs.elf process:simple_time::TimeBuilder

As output, you will get the CFG expressed in XML as below. The set properties are displayed and particularly otawa::ipet::TIME that allows to check if simple_time::TimeBuilder has worked as expected:

<?xml version="1.0" encoding="UTF-8"?>
<cfg-collection>
  <cfg id="_0" address="0x00008218" label="main" number="0">
    <entry id="_0-0"/>
    <bb id="_0-1" number="1" address="0x00008218" size="20">
      <property identifier="otawa::ipet::TIME">5</property>
    </bb>
    <bb id="_0-2" number="2" address="0x0000822c" size="8">
      <property identifier="otawa::ipet::TIME">2</property>
    </bb>
    <bb id="_0-3" number="3" call="_1"/>
    <exit id="_0-4"/>
    <edge source="_0-0" target="_0-1"/>
    <edge source="_0-1" target="_0-3"/>
    <edge source="_0-2" target="_0-4"/>
    <edge source="_0-3" target="_0-2"/>
  </cfg>
  <cfg id="_1" address="0x00008234" label="binary_search" number="1">
    <entry id="_1-0"/>
    <bb id="_1-1" number="1" address="0x00008234" size="44">
      <property identifier="otawa::ipet::TIME">11</property>
    </bb>
    <bb id="_1-2" number="2" address="0x00008304" size="16">
      <property identifier="otawa::ipet::TIME">4</property>
    </bb>
    <bb id="_1-3" number="3" address="0x00008270" size="44">
      <property identifier="otawa::ipet::TIME">11</property>
    </bb>
    <bb id="_1-4" number="4" address="0x000082cc" size="24">
      <property identifier="otawa::ipet::TIME">6</property>
    </bb>
    <bb id="_1-5" number="5" address="0x000082f4" size="16">
      <property identifier="otawa::ipet::TIME">4</property>
    </bb>
    <bb id="_1-6" number="6" address="0x00008260" size="16">
      <property identifier="otawa::ipet::TIME">4</property>
    </bb>
    <bb id="_1-7" number="7" address="0x000082e4" size="16">
      <property identifier="otawa::ipet::TIME">4</property>
    </bb>
    <bb id="_1-8" number="8" address="0x0000829c" size="48">
      <property identifier="otawa::ipet::TIME">12</property>
    </bb>
    <exit id="_1-9"/>
    <edge source="_1-0" target="_1-1"/>
    <edge source="_1-1" target="_1-6"/>
    <edge source="_1-2" target="_1-9"/>
    <edge source="_1-3" target="_1-4"/>
    <edge source="_1-3" target="_1-8"/>
    <edge source="_1-4" target="_1-5"/>
    <edge source="_1-4" target="_1-7"/>
    <edge source="_1-5" target="_1-6"/>
    <edge source="_1-6" target="_1-2"/>
    <edge source="_1-6" target="_1-3"/>
    <edge source="_1-7" target="_1-6"/>
    <edge source="_1-8" target="_1-6"/>
  </cfg>
</cfg-collection>

Using

A classic use of analyses in a plug-in is their invocation from a script. For example, the script below will:

  1. Launch simple_time;;TimeBuilder analysis
  2. Use its result in order to compute the WCET using IPET method.

The script simple_time.osx is:

<?xml version="1.0"?>
<otawa-script>
 
	<name>simple_time</name>
	<info>
		Test script for simple_time plug-in.
	</info>
 
	<id>
		<arch>arm</arch>
	</id>
 
	<script>
		<step processor="simple_time::TimeBuilder"/>
		<step require="otawa::ipet::WCET_FEATURE"/>
	</script>
 
</otawa-script>

To run it, we have just to type:

$ owcet -s ./simple_time.osx bs.elf
INFO: plugged simple_time (.../lib/otawa/simple_time.so)
WCET[main] = 295 cycles

Now, the procedure described can be repeated for any plug-in you want to design with any number of analyses/code processors, features or properties inside.

Tips about the .eld file

Linking a plug-in is sometimes tricky: it may require several dependencies like other dynamic libraries or other plugins. The .eld file is used to let OTAWA to prepare the environment for your plug-in. The following definitions may be helpful and added to .eld:

  • deps=PLUG1;PLUG2;… – declares that the current plug-in depends on the listed ones that will be plugged before the current one.
  • libs=LIB1;LIB2;… – declares a list of dynamic libraries used by the plug-in and will be linked before loading the plug-in.
  • path=PATH – path to the plug-in dynamic library if it is not named as the .eld file.

In addition, these definitions are also used by otawa-config to generate linking options for the compiler.

FAQ

Q: I get the error “ERROR: cannot resolve *otawa::my-plugin::MyProcessor*”.

A: First, Check if your plugin is correctly installed: by default, plugins are installed at OTAWA_HOME_PATH/lib/otawa/otawa, you must find my-plugin.so and my-plugin.eld. If both files are present, check if you have invoked perform with the correct plugin name and processor name, for example, if you have invoked

$ operform binary.elf process:otawa::my-plugin::MyProcessor

then you should have declared the register of your processor otawa::my-plugin::MyProcessor, for example, if MyProcessor does not provide/require any feature:

p::declare MyProcessor::reg = p::init("otawa::xdd::MyProcessor", Version(1,0,0));

Q: I get the error “ERROR: cannot find processor my-plugin::MyProcessor”.

A: You should have a class MyProcessor that inherited from otawa::Processor and have registered it correctly as described in the previous question.

Q: I get the error “invalid plugin found at “OTAWA_HOME_PATH/lib/otawa/otawa/my-plugin.so” (no handle): another-plugin.so: cannot open shared object file: No such file or directory”

A. That often because your plugin depends on another plugin and the latter has not been loaded correctly. It can be confirmed by:

$ ldd my-plugin.so

if some dynamic libraries of plugins are marked not found, then add those dependencies into the your-plugin.eld file in your project and compile and install your project again.