def getMessage(self):
return "Element <" + self.element + "> outside of <" + self.parent + ">"
-class BadName(XMLDataException):
- def __init__(self, attr, val):
- self.attr = attr
- self.val = val
+class MisplacedCData(XMLDataException):
+ def getMessage(self):
+ return "Character data outside of <output> element"
+
+class DuplicateTag(XMLDataException):
+ def __init__(self, element, parent):
+ self.element = element
+ self.parent = parent
def getMessage(self):
- return ("Value of " + self.attr + " not a legal C function name: "
- + self.val)
+ return "Only one <" + self.element + "> allowed in <" + self.parent + ">"
class BadId(XMLDataException):
def __init__(self, id_name, val):
return ("Invalid reference: No " + self.id_name + " with id "
+ self.val)
-class DuplicateHook(XMLDataException):
- def __init__(self, run_name, hook_name):
- self.run_name = run_name
- self.hook_name = hook_name
-
- def getMessage(self):
- return (self.run_name + ": Run contains two hooks with same name, "
- + self.hook_name + ", but different arguments")
-
-# A plug-in includes a source file and the descriptions of each hook
-# that the plug-in might add a call for.
+# A plug-in includes a source file and id.
class Plugin:
def __init__(self, plugin_id, source):
self.plugin_id = plugin_id
self.source = source
- self.hooks = {}
-
-# A hook description comprises the C function name for the hook and
-# the types for all its arguments.
-class Hook:
- def __init__(self, name):
- self.name = name
- self.arg_list = []
-
-class Arg:
- def __init__(self, arg_id, arg_type):
- self.arg_id = arg_id
- self.arg_type = arg_type
# A run has a target source file along with a list of plug-ins that
# should be used when compiling the target and the hooks necessary for
# output that the target is supposed to produce (because of the
# plug-in) when it runs.
class Run:
- def __init__(self, name, target_source):
- self.name = name;
- self.target_source = target_source;
+ def __init__(self, name, target_source, hooks_source):
+ self.name = name
+ self.target_source = target_source
+ self.hooks_source = hooks_source
self.plugin_list = []
- self.hooks = {}
- self.output = []
-
-# A Value is one entry in the output list for a Run object.
-class Value:
- def __init__(self, val_type, val):
- self.val_type = val_type
- self.val = val
-
-# Called for two hooks with the same name, return True if they have
-# _exactly_ the same arguments.
-def hooksMatch(hook1, hook2):
- assert hook1.name == hook2.name
-
- if len(hook1.arg_list) != len(hook2.arg_list):
- return False
-
- for i in range(len(hook1.arg_list)):
- arg1 = hook1.arg_list[i]
- arg2 = hook2.arg_list[i]
- if arg1.arg_id != arg2.arg_id or arg1.arg_type != arg2.arg_type:
- return False
-
- return True
+ self.output = ""
class TestcaseHandler(handler.ContentHandler):
plugins = {}
- current_plugin = None
- current_hook = None
run_list = []
current_run = None
current_call_hook = None
- current_value_arg_id = None
- current_value_cdata = ""
-
- def isAllowedInC(self, token):
- if not re.match('[a-zA-Z_][0-9a-zA-Z_]*', token):
- return False
- elif token == 'if':
- return False
- elif token == 'else':
- return False
- elif token == 'for':
- return False
- elif token == 'do':
- return False
- elif token == 'while':
- return False
- elif token == 'void':
- return False
- elif token == 'unsigned':
- return False
- elif token == 'short':
- return False
- elif token == 'long':
- return False
- elif token == 'int':
- return False
- elif token == 'float':
- return False
- elif token == 'double':
- return False
- else:
- return True
+ current_cdata = ""
+ in_output = False
def startTestcase(self, attrs):
self.name = attrs.get('name')
if source is None:
raise MissingAttr("plugin", "source")
- self.current_plugin = Plugin(plugin_id, source)
-
- def endPlugin(self):
- self.plugins[self.current_plugin.plugin_id] = (self.current_plugin)
- self.current_plugin = None
-
- def startHook(self, attrs):
- if self.current_hook is not None:
- raise MisplacedElement("hook", "plugin");
-
- name = attrs.get('name')
- if name is None:
- raise MissingAttr("hook", "name")
- elif not self.isAllowedInC(name):
- raise BadName("name", name)
-
- self.current_hook = Hook(name)
-
- def endHook(self):
- if self.current_plugin is None:
- raise MisplacedElement("hook", "plugin")
-
- self.current_plugin.hooks[self.current_hook.name] = self.current_hook
- self.current_hook = None
-
- def startArg(self, attrs):
- if self.current_hook is None:
- raise MisplacedElement("arg", "hook")
-
- arg_id = attrs.get('id')
- if arg_id is None:
- raise MissingAttr("arg", "id")
-
- arg_type = attrs.get('type')
- if arg_type is None:
- raise MissingAttr("arg", "type")
-
- arg = Arg(arg_id, arg_type)
- self.current_hook.arg_list.append(arg)
+ self.plugins[plugin_id] = Plugin(plugin_id, source)
def startRun(self, attrs):
- if self.current_plugin is not None:
- raise MisplacedElement("run", "testcase")
-
run_name = attrs.get('name')
if run_name is None:
raise MissingAttr("run", "name")
if target_source is None:
raise MissingAttr("run", "target")
- self.current_run = Run(run_name, target_source)
+ hooks_source = attrs.get('hooks')
+ if hooks_source is None:
+ raise MissingAttr("run", "hooks")
+
+ self.current_run = Run(run_name, target_source, hooks_source)
def endRun(self):
self.run_list.append(self.current_run)
except KeyError as e:
raise BadId("plugin", plugin_id)
- # Add all of this plug-in's hooks to the run.
- for name, hook in plugin.hooks.iteritems():
- dup_hook = None
- try:
- dup_hook = self.current_run.hooks[name]
- except KeyError as e:
- pass
-
- if dup_hook is None:
- # We don't have any hooks with this name.
- self.current_run.hooks[name] = hook
- else:
- # There is already a hook with this name. Make sure
- # it matches.
- if not hooksMatch(hook, dup_hook):
- raise DuplicateHook(self.current_run.name, name)
-
# And the plug-in itself!
self.current_run.plugin_list.append(plugin)
- def startCall(self, attrs):
- if self.current_run is None or self.current_call_hook is not None:
- raise MisplacedElement("call", "run")
+ def startOutput(self, attrs):
+ if (self.current_run is None):
+ raise MisplacedElement("output", "run")
- hook_name = attrs.get('name')
- if hook_name is None:
- raise MissingAttr("call", "name")
+ if (self.current_run.output != ""):
+ raise DuplicateTag("output", "run")
- try:
- hook = self.current_run.hooks[hook_name]
- except KeyError as e:
- raise BadId("hook", hook_name)
-
- self.current_call_hook = hook
-
- output_val = Value('Hook', hook_name)
- self.current_run.output.append(output_val)
-
- def endCall(self):
- self.current_call_hook = None
-
- def startValue(self, attrs):
- if (self.current_call_hook is None
- or self.current_value_arg_id is not None):
- raise MisplacedElement("value", "call")
-
- arg_id = attrs.get('id')
- if arg_id is None:
- raise MissingAttr("value", "id")
-
- # We can't examine the cdata until the endValue event. Stash
- # the argument name until then.
- self.current_value_arg_id = arg_id
- self.current_value_cdata = ""
-
- def endValue(self):
- assert self.current_call_hook is not None
- assert self.current_value_arg_id is not None
+ # We're only interested in the cdata, which we can't get until
+ # the endOutput event.
+ self.current_cdata = ""
+ self.in_output = True
- # Find the arg with the given arg id. These arguments are not
- # stored in an associative array because their order is
- # important. These lists are way too small to justify the
- # overhead of a separate index.
- arg = None
- for arg_it in self.current_call_hook.arg_list:
- if self.current_value_arg_id == arg_it.arg_id:
- arg = arg_it
- if arg is None:
- raise BadId("arg", self.current_value_arg_id)
-
- new_value = Value(arg.arg_type, self.current_value_cdata)
- self.current_run.output.append(new_value)
-
- self.current_value_arg_id = None
+ def endOutput(self):
+ assert self.current_run is not None
+ self.current_run.output = self.current_cdata
+ self.in_output = False
# Parsing with SAX is simple but tedious. There's a start
# function for each tag and an end function for some tags. They
self.startTestcase(attrs)
elif name == 'plugin':
self.startPlugin(attrs)
- elif name == 'hook':
- self.startHook(attrs)
- elif name == 'arg':
- self.startArg(attrs)
elif name == 'run':
self.startRun(attrs)
elif name == 'using':
self.startUsing(attrs)
- elif name == 'call':
- self.startCall(attrs)
- elif name == 'value':
- self.startValue(attrs)
+ elif name == 'output':
+ self.startOutput(attrs)
def endElement(self, name):
- if name == 'plugin':
- self.endPlugin()
- elif name == 'hook':
- self.endHook()
- elif name == 'run':
+ if name == 'run':
self.endRun()
- elif name == 'call':
- self.endCall()
- elif name == 'value':
- self.endValue()
+ elif name == 'output':
+ self.endOutput()
def characters(self, chars):
- if self.current_value_arg_id is not None:
- self.current_value_cdata += chars
-
-def getCheckFormat(c_type):
- c_type = c_type.strip()
- if re.match(r".*\bchar\s+\*", c_type):
- return r"string: %s\n"
- if re.match(r".*\*$", c_type):
- return r"pointer: %p\n"
- elif c_type == 'int':
- return r"int: %d\n"
- else:
- return None
+ if not self.in_output and re.search(r"\S", chars):
+ raise MisplacedCData()
-# Print the C prototype and function body for the given plug-in hook
-# to the given stream.
-def printHook(stream, hook):
- params = ['{0:s} arg{1:d}'.format(hook.arg_list[i].arg_type, i) for i in
- range(len(hook.arg_list))]
- param_text = ', '.join(params)
-
- stream.write('void {0:s}({1:s})\n'.format(hook.name, param_text))
- stream.write('{\n');
- stream.write(' check_printf("hook: {0:s}\\n");\n'.format(hook.name))
- for i in range(len(hook.arg_list)):
- arg = hook.arg_list[i]
- check_format = getCheckFormat(arg.arg_type)
- if check_format is not None:
- stream.write(' check_printf("{0:s}", arg{1:d});\n'.format(check_format, i))
- stream.write('}\n');
+ self.current_cdata += chars
# Run GCC with the given arguments. On failure, print an appropriate
# error and return False.
return plugin_lib_name
-# Compile the testcase instrumentation target, along with
-# auto-generated hook functions. Several compilation files get
-# created in the given working directory. It is the caller's
-# responsibility to delete these files. No files will be left over in
-# other directories, though.
+# Compile the testcase instrumentation target, along with supplied
+# hook functions. Several compilation files get created in the given
+# working directory. It is the caller's responsibility to delete
+# these files. No files will be left over in other directories,
+# though.
#
# working_dir: A temporary directory to store intermediate files and
# the resulting executable.
# target_source: The C source file for the target program (the program
# that we intend to instrument with the test plug-ins).
#
+# hooks_source: The C source file with the advice functions. This
+# source is compiled without any plug-ins.
+#
# plugin_libs: A list of .so plug-in files that will be used to
# compile the target program.
#
# On success, compileTestcase returns the path to the final test
# executable (which will be in working_dir). On failure, the return
# value is None.
-def compileTestcase(working_dir, target_source, plugin_libs, hooks):
- # Create a C file with the necessary plug-in hooks and compile it.
- hook_file_name = working_dir + '/hooks.c'
- hook_file = open(hook_file_name, 'w');
- hook_file.write('#include "test-driver.h"\n')
- for name, hook in hooks.iteritems():
- printHook(hook_file, hook)
- hook_file.close()
-
+def compileTestcase(working_dir, target_source, hooks_source, plugin_libs):
test_include = '-I{0:s}/test'.format(gcc_interaspect_src)
hook_o_file = working_dir + '/hooks.o'
cmd_args = ['-Wall', '-Werror', test_include, '-c', '-o', hook_o_file,
- hook_file_name]
+ hooks_source]
result = runGCC(cmd_args, "Fatal -- Failed to compile plug-in hooks:")
if not result:
return None
return False
plugin_libs.append(plugin_lib_name)
- test_executable = compileTestcase(tmp_dir, run.target_source, plugin_libs,
- run.hooks)
+ test_executable = compileTestcase(tmp_dir, run.target_source,
+ run.hooks_source, plugin_libs)
if test_executable is not None:
test_proc = subprocess.Popen([test_executable])