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:
/* * simple_time -- OTAWA plug-in assigning 1 cycle/instruction. */ #ifndef SIMPLE_TIME_H #define SIMPLE_TIME_H #include <otawa/proc/BBProcessor.h> namespace simple_time { using namespace otawa; class TimeBuilder: public BBProcessor { public: static p::declare reg; TimeBuilder(); protected: void processBB(WorkSpace *ws, CFG *g, Block *b) override; }; } // simple_time #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
:
/* * simple_time -- OTAWA plug-in assigning 1 cycle/instruction. */ #include "simple_time.h" #include <otawa/ipet/features.h> #include <otawa/proc/ProcessorPlugin.h> namespace simple_time { /// p::declare TimeBuilder::reg = p::init("simple_time::TimeBuilder", Version(1, 0, 0)) .extend<BBProcessor>() .provide(ipet::BB_TIME_FEATURE) .make<TimeBuilder>(); /// TimeBuilder::TimeBuilder(): BBProcessor(reg) { } /// void TimeBuilder::processBB(WorkSpace *ws, CFG *g, Block *b) { if(!b->isBasic()) return; auto bb = b->toBasic(); ipet::TIME(b) = bb->count(); if(logFor(LOG_BLOCK)) } class Plugin: public ProcessorPlugin { public: Plugin(void): ProcessorPlugin("simple_time", Version(1, 0, 0), OTAWA_PROC_VERSION) { } }; } // simple_time simple_time::Plugin simple_time_plugin; 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
:
# configuration # C++ flags endif() add_compile_options(-Wall) add_compile_options(--std=c++11) else() endif() # script # look for OTAWA endif() endif() OUTPUT_VARIABLE OTAWA_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE OTAWA_PLUGDIR OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE OTAWA_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_VARIABLE OTAWA_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) # plugin definition include_directories ("${CMAKE_SOURCE_DIR}") add_library ("${PLUGIN}" SHARED ${SOURCES}) set_property (TARGET "${PLUGIN}" PROPERTY PREFIX "") set_property (TARGET "${PLUGIN}" PROPERTY COMPILE_FLAGS "${OTAWA_CFLAGS}") target_link_libraries ("${PLUGIN}" "${TARGET_LIB}" "${OTAWA_LDFLAGS}") # installation
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:
- 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.
- 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:
- Launch
simple_time;;TimeBuilder
analysis - 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.