How to instrument an existing binary application

1. Prerequisites

  • If you are a new OMF user, make sure that you have read and understood at least the first couple of tutorials on our basic tutorial pages

2. Goal

How to write an OML Ruby application to wrap around another application?

As a user, you want to use applications in your experiments and instruments them to collect measurement while they are running. However sometime, you do not have the source code of these applications, and only have their executable binary.

If these applications produce some outputs on standard-out while they run, then you can capture these outputs as measurements and collect them using OML. In order to do that you need to:

  • A - create a wrapper around your existing binary application, which will capture its output and send them to an OML collection server
  • B - create an OMF Application Definition for this wrapper, which will allow you to use that wrapper in an OMF Experiment Description
  • C - use that wrapper in your experiment

This tutorial shows you how to realise these steps.

3. Scenario

The experiment scenario and system overview for this tutorial are illustrated in the following figure:

We will use the application wlanconfig has the example of a binary application to instrument. When run with the command wlanconfig ath0 list, this application writes (on its standard output) a line with the MAC address of any WIFI device that the interface ath0 has detected, followed by various statistics about the link quality towards that device. We will capture these lines as our measurements.

4. Step A - Creating a Wrapper around the application binary wlanconfig

  • We use the Ruby language to create the wrapper application called wlanmonitor-simple.rb
  • The complete Ruby source code (70 lines without comments) for this wrapper is attached here: wlanmonitor-simple.rb
  • First we make sure that our wrapper is using the oml4r library which is part of the OML Library
    • If you've installed OML from Debian/Ubuntu packages, you can find it at /usr/share/liboml2-dev/oml4r.rb. Copy or symlink it to the path where you write your wrapper, or set the Ruby environment to include the path to the library.
    • If you've compiled OML from source, the library is at /usr/share/oml2/oml4r.rb
    • The first lines of the wrapper has a require statement:
1 require "oml4r" 
  • Then we define the schema of the Measurement Point we would like to create
    • here we call our Measurement Point MPStat and it is of the class OML4R:MPBase
    • this is the description of each sample that you would like to collect and store
    • this is also the description of the schema for the database table in each your samples will be stored
 1 class MPStat < OML4R::MPBase
 2     name :wlanstat
 3     param :src_addr
 4     param :dst_addr
 5     param :aid, :type => :long
 6     param :channel, :type => :long
 7     param :rate
 8     param :rssi, :type => :long
 9     param :dbm, :type => :long
10     param :idle, :type => :long
11     param :txseq, :type => :long
12     param :rxseq, :type => :long
13     # wlanconfig returns other metrics which we ignore here
14 end
  • Then we define the class for our Wrapper, we call it Wrapper:
1 class Wrapper
2 ...
3 end
  • Inside that Wrapper class we define a Initialize method
    • this method parses the command line options for our wrapper application
    • this method also initialises the OML4R Library to allow future measurement to be forwarded to it
    • Line 2-3: initialise some variables for our wrapper
    • Line 4-8: initialise the OML4R Library
      • Line 4: appID is the name of your application (wlanconfig here), which will also be the used as a prefix to name the resulting database table
      • Line 6-7: we define the command line options for our wrapper and store their values into some variables
 1   def initialize(args)
 2     @interface = nil
 3     @interval = 1
 4     OML4R::init(args, :appID => "wlanconfig") do |argParser|
 5       argParser.banner = "\nExecute a wrapper around wlanconfig\n Use -h or --help for a list of options\n\n" 
 6       argParser.on("-i", "--interface IFNAME","Name of Interface to monitor") { |name| @interface = name }
 7       argParser.on("-s", "--sampling DURATION", "Interval in second between collected samples") { |time| @interval = time }
 8     end
 9     unless @interface != nil
10       raise "You did not specify an interface to monitor! (-i option)" 
11     end
12   end
  • Still inside that Wrapper class we define a process_output method
    • this method parses each lines that the wlanconfig application writes on its standard output
    • it constructs the measurement sample to send for OML collection using the information from the parsed lines
    • Line 2: turn the output of wlanconfig into an array where each entry is an line of text
    • Line 3: delete the first line which contains only label information
    • Line 4-9: for each line
      • we parse the line using white-space as separator
      • we construct a measurement sample using selected columns in that line
      • we inject that measurement sample into the measurement Point which we defined earlier
 1   def process_output(output)
 2     lines = output.split("\n")
 3     labels = lines.delete_at(0)
 4     lines.each { |row|
 5       column = row.split(" ")
 6       MPStat.inject("#{column[0]}", column[1], column[2],
 7                     "#{column[3]}", column[4], column[5],
 8                     column[6], column[7], column[8])
 9     }
10   end
  • Still inside that Wrapper class we define a start method
    • this method will run the wlanconfig command and process its output, in an infinite loop
    • Line 3-4: run the command line "/usr/sbin/wlanconfig <interface> list"
    • Line 5: call the process_output method on the output of that command
    • Line 6: sleep for some time and loop again
1   def start()
2     while true
3       cmd = "/usr/sbin/wlanconfig #{@interface} list" 
4       output = `#{cmd}`
5       process_output(output)
6       sleep @interval.to_i
7     end
8   end
  • Finally, outside the Wrapper class definition, we write the code that will start this wrapper application
1 begin
2   app = Wrapper.new(ARGV)
3   app.start()
4 rescue Exception => ex
5   puts "Received an Exception when executing the wrapper!" 
6   puts "The Exception is: #{ex}\n" 
7 end
  • The complete Ruby source code (70 lines without comments) for this wrapper is attached here: wlanmonitor-simple.rb
  • Install this wrapper to your experimental resources (e.g. a PC-node)
    • copy this wrapper to the /usr/bin/ directory of your experimental resource
    • If you have OML installed on your resource, the OML4R library should be present there as well, if not, copy the oml4r.rb file to the same /usr/bin directory
    • OMF provides a way for automatically performing this installation, to keep this tutorial short we are not describing it here, have a look at the tutorial on "How to install your own application on your resources within an experiment"

