diff options
Diffstat (limited to 'bindings/python')
-rw-r--r-- | bindings/python/.gitignore | 8 | ||||
-rw-r--r-- | bindings/python/MANIFEST.in | 21 | ||||
-rw-r--r-- | bindings/python/README | 11 | ||||
-rwxr-xr-x | bindings/python/examples/simple.py | 49 | ||||
-rw-r--r-- | bindings/python/ppamltracer.py | 365 | ||||
-rwxr-xr-x | bindings/python/setup.py | 34 |
6 files changed, 488 insertions, 0 deletions
diff --git a/bindings/python/.gitignore b/bindings/python/.gitignore new file mode 100644 index 0000000..4ac65cb --- /dev/null +++ b/bindings/python/.gitignore @@ -0,0 +1,8 @@ +# .gitignore for ppamltracer-python -*- conf -*- + +# Python +*.pyc + +# distutils +MANIFEST +dist/ diff --git a/bindings/python/MANIFEST.in b/bindings/python/MANIFEST.in new file mode 100644 index 0000000..6ec6c9b --- /dev/null +++ b/bindings/python/MANIFEST.in @@ -0,0 +1,21 @@ +# MANIFEST.in -- manifest template for source distributions +# Copyright (C) 2013 Galois, Inc. +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see <http://www.gnu.org/licenses/>. +# +# To contact Galois, complete the Web form at <http://corp.galois.com/contact/> +# or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland, +# Oregon, 97204-1622. + +recursive-include examples *.py diff --git a/bindings/python/README b/bindings/python/README new file mode 100644 index 0000000..35a6bbd --- /dev/null +++ b/bindings/python/README @@ -0,0 +1,11 @@ + ppamltracer-python, v0.1.0 + +This package provides Python bindings to ppamltracer. They require Python 2.7. + +This package uses distutils for installation; you can run + + python setup.py install + +to install the package. + +For examples of use, see the examples directory. diff --git a/bindings/python/examples/simple.py b/bindings/python/examples/simple.py new file mode 100755 index 0000000..350c85a --- /dev/null +++ b/bindings/python/examples/simple.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# simple.py -- basic ppamltracer-python example +# This file is in the public domain. + +import os +import sys + +from ppamltracer import Tracer + +def main(): + # Disable buffering on stdout so we can see the numbers as they are printed. + sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) + # Start ppamltracer. + with Tracer("/tmp/simple_report") as tracer: + # Register the factorial phase. + with tracer.create_phase("fact") as phase: + # Print factorials. + print "Factorials:", + for i in range(21): + print fact(phase, i), + print + # Register the Fibonacci phase. + with tracer.create_phase("fib") as phase: + # Print Fibonacci numbers. + print "Fibonacci numbers: ", + for i in range(24): + print fib(phase, i), + print + +def fact(phase, n): + # Record that we're running inside the factorial phase. + with phase.running(): + # Compute the factorial. + if n == 0: + return 1 + else: + return n * fact(phase, n - 1) + +def fib(phase, n): + # Record that we're running inside the Fibonacci phase. + with phase.running(): + # Compute the nth Fibonacci number. + if n == 0 or n == 1: + return n + else: + return fib(phase, n - 1) + fib(phase, n - 2) + +if __name__ == '__main__': + main() diff --git a/bindings/python/ppamltracer.py b/bindings/python/ppamltracer.py new file mode 100644 index 0000000..dde85a3 --- /dev/null +++ b/bindings/python/ppamltracer.py @@ -0,0 +1,365 @@ +# ppamltracer -- Python bindings to ppamltracer +# Copyright (C) 2013 Galois, Inc. +# +# This library is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This library is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this library. If not, see <http://www.gnu.org/licenses/>. +# +# To contact Galois, complete the Web form at <http://corp.galois.com/contact/> +# or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland, +# Oregon, 97204-1622. + +"""A tracing library for explicit instrumentation of generated code. + +ppamltracer is a lightweight, portable tracing library designed for +explicit instrumention of generated code. If you're writing a compiler +and need hard data on your optimizer's efficacy, ppamltracer is the +library for you. ppamltracer-python provides a high-level Python API on +top of the libppamltracer C API. + +ppamltracer-python's usage can be summed up in a couple lines: + + from ppamltracer import Tracer + with Tracer("/tmp/my_report") as tracer: + with tracer.create_phase("phase 1") as phase: + with phase.running(): + do_stuff() + with tracer.create_phase("phase 2") as phase: + with phase.running(): + do_other_stuff() + with phase.running(): + do_yet_more_stuff() + +This creates a report with the total runtime of do_stuff recorded as +"phase 1", and the total runtime of do_other_stuff and do_yet_more_stuff +combined as "phase 2". + +The ppamltracer-python distribution also contains a more lengthy example +in the "examples" directory. + +ppamltracer writes trace logs in the Open Trace Format [1], a free and +open standard developed by the Zentrum fuer Informationsdienste und +Hochleistungsrechnen (Center for Information Services and +High-Performance Computing) at the Technical University of Dresden. + +We developed ppamltracer and ppamltracer-python as part of DARPA's +Probabilistic Programming for Advancing Machine Learning (PPAML) +project [2]. + +References: + [1] http://tu-dresden.de/zih/otf/ + [2] http://darpa.mil/Our_Work/I2O/Programs/Probabilistic_Programming_for_Advanced_Machine_Learning_(PPAML).aspx +""" +# TODO: Replace "ue" in "fuer" with Unicode u-with-diaresis (blocking on +# deployment of fix to Python #1065986, "Fix pydoc crashing on unicode +# strings"). + +import ctypes +import ctypes.util +import warnings + + +################################ Low-level API ################################ + +_lib = ctypes.cdll.LoadLibrary(ctypes.util.find_library("ppamltracer")) +"""ctypes binding to the ppamltracer C library.""" + +# The code in this section binds the C API using ctypes, translating +# return codes into the exception hierarchy defined below. Since these +# functions are not intended to be used externally, they're not +# documented using pydoc; see the ppamltracer C API documentation for +# information on their behavior. + +_sizeof_ppaml_tracer_t = \ + ctypes.c_size_t.in_dll(_lib, "ppaml_tracer_t_size").value + +_sizeof_ppaml_phase_t = ctypes.c_size_t.in_dll(_lib, "ppaml_phase_t_size").value + +def _ppaml_tracer_init(tracer, report_name_base): + result = _lib.ppaml_tracer_init(tracer, report_name_base) + if result == 0: + return None + elif result == 1: + raise OTFManagerInitializationError() + elif result == 2: + raise OTFWriterInitializationError() + elif result == 3: + raise OTFWriterResolutionError() + elif result == 4: + raise OTFWriterProcessDefinitionError("main") + else: + _warn_unexpected_return_code() + raise TracerError() + +def _ppaml_tracer_done(tracer): + result = _lib.ppaml_tracer_done(tracer) + if result == 0: + return None + elif result == 1: + raise OTFWriterCloseError() + else: + _warn_unexpected_return_code() + raise TracerError() + +def _ppaml_phase_init(tracer, phase, name): + result = _lib.ppaml_phase_init(tracer, phase, name) + if result == 0: + return None + elif result == 1: + raise OTFWriterPhaseDefinitionError() + else: + _warn_unexpected_return_code() + raise TracerError() + +def _ppaml_phase_start(phase): + result = _lib.ppaml_phase_start(phase) + if result == 0: + return None + elif result == 1: + raise ClockAcquisitionError() + elif result == 2: + raise OTFWriterEntryError() + else: + _warn_unexpected_return_code() + raise TracerError() + +def _ppaml_phase_stop(phase): + result = _lib.ppaml_phase_stop(phase) + if result == 0: + return None + elif result == 1: + raise ClockAcquisitionError() + elif result == 2: + raise OTFWriterExitError() + else: + _warn_unexpected_return_code() + raise TracerError() + +def _ppaml_phase_done(phase): + result = _lib.ppaml_phase_done(phase) + if result == 0: + return None + else: + _warn_unexpected_return_code() + raise TracerError() + + +############################# Resource-based API ############################## + +class Tracer(object): + """A tracer for programs, which records execution timing information. + + This class is designed to be used with Python's "with" statement-- e.g., + + with Tracer("/tmp/my_report") as tracer: + main(tracer) + + """ + + def __init__(self, report_name_base): + """Create a new tracer. + + The tracer will create an Open Trace Format report during program + execution. The multiple files of the report will all start with the + specified base name. + + """ + self._report_name_base = report_name_base + self._underlying = None + + def __enter__(self): + if self._underlying is None: + self._underlying = \ + ctypes.create_string_buffer(_sizeof_ppaml_tracer_t) + _ppaml_tracer_init(self._underlying, self._report_name_base) + return self + + def __exit__(self, *exception_info): + if self._underlying is not None: + _ppaml_tracer_done(self._underlying) + self._underlying = None + + def create_phase(self, name): + """Construct a Phase associated with this Tracer. + + This function is merely a convenience function; internally, it shells + out to the Phase constructor. + + """ + return Phase(self, name) + + +class Phase(object): + """A phase of computation traced by ppamltracer. + + This class is designed to be used with Python's "with" statement-- e.g., + + with Phase(tracer, "my phase") as phase: + with phase.running(): + do_stuff() + with phase.running(): + do_stuff_again() + + Note the double use of "with". The outer "with" manages the lifetime of + a Phase; the inner "with" actually starts and stops the phase timer. + + """ + + def __init__(self, tracer, name): + """Define a new phase tracked by a given tracer.""" + self._tracer = tracer + self._name = name + self._underlying = None + + def __enter__(self): + if self._underlying is None: + self._underlying = \ + ctypes.create_string_buffer(_sizeof_ppaml_phase_t) + _ppaml_phase_init( + self._tracer._underlying, + self._underlying, + self._name) + return self + + def __exit__(self, *exception_info): + if self._underlying is not None: + _ppaml_phase_done(self._underlying) + self._underlying = None + + def _start(self): + _ppaml_phase_start(self._underlying) + + def _stop(self): + _ppaml_phase_stop(self._underlying) + + def running(self): + """ Create a resource manager for the phase timer. + + This manager handles starting and stopping the phase, allowing you to + time operations by saying, e.g., + + with phase.running(): + do_stuff() + + """ + return _PhaseTimerManager(self) + + +class _PhaseTimerManager(object): + """Manage entry and exit from a Phase using the "with" statement.""" + + def __init__(self, phase): + self._phase = phase + + def __enter__(self): + self._phase._start() + + def __exit__(self, *exception_info): + self._phase._stop() + + +############################# Exception hierarchy ############################# + +class TracerError(Exception): + """A generic ppamltracer error.""" + pass + +class OTFError(TracerError): + """An error related to Open Trace Format input and output.""" + pass + +class OTFManagerError(OTFError): + """An error caused by the Open Trace Format manager.""" + pass + +class OTFManagerInitializationError(OTFManagerError): + """Failure to initialize the Open Trace Format manager.""" + + def __init__(self): + super.__init__( + self, + "could not initialize Open Trace Format file manager") + +class OTFWriterError(OTFError): + """An error caused by the Open Trace Format writer.""" + pass + +class OTFWriterInitializationError(OTFWriterError): + """Failure to initialize the Open Trace Format writer.""" + + def __init__(self): + super.__init__(self, "could not open Open Trace Format writer") + +class OTFWriterPhaseDefinitionError(OTFWriterError): + """Failure to define a phase. """ + + def __init__(self): + super.__init__(self, "could not define phase") + +class OTFWriterEntryError(OTFWriterError): + """Failure to record entry into a phase.""" + + def __init__(self): + super.__init__(self, "could not record phase start") + +class OTFWriterExitError(OTFWriterError): + """Failure to record exit from a phase.""" + + def __init__(self): + super.__init__(self, "could not record phase end") + +class OTFWriterCloseError(OTFWriterError): + """Failure to close the Open Trace Format writer.""" + + def __init__(self): + super.__init__(self, "could not close Open Trace Format writer") + +class OTFWriterResolutionError(OTFWriterError): + """Failure to set the tracer resolution.""" + + def __init__(self): + super.__init__(self, "could not set trace resolution") + +class OTFWriterProcessDefinitionError(OTFWriterError): + """Failure to define an Open Trace Format process.""" + + def __init__(self, process_name=None): + if process_name is None: + super.__init__(self, "could not define Open Trace Format process") + else: + super.__init__(self, + ("could not define Open Trace Format process \"" + + process_name + + "\"")) + +class TimingError(TracerError): + """An error related to system timers.""" + pass + +class ClockAcquisitionError(TimingError): + """A failure to get the current clock time.""" + + def __init__(self): + super.__init__(self, "could not get current time") + + +############################### Warnings ############################### + +class UnwrappedCErrorWarning(Warning): + """A warning indicating a failure to correctly wrap a C API.""" + pass + +def _warn_unexpected_return_code(): + warnings.warn(("Unexpected C return code\n" + + "*** This is a bug in ppamltracer-python! Report it to the maintainers."), + UnwrappedCErrorWarning, + 2) diff --git a/bindings/python/setup.py b/bindings/python/setup.py new file mode 100755 index 0000000..d8e9ec2 --- /dev/null +++ b/bindings/python/setup.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# setup.py -- distutils setup script for ppamltracer-python +# Copyright (C) 2013 Galois, Inc. +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program. If not, see <http://www.gnu.org/licenses/>. +# +# To contact Galois, complete the Web form at <http://corp.galois.com/contact/> +# or write to Galois, Inc., 421 Southwest 6th Avenue, Suite 300, Portland, +# Oregon, 97204-1622. + +from distutils.core import setup + +setup(name="ppamltracer-python", + version="0.1.0", + description="ppamltracer Python bindings", + author="Benjamin Barenblat", + author_email="bbarenblat@galois.com", + classifiers=["Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License (GPL)", + "Topic :: Software Development :: Compilers"], + license="GPL", + py_modules=['ppamltracer']) |