Plugin architecture
Voltron uses a plugin architecture for implementing debugger host suppprt, API methods, and UI views. Much of the core of Voltron is implemented using this plugin architecture, and it can also be used to extend Voltron to support new debugger hosts and add new custom API methods and views.
Types of plugins
There are four types of plugins supported in Voltron.
View plugins
View plugins provide views that can be run from the voltron
command-line entry point. Typically these are, as are the included views, terminal-based. These plugins define a view class, which is instantiated and run by the command-line entry point program. See the included views in voltron/plugins/views/*.py
.
More information on building view plugins can be found here.
API plugins
API plugins implement an API method which is accessible via the various configured listeners. These define a request and response class, and interact with a package-wide instance of one of the above debugger adaptor plugins. When an incoming API request is received, its request
field is used to look up the appropraite API plugin to handle the request. See the included core API plugins in voltron/plugins/api/*.py
.
More information on building API plugins can be found here.
Web plugins
Web plugins provide views or other features that are accessible via the embedded web server. They typically define a Flask app or just provide a directory of static which is served by the embedded web server, and use JavaScript to talk to the API back end. No web plugins are included in the core at the moment, but see the examples
directory.
More information on building web plugins can be found here.
See also the voltron-web package, which implements a basic web UI for Voltron that is useful for testing API requests.
Debugger adaptor plugins
Debugger adaptor plugins implement the majority of the support for a given debugger host platform. There are two core debugger adaptor plugins included with Voltron which provide support for LLDB and GDB. These plugins both implement a common set of methods that are used by the core API plugins. If you want to add support for a new debugger host, you'll need to look at the code for one of these plugins in voltron/plugins/debugger/{dbg_lldb|dbg_gdb}.py
and implement the same methods. They are reasonably well-documented and should provide a reasonable starting point.
More information on building debugger adaptor plugins can be found here.
Anatomy of a plugin
The bare minimum for a Voltron plugin is a Python module containing a subclass of one of the plugin classes - APIPlugin
, DebuggerAdaptorPlugin
, ViewPlugin
or WebPlugin
(see voltron/plugin.py
). The plugin class is the top-level object in a plugin. It usually has a name and references to other resources in the plugin. For example an APIPlugin
has a name like wait
, and references to an APIRequest
subclass and an APIResponse
subclass used to handle the API request. This is what a sample API plugin class might look like:
class SampleAPIPlugin(APIPlugin):
name = 'sample'
request_class = SampleAPIRequest
response_class = SampleAPIResponse
The plugin class itself typically does not contain any methods, it is just used as the top-level object to reference a plugin. The common parent of all the plugin classes, Plugin
(in Scruffy) utilises a metaclass, PluginRegistry
, to collect all its subclasses as they're loaded by Scruffy. Voltron implements its own PluginManager
class which is used as an interface to the PluginRegistry
.
Installing plugins
User plugins directory
When Voltron is loaded into the debugger, the Scruffy environment is initialised and plugins are discovered in two locations: the plugins
directory within the voltron
package, and the user plugins directory in ~/.voltron/plugins
. The latter is where custom plugins should be installed. These directories are searched recursively, and any Python module discovered within is loaded. Classes in these modules that inherit from one of the Voltron plugin classes will be recognised and loaded into the plugin registry.
Registering programmatically
Plugins can also be loaded and registered programmatically. This may be useful for custom clients that aren't using the standard view architecture, or for loading plugins from the debugger's init script.
After Voltron has been loaded into the debugger, a package-wide shared instance of the PluginManager
class will be available at voltron.plugin.pm
. This instance can be used to programmatically register plugins like so:
from myplugin import MyAPIPlugin, MyAPIRequest, MyAPIResponse
voltron.plugin.pm.register_plugin(MyAPIPlugin)
This will make an instance of the plugin class available through the shared PluginManager
instance.
Accessing plugins
The shared PluginManager
instance voltron.plugin.pm
can also be used to access plugins programmatically from your custom plugins or Voltron clients. There are convenience methods in voltron/plugin.py
to make this easier.
For example, to instantiate an API request:
req = api_request('disassemble', count=16)
An API response:
res = api_response('disassemble', disassembly="push r...")
To allocate a view class:
view = view('register')
To allocate a new debugger adaptor instance (this would only really be useful if you were building another tool on top of Voltron):
adaptor = debugger_adaptor('lldb')
If you want to access the plugin classes themselves directly, you can call the PluginManager
methods:
plugin = voltron.plugin.pm.api_plugin_for_request('wait')
plugin = voltron.plugin.pm.debugger_plugin_for_host('lldb')
plugin = voltron.plugin.pm.view_plugin_with_name('register')
plugin = voltron.plugin.pm.web_plugin_with_name('angularview')
The package-wide debugger adaptor is located at voltron.debugger
. This can be used from your custom API plugins to query the debugger host back end. See some of the core API plugins and the included LLDB and GDB host adaptor plugins for usage.