NPR Tutorial >> Writing A Plugin | TOC |
This page gives a recipe for how to write and test your own plugin; it tells you the "How" of writing plugin. The page Tour Of A Simple Plugin covers some of the "Why" and "What" parts of a simple plugin and Tour Of The Delay Plugin covers periodic computations. This page will begin by showing you how to make your own copy of the mycount plugin. Then, it leads you through the steps need to make a new plugin that is a small extension of the mycount plugin. In the example commands below, you will need to particularize the commands for your situation.
We assume below that you have logged into the onlusr host and that you are in your home directory. We will compile your own version of the mycount plugin. This should be straightforward since you will be starting with the source code of a predefined plugin and therefore should not be getting any compiler errors.
mkdir npr-plugins # your NPR plugin directory cd npr-plugins mkdir mycount # your own mycount plugin directory
cd mycount cp ~onl/npr/plugins/mycount/{mycount.c,plugin_helpers.h,Makefile} .
make > make.log 2>&1 # bash make >& make.log # csh
egrep 'error|warn' make.logYou should see about 10 lines that say there are "0 error(s)".
more make.log
There should be files in the subdirectories list/ and uof/. This is an indication that object files and executables were created by the make command. The contents of the uof/ directory should look like this when you enter the command ls uof:
onlusr> ls uof mycount.uof mycount.uof_1_2800 mycount.uof_2_2805 mycount.uof_4_2800 mycount.uof_0_2800 mycount.uof_1_2805 mycount.uof_3_2800 mycount.uof_4_2805 mycount.uof_0_2805 mycount.uof_2_2800 mycount.uof_3_2805
The next section will show you how to load and test this plugin. This plugin will use eight hardware-supported threads: six packet handling threads, one control message thread and one periodic computation thread. Actually, the periodic computation thread doesn't do anything useful. The underlying ONL plugin framework used by the six packet handling threads ensure that packets are handled in FIFO order even though the threads execute concurrently. The concurrency occurs because of the structure of the main() function and the settings in the Makefile. The details are described in the next tutorial page Tour Of A Simple Plugin.
Now you need to test your own mycount plugin. We'll use the 1-NPR configuration (above). We will install a filter at input port 1.0 that directs traffic to a mycount plugin running on plugin microengine 0. Everytime a meta-packet arrives to the plugin, the plugin will increment its two packet counters and then forward the meta-packet to output port 1 for transmission. If the plugin works, we should be able to send packets through the plugin at a low rate using the ping command and see that:
$n1p0> ping -c 5 n1p1
Note that even though there is a predefined mycount plugin directory at ~onl/npr/plugins/mycount/, we will be putting our personal version in a subdirectory of our home directory; i.e., ~/npr-plugins/mycount/.
The NPR.1 Plugin Table will appear.
The Add Plugin Directory window will appear.
Note: The pathname should be the full pathname. For example, if I put the mycount source code in the directory /users/MYLOGIN/npr-plugins/mycount/, I would enter: /users/MYLOGIN/npr-plugins/.
If you now select Edit => Add Plugin in the Plugin Table, the RLI should display all predefined plugins as well as the plugins in your personal plugin directory.
An incomplete entry into the Plugin Table appears. Usually, the microengine number (second) field contains the wrong value.
Note: The figure (right) has been edited to reduce the image footprint. It shows that there are only two predefined plugins (mycount and stringSub). In fact, you will see over 10 predefined plugins if you are following these steps.
We could have chosen any plugin microengine number (0, 1, 2, 3, or 4).
A default filter table window will appear.
All packets coming into port 1.0 will first be directed to plugin ME 0 (output plugins: 0) and then move on to a datagram queue at output 1 (qid=0 indicates that we will use a hash function that maps to one of the queues numbered from 0 to 63).
$n1p0> ping -c 5 n1p1
A dialogue box should appear.
A command log window should appear with a line containing the output shown right. The number corresponds to the value of the plugin variable pkt_count. Since you just sent five ping packets from host $n1p0 spaced one second apart, the plugin should have seen five packets (pkt_count = 5).
Recall that the mycount plugin keeps the packet count in both the pkt_count variable and PluginCounter 0. The difference is that the value of PluginCounters is visible for charts through the NPR menu item Monitoring => PluginCounter.
The Add Parameter dialogue box will appear.
The absolute value of PluginCounter 0 will be displayed. It will start at 0 and increment for each packet received by the plugin.
The PluginCounter 0 chart will show that the value of the counter is now 0. You can verify that the pkt_counter counter has been zeroed by sending the g command to the plugin.
We will now test the plugin debug message facility. Debug messages are recorded in a log file specified by the user if the user tells the RLI where to record debug messages and the plugin calls the onl_api_debug_message() function. In the mycount plugin, the onl_api_debug_message() function is only called if the debug_on variable is 1 (the default value).
A Send Command PluginDebugging dialogue box appears.
Note 1: The RLI does not understand shell metacharacters (e.g., "~", "."). So, the RLI will reject ~/debug.log.
Note 2: There may be a small time lag between the time that the message is generated and when it appears in the log file since the file is on a remotely mounted file system on most ONL hosts.
The log file shows that five packets were handled by the plugin and that the messages were logged about every 1 second. Notice that the packet numbers are preceded by the character 'x' which indicates that the number is a hexadecimal number.
The value of the plugin variable debug_on will change from 1 to 0 so that the plugin will no longer attempt to log debug messages.
The value of the plugin variable debug_on will change from 0 back to 1 so that the plugin will resume logging debug messages.
We now address the issue of really creating your own plugin. This section shows you that the mycount Makefile needs to be updated to reflect the name of the new plugin. The next section will lead you through making a few changes to the mycount source code.
cd .. # the root plugin directory mkdir zzz # create the directory for zzz cp ./mycount/mycount.c zzz/zzz.c # copy and rename the .c file cp ./mycount/plugin_helpers.h zzz # copy the plugin_helpers.h file cp ./mycount/Makefile zzz # copy the Makefile file
NAME=mycount ... SRCS=mycount.cBut they should be changed to:
NAME=zzz ... SRCS=zzz.c
make > make.log 2>&1 # bash make >& make.log # csh
egrep 'error|warn' make.log more make.log # for more detailsYou should see about 10 lines that say there are "0 error(s)". If not, then, you made a mistake.
Are you still awake?
Now, let's make a few simple modifications to the zzz plugin:
In the explanation to follow, you will see the following examples that are specific to the IXP programming environment (and therefore, may appear unusual) and to the ONL plugin framework:
The only change required here is to add the declaration of the new variable total_count.
// my global declarations __declspec(shared gp_reg) unsigned int pkt_count; __declspec(shared gp_reg) unsigned int total_count; <<<<< __declspec(shared gp_reg) unsigned int debug_on;
The only change required is to inialize the value of total_count to 0.
void plugin_init_user() { if(ctx() == 0) // only thread 0 should do the initialization { pkt_count = 0; total_count = 0; <<<<< debug_on = 1; sleep(timeout); helper_plugin_cntr_zero( 0 ); } }
The handle_pkt() function is called whenever one of the six packet handling threads gets a packet. That function calls handle_pkt_user() to do user-specific processing.
If we want to output a debug message any time that the value of total_count is incremented to a value that is evenly divisible by 10, we will have to modify the handle_pkt_user() function so that it:
1 void handle_pkt_user() { 2 __declspec(local_mem) char dbgmsg[28] = "got pkt x"; 3 __declspec(local_mem) char pkt_count_str[9]; 4 __declspec(local_mem) char space_str[2] = " "; <<<<< 5 ++pkt_count; 6 ++total_count; <<<<< 7 if( debug_on ) { 8 onl_api_int2str( pkt_count, pkt_count_str ); 9 strcat_lmem(dbgmsg, pkt_count_str); 10 if( total_count%10 == 0 ) { <<<<< 11 strcat_lmem( dbgmsg, space_str ); <<<<< 12 onl_api_int2str( total_count, pkt_count_str ); <<<<< 13 strcat_lmem(dbgmsg, pkt_count_str); <<<<< 14 } <<<<< 15 onl_api_debug_message(msgNextBlock, dbgmsg); 16 } 17 onl_api_plugin_cntr_inc(pluginId, 0); // Incr global plugin cntr 0 18 }
Lines 6, 10 and 14 should be obvious. Line 4 declares space_str[2] to be the C-string containing the space character followed by the null byte and resides in local memory. Lines 12-13 look like lines 8-9 and concatenates the hexadecimal ASCII representation of total_count to the debug message after appending a space character. (Note that we said hexadecimal, not decimal.) Tour Of A Simple Plugin explains the purpose for the onl_api_int2str() function.
The handle_msg() function is called whenever the control message handling thread gets a control message from the Xscale control processor. These messages are generated as a result of the RLI Plugin Table menu item Edit => Send Command to Plugin. The mycount plugin recognizes three commands: z(ero) counts, toggle d(debug) flag, and g(et) counts.
We want to modify handle_msg() so that whenever it gets the g control message, it will reply with both the value of pkt_count and total_count. This is a simple change since the idea is similar to the debug message changes that we made to handle_pkt_user() except that the counter values will be in decimal format instead of hexadecimal format.
1 void handle_msg() 2 { 3 // assume messages are at most 8 words for now 4 ... declarations deleted ... 5 __declspec(local_mem) char space_str[2] = " "; <<<<< 6 ... code deleted ... 7 dl_source_message( msgFromBlock, message ); 8 ... code deleted ... 9 if( in_msgstr[0] == 'z' ) { 10 ... code deleted ... 11 } else if( in_msgstr[0] == 'g' ) { 12 helper_ultodec_lmem( pkt_count, pkt_count_str ); 13 strcpy_lmem( out_msgstr, pkt_count_str ); 14 strcat_lmem( out_msgstr, space_str( ) ); <<<<< 15 helper_ultodec_lmem( total_count, pkt_count_str ); <<<<< 16 strcat_lmem( out_msgstr, pkt_count_str ); <<<<< 17 if( onl_api_str2intarr(out_msgstr, &message[1]) < 0 ) { return; } 18 } else { ... code deleted ... } 19 20 ... code deleted ... 21 dl_sink_message(msgNextBlock, message); 22 }
These changes are straightforward since lines 15-16 are the total_count counterparts to those for pkt_count in lines 12-13. Note though that lines 12 and 15 use the helper_ultodec_lmem() function instead of the onl_api_int2str() function used in handle_pkt_user(). That's because helper_ultodec_lmem() converts from unsigned long to ASCII decimal using local memory parameters instead of to hexadecimal.
You should now be able to test the new zzz plugin in much the same manner that you tested the mycount plugin earlier. The only difference is that now there will be an extra field in the output of the g command and there will be extra debug messages due to the total_count variable.
Revised: Thu, Nov 13, 2008
NPR Tutorial >> Writing A Plugin | TOC |