- Status: open --> closed-out-of-date
From Al:
After last weeks plugin discussion we had, I had a bright idea to hack
up one way the cli plugins might be approached. This email is intended
to document that idea.
The reason I attempted this was because the GUI does this for every
panel. The GUI creates
plugin commands all the time. It really doesn't make much difference
if gui-ism (clicketty stuff)
stuff is done or not. They're commands.
So, I took an approach and hacked up a little test case: In my test
case I have a plugin handler that goes-a-lookin' for cli plugins when
the cli is initialized. The plugin handler dlopens all the dso's
looking for a special entry point, cli_plugin_init(). If the entry
point exists, it adds it to a cli plugin registry and continues until
all the files are interrogated. Simple enough.
Then when a command is typed in by the user, the first thing the cli
hook (I added) looks through the registry to see if the command is a
plugin. If it is, it calls the plugin entry point with the user's
command and the command is processed. If it's not in the registry the
command is processes just as it is now.
Simple enough.
After all that, all the user needs to do to add a new command is the add
a new class.
I've attached example of 3 plugin commands: ExpExtraFancy.cxx, q.cxx,
and frap.cxx (Obviously there's a bit more to it... i.e. a header file
and simple make file...)
o ExpExtraFancy makes calls to the framework directly... looking
for the number of threads attached to an experiment. All it
does is print the number of threads in the thread group for
experiment 1.
o q makes calls to the cli. It simply calls the cli with the
"exit" command.
o frap is basically a 'list' command. It takes the first argument,
does a simple minded strcmp-ism, and calls the cli with the
appropriate command.
Here's a simple scenario:
Script started on Tue 11 Oct 2005 03:03:08 PM CDT
gjoe: openss -cli
cli_init() entered
Welcome to Open|SpeedShop, version 0.1.
Type 'help' for more information.
openss>>help
Open|SpeedShop is a performance tool brought^M
to you by SGI.
Here are the available help topics:
commands -> Lists all available commands
grammar -> Lists command grammar elements
experiments -> Lists available experiments
man -> How to access the Open|SpeedShop man page
terms -> Lists general Open|SpeedShop nomenclature
To get details for a particular topic,
type "help <topic>".
For instance, to get help list of the topic "commands"
openss>>help commands
To get help on an individual command type, such as "expCreate"
openss>>help expCreate
openss>>frap pids
frap::parseCmd(frap pids) entered
frap::doCmd(frap pids) entered
There is no focused experiment
openss>>ExpExtraFancy
ExpExtraFancy::parseCmd(ExpExtraFancy ) entered
ExpExtraFancy::doCmd(ExpExtraFancy ) entered
No experiment yet defined.
openss>>expCreate -f fred pcsamp
Found symtab
The new focused experiment identifier is: -x 1
openss>>ExpExtraFancy
ExpExtraFancy::parseCmd(ExpExtraFancy) entered
ExpExtraFancy::doCmd(ExpExtraFancy) entered
You have 1 threads defined.
openss>>expGo
Start asynchronous execution of experiment: -x 1
openss>>
openss>>expStatus
Experiment definition
{ # ExpId is 1, Status is Running, Temporary database is
/tmp/ssdb1fvHa0J.openss
Currently Specified Components:
-h lnx-stipek -p 2701 pcsamp
Metrics:
pcsamp::time
Parameter Values:
pcsamp::sampling_rate = 100
Available Views:
pcsamp
}
openss>>Usage:
/home/stipek/OpenSpeedShop/current/usability/phaseII/fred <size>
No size argument given. Defaulting to 750.
/home/stipek/OpenSpeedShop/current/usability/phaseII/fred: succesfully
completed.
Experiment 1 has terminated.
openss>>
openss>>frap pids
frap::parseCmd(frap pids) entered
frap::doCmd(frap pids) entered
2701
openss>>q
Just created the class q(q)
q::parseCmd(q) entered
q::doCmd(q) entered
gjoe:
This is live data (well with some formatting changes to make it look
better in the email).
FWIW,
Al
[ text/plain ] :
#include "ExpExtraFancy.hxx"
#include "cli_plugin_entry_point.hxx"
#include "SS_Input_Manager.hxx"
ExpExtraFancy::ExpExtraFancy() : CmdClass()
{
cmd_name = "ExpExtraFancy";
// printf("Just created the class ExpExtraFancy(%s)\n", cmd_name.c_str());
}
ExpExtraFancy::~ExpExtraFancy()
{
}
void
ExpExtraFancy::parseCmd(const char *command)
{
printf(" ExpExtraFancy::parseCmd(%s) entered\n", command);
}
void
ExpExtraFancy::doCmd(const char *command)
{
printf(" ExpExtraFancy::doCmd(%s) entered\n", command);
int expID = 1;
try
{
std::set<Statement>::const_iterator di = NULL;
ExperimentObject *eo = Find_Experiment_Object((EXPID)expID);
if( eo == NULL )
{
fprintf(stderr, "No experiment yet defined.\n");
return;
}
Experiment *fw_experiment = eo->FW();
ThreadGroup tgrp = fw_experiment->getThreads();
ThreadGroup::iterator ti = tgrp.begin();
printf("You have %d threads defined.\n", tgrp.size() );
if( tgrp.size() == 0 )
{ // No threads to look up the data...
return;
}
}
catch(const std::exception& error)
{
std::cerr << std::endl << "Error: "
<< (((error.what() == NULL) || (strlen(error.what()) == 0)) ?
"Unknown runtime error." : error.what()) << std::endl
<< std::endl;
return;
}
}
[ text/plain ] :
#include "q.hxx"
#include "cli_plugin_entry_point.hxx"
#include "SS_Input_Manager.hxx"
q::q() : CmdClass()
{
cmd_name = "q";
printf("Just created the class q(%s)\n", cmd_name.c_str());
}
q::~q()
{
}
void
q::parseCmd(const char *command)
{
printf(" q::parseCmd(%s) entered\n", command);
}
#include "Commander.hxx"
void
q::doCmd(const char *_command)
{
printf(" q::doCmd(%s) entered\n", _command);
char *command = "exit";
InputLineObject *clip = Append_Input_String( 1, (char *)command);
}
[ text/plain ] :
#include "frap.hxx"
#include "cli_plugin_entry_point.hxx"
#include "SS_Input_Manager.hxx"
frap::frap() : CmdClass()
{
cmd_name = "list";
// printf("Just created the class frap(%s)\n", cmd_name.c_str());
}
frap::~frap()
{
}
void
frap::parseCmd(const char *command)
{
printf(" frap::parseCmd(%s) entered\n", command);
}
void Default_TLI_Command_Output(CommandObject *C)
{
printf("CmdPanel::callback(well sort of) entered\n");
}
void Default_TLI_Line_Output( InputLineObject *clip)
{
printf("Default_TLI_Line_Output() entered\n");
}
void
frap::doCmd(const char *_command)
{
printf(" frap::doCmd(%s) entered\n", _command);
const char *argument = _command + cmd_name.size() + 1;
// char *command = "listPids";
std::string command_argument(argument);
char *command = NULL;
if( command_argument == "pids" || command_argument == "p" )
{
command = "listPids";
} else if( command_argument == "types" || command_argument == "v" )
{
command = "listView";
} else if( command_argument == "experiments" || command_argument == "exp" ||
command_argument == "expr" || command_argument == "e" )
{
command = "listExp";
} else if( command_argument == "types" || command_argument == "t" )
{
command = "listTypes";
} else if( command_argument == "status" || command_argument == "stats" ||
command_argument == "s" )
{
command = "listStatus";
} else
{
printf("Unknown argument : usage 'list list-type'\n");
return;
}
// InputLineObject *clip = Append_Input_String( 1, (char *)command, NULL,
&Default_TLI_Line_Output, &Default_TLI_Command_Output);
InputLineObject *clip = Append_Input_String( 2, (char *)command, NULL,
&Default_TLI_Line_Output, &Default_TLI_Command_Output);
}
==========================
ADDITIONAL INFORMATION (ADD)
From: jcarter@sgi.com (BugWorks)
Date: Oct 12 2005 07:29:39AM
==========================
From Jack:
If we have a command plugin:
Does it get recorded in HISTORY?
When does it get recorden in HELP?
How does scripting handle it? I can't call the
parser directly because I won't know which parser
to call. I'll need to call a preparser, but how
does it know which command to use in case of name
collision?
How do I "plugin" permanent command
documentation. User's Guide, Manpage, etc.
This is going to come up if we want to use
plugins for the Pro version.
==========================
ADDITIONAL INFORMATION (ADD)
From: jcarter@sgi.com (BugWorks)
Date: Oct 12 2005 07:30:53AM
==========================
From Dave:
>
> If we have a command plugin:
>
> Does it get recorded in HISTORY?
Every Command is added to the HISTORY list when it is returned from
SpeedShop_ReadLine, which is the (indirect) call that Python makes to
get a new line. I mean EVERY command - all Python commands, as well as
OpenSS commands. We really don't care what it is, as long as we pass it
to Python. Therefor, all "plugin commands" will be recorded - if they
go through the normal read mechanism.
Of course, none of the scripting commands will be recorded, because they
do not go through SpeedShop_ReadLine. We can devise something else, if
we want to support the HISTORY command for the scripting environment.
It's not obvious to me that we want to, but there is a similar point at
which we could log the OpenSS commands that the user passes in.
>
> When does it get recorden in HELP?
Yet to be determined.
>
> How does scripting handle it? I can't call the
> parser directly because I won't know which parser
> to call. I'll need to call a preparser, but how
> does it know which command to use in case of name
> collision?
To re-phrase the question: How could we support plugin commands for the
scripting user? Because we have a different user interface, I think we
may need to require two, different plugins, to support both connections.
Makes me wonder if we want to do it.
On the other hand, if we chose to implement plugins with Python code, we
could supply the same code for the scripting user. They would need to
invoke the plugin directly, in their Python code, and the plugin would
link to the scripting interface. You are correct - it may involve doing
some parsing in the plugin.
>
> How do I "plugin" permanent command
> documentation. User's Guide, Manpage, etc.
> This is going to come up if we want to use
> plugins for the Pro version.
Good question.