5. Step B - Create an OMF Application Definition for this wrapper

  • As explained on the usage overview page, to be able to use an application (such ash the above wrapper) in you OMF experiment, you need to write an OMF Application Definition for it
  • Here is the source code for the OMF Application Definition for the above wrapper. Again, please refer to the mentioned tutorial page to get a full explanation of this source code. This Application Definition is also attached here: wlanmonitor_app.rb
 1 defApplication('wlanmonitor_app', 'wlanmonitor') do |a|
 2   a.path = "/usr/bin/wlanmonitor-simple.rb" 
 3   a.version(1, 2, 0)
 4   a.shortDescription = "Wrapper around wlanconfig's list mode" 
 5   a.description = <<TEXT
 6 This is a wrapper around the wlanconfig command used in list mode.
 7 This application is using OML4R part of OML v2.3 or v2.4
 8 TEXT
 9 
10   a.defProperty('interface', 'Interface to listen on', 'i', {:type => :string, :dynamic => false})
11   a.defProperty('sampling', 'Sampling interval in sec', 's', {:type => :integer, :dynamic => false})
12 
13   a.defMeasurement('wlanstat') do |m|
14     m.defMetric('src_addr',:string)
15     m.defMetric('dst_addr',:string)
16     m.defMetric('aid',:long)
17     m.defMetric('channel',:long)
18     m.defMetric('rate',:string)
19     m.defMetric('rssi',:long)
20     m.defMetric('dbm',:long)
21     m.defMetric('idle',:long)
22     m.defMetric('txseq',:long)
23   end
24 end

6. Use the Wrapper in your Experiment

  • Here is the complete Experiment Description for the scenario described above, where the Wrapper application is running on a single resource or node. In this Experiment Description, we also make use of OMF capability to plot graphs at experiment runtime to illustrate the experiment results.
  • This Experiment Definition is also attached here: experiment.rb
 1 # Some experiment properties
 2 defProperty('node', "omf.nicta.node29", "ID of a node")
 3 defProperty('mode1', "adhoc", "wifi mode for 1st node")
 4 defProperty('wifi', "g", "wifi type to use")
 5 defProperty('channel', "1", "wifi channel to use")
 6 
 7 # Define the group of resource that 'observe' the wireless medium
 8 defGroup('Observers', property.node) {|node|
 9   node.addApplication("wlanmonitor_app") {|app|
10     app.setProperty('interface', 'ath0')
11     app.setProperty('sampling', 3)
12     app.measure('wlanstat')
13   }
14   node.net.w0.mode = property.mode1
15   node.net.w0.type = property.wifi
16   node.net.w0.channel = property.channel
17   node.net.w0.essid = "expmonitor" 
18   node.net.w0.ip = "192.168.0.1" 
19 }       
20 
21 # When all resources are up and applications are installed...
22 onEvent(:ALL_UP_AND_INSTALLED) do |event|
23   wait 10
24   allGroups.startApplications
25   wait 300
26   allGroups.stopApplications
27   Experiment.done
28 end
29 
30 # Use the OMF-builtin graph plotting capabilities to plot RSSI
31 # (Received Signal Strength Indication) on the link towards 
32 # the observed wireless devices...
33 addTab(:defaults)
34 addTab(:graph2) do |tab|
35   opts = { :postfix => %{This graph shows the RSSI.}, :updateEvery => 3 }
36   tab.addGraph("RSSI", opts) do |g|
37     data = Hash.new
38     mp = ms('wlanstat')
39     mp.project(:oml_ts_server, :src_addr, :dst_addr, :rssi).each do |sample|
40       time, src, dst, rssi = sample.tuple
41       data[dst] = [] if data[dst] == nil
42       data[dst] << [time, rssi] if src != dst
43     end
44     data.each do |d,v|
45       g.addLine(v, :label => d)
46     end
47   end
48 end

7. Run the experiment

  • Prerequisites:
    • On your resource (i.e. node "omf.nicta.node29"):
      • Your 3rd party application ("wlanconfig") is installed at the path: /usr/sbin/wlanconfig
      • Your Ruby Wrapper with OML support ("wlanmonitor-simple.rb") is installed at: /usr/bin/wlanmonitor-simple
      • The OML 2.4 library (or newer) is installed
      • The OML4R library is either installed (by default with OML) or installed by you at: /usr/bin/oml4r.rb
    • On your machine or testbed console:
      • Your OMF Application Definition ("wlanmonitor_app.rb") is in the same directory as your experiment ("experiment.rb")
  • You can run the above experiment with:
    omf-5.3 exec experiment.rb
    

8. The Results

  • The OMF Experiment Controller (EC) is capable of plotting user-defined graphs at experiment runtime, which are visible on the its runtime webpage.
  • Assuming you ran the above experiment and pointed your web browser to the EC's web page, under the "Graph" tab, you should see a graph similar to this:

9. What is Next?


Tutorial-OMLWrapper.png (40.3 kB) Thierry Rakotoarivelo, 08/31/2010 03:29 pm

wlanmonitor-simple.rb (4 kB) Thierry Rakotoarivelo, 08/31/2010 05:18 pm

wlanmonitor_app.rb (2.4 kB) Thierry Rakotoarivelo, 08/31/2010 05:22 pm

experiment.rb (1.2 kB) Thierry Rakotoarivelo, 09/01/2010 11:37 am

experiment-rssi-graph.png (35.9 kB) Thierry Rakotoarivelo, 09/01/2010 11:37 am