<html>
<head>
<meta content="text/html; charset=windows-1252"
http-equiv="Content-Type">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div class="moz-cite-prefix">On 16/09/16 20:25, Chris Matthews
wrote:<br>
</div>
<blockquote cite="mid:etPan.57dc3919.29c0aa5b.2baa@apple.com"
type="cite">
<style>body{font-family:Helvetica,Arial;font-size:13px}</style>
<div id="bloop_customfont"
style="font-family:Helvetica,Arial;font-size:13px; color:
rgba(0,0,0,1.0); margin: 0px; line-height: auto;">Yes those are
good changes! Please do add them.</div>
</blockquote>
Thanks, done in r281890. The tool is great! Are there plans to
upload unix builds, too? It'd be very helpful to us.
<meta http-equiv="content-type" content="text/html;
charset=windows-1252">
<blockquote cite="mid:etPan.57dc3919.29c0aa5b.2baa@apple.com"
type="cite"> <br>
<br>
<p class="airmail_on">On September 16, 2016 at 3:10:27 AM, Vassil
Vassilev (<a moz-do-not-send="true"
href="mailto:v.g.vassilev@gmail.com">v.g.vassilev@gmail.com</a>)
wrote:</p>
<blockquote type="cite" class="clean_bq"><span>
<div>
<div>On 08/10/15 23:52, Chris Matthews via llvm-commits
wrote:
<br>
> Author: cmatthews
<br>
> Date: Thu Oct 8 16:52:50 2015
<br>
> New Revision: 249757
<br>
>
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project?rev=249757&view=rev">http://llvm.org/viewvc/llvm-project?rev=249757&view=rev</a>
<br>
> Log:
<br>
> llvmlab bisect tool
<br>
>
<br>
> Added:
<br>
> zorg/trunk/llvmbisect/
<br>
> zorg/trunk/llvmbisect/bin/
<br>
> zorg/trunk/llvmbisect/bin/llvmlab (with props)
<br>
> zorg/trunk/llvmbisect/docs/
<br>
> zorg/trunk/llvmbisect/docs/Makefile
<br>
> zorg/trunk/llvmbisect/docs/builders.rst
<br>
> zorg/trunk/llvmbisect/docs/conf.py
<br>
> zorg/trunk/llvmbisect/docs/index.rst
<br>
> zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst
<br>
> zorg/trunk/llvmbisect/llvmlab/
<br>
> zorg/trunk/llvmbisect/llvmlab/__init__.py
<br>
> zorg/trunk/llvmbisect/llvmlab/algorithm.py
<br>
> zorg/trunk/llvmbisect/llvmlab/ci.py
<br>
> zorg/trunk/llvmbisect/llvmlab/clang_link (with props)
<br>
> zorg/trunk/llvmbisect/llvmlab/gcs.py
<br>
> zorg/trunk/llvmbisect/llvmlab/llvmlab.py
<br>
> zorg/trunk/llvmbisect/llvmlab/scripts.py
<br>
> zorg/trunk/llvmbisect/llvmlab/shell.py
<br>
> zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py
<br>
> zorg/trunk/llvmbisect/llvmlab/util.py
<br>
> zorg/trunk/llvmbisect/setup.py
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/bin/llvmlab
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/bin/llvmlab?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/bin/llvmlab?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/bin/llvmlab (added)
<br>
> +++ zorg/trunk/llvmbisect/bin/llvmlab Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,27 @@
<br>
> +#!/usr/bin/env python
<br>
> +
<br>
> +import sys
<br>
> +import errno
<br>
> +
<br>
> +from llvmlab.ci import action_fetch, action_ls,
action_bisect, action_exec
<br>
> +from llvmlab.ci import action_test
<br>
> +from llvmlab import scripts
<br>
> +
<br>
> +
<br>
> +tool = scripts.Tool(locals())
<br>
> +main = tool.main
<br>
> +
<br>
> +if __name__ == '__main__':
<br>
> + rc = None
<br>
> + # Execute the main function in a try block to catch
EPIPE exceptions.
<br>
> + try:
<br>
> + rc = main(sys.argv)
<br>
> +
<br>
> + # Force a flush on the output pipe to ensure EPIPE
shows up here (prior
<br>
> + # to sys.stdout shutdown).
<br>
> + sys.stdout.flush()
<br>
> + sys.stderr.flush()
<br>
> + except IOError as e:
<br>
> + if e.errno != errno.EPIPE:
<br>
> + raise
<br>
> + sys.exit(0)
<br>
>
<br>
> Propchange: zorg/trunk/llvmbisect/bin/llvmlab
<br>
>
------------------------------------------------------------------------------<br>
> svn:executable = *
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/docs/Makefile
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/Makefile?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/Makefile?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/docs/Makefile (added)
<br>
> +++ zorg/trunk/llvmbisect/docs/Makefile Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,177 @@
<br>
> +# Makefile for Sphinx documentation
<br>
> +#
<br>
> +
<br>
> +# You can set these variables from the command line.
<br>
> +SPHINXOPTS =
<br>
> +SPHINXBUILD = sphinx-build
<br>
> +PAPER =
<br>
> +BUILDDIR = _build
<br>
> +
<br>
> +# User-friendly check for sphinx-build
<br>
> +ifeq ($(shell which $(SPHINXBUILD) >/dev/null
2>&1; echo $$?), 1)
<br>
> +$(error The '$(SPHINXBUILD)' command was not found.
Make sure you have Sphinx installed, then set the
SPHINXBUILD environment variable to point to the full path
of the '$(SPHINXBUILD)' executable. Alternatively you can
add the directory with the executable to your PATH. If you
don't have Sphinx installed, grab it from
<a class="moz-txt-link-freetext" href="http://sphinx-doc.org/">http://sphinx-doc.org/</a>)
<br>
> +endif
<br>
> +
<br>
> +# Internal variables.
<br>
> +PAPEROPT_a4 = -D latex_paper_size=a4
<br>
> +PAPEROPT_letter = -D latex_paper_size=letter
<br>
> +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees
$(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
<br>
> +# the i18n builder cannot share the environment and
doctrees with the others
<br>
> +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS)
.
<br>
> +
<br>
> +.PHONY: help clean html dirhtml singlehtml pickle
json htmlhelp qthelp devhelp epub latex latexpdf text man
changes linkcheck doctest gettext
<br>
> +
<br>
> +help:
<br>
> + @echo "Please use \`make <target>' where
<target> is one of"
<br>
> + @echo " html to make standalone HTML files"
<br>
> + @echo " dirhtml to make HTML files named index.html
in directories"
<br>
> + @echo " singlehtml to make a single large HTML
file"
<br>
> + @echo " pickle to make pickle files"
<br>
> + @echo " json to make JSON files"
<br>
> + @echo " htmlhelp to make HTML files and a HTML help
project"
<br>
> + @echo " qthelp to make HTML files and a qthelp
project"
<br>
> + @echo " devhelp to make HTML files and a Devhelp
project"
<br>
> + @echo " epub to make an epub"
<br>
> + @echo " latex to make LaTeX files, you can set
PAPER=a4 or PAPER=letter"
<br>
> + @echo " latexpdf to make LaTeX files and run them
through pdflatex"
<br>
> + @echo " latexpdfja to make LaTeX files and run them
through platex/dvipdfmx"
<br>
> + @echo " text to make text files"
<br>
> + @echo " man to make manual pages"
<br>
> + @echo " texinfo to make Texinfo files"
<br>
> + @echo " info to make Texinfo files and run them
through makeinfo"
<br>
> + @echo " gettext to make PO message catalogs"
<br>
> + @echo " changes to make an overview of all
changed/added/deprecated items"
<br>
> + @echo " xml to make Docutils-native XML files"
<br>
> + @echo " pseudoxml to make pseudoxml-XML files for
display purposes"
<br>
> + @echo " linkcheck to check all external links for
integrity"
<br>
> + @echo " doctest to run all doctests embedded in the
documentation (if enabled)"
<br>
> +
<br>
> +clean:
<br>
> + rm -rf $(BUILDDIR)/*
<br>
> +
<br>
> +html:
<br>
> + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS)
$(BUILDDIR)/html
<br>
> + @echo
<br>
> + @echo "Build finished. The HTML pages are in
$(BUILDDIR)/html."
<br>
> +
<br>
> +dirhtml:
<br>
> + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS)
$(BUILDDIR)/dirhtml
<br>
> + @echo
<br>
> + @echo "Build finished. The HTML pages are in
$(BUILDDIR)/dirhtml."
<br>
> +
<br>
> +singlehtml:
<br>
> + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS)
$(BUILDDIR)/singlehtml
<br>
> + @echo
<br>
> + @echo "Build finished. The HTML page is in
$(BUILDDIR)/singlehtml."
<br>
> +
<br>
> +pickle:
<br>
> + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS)
$(BUILDDIR)/pickle
<br>
> + @echo
<br>
> + @echo "Build finished; now you can process the
pickle files."
<br>
> +
<br>
> +json:
<br>
> + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS)
$(BUILDDIR)/json
<br>
> + @echo
<br>
> + @echo "Build finished; now you can process the JSON
files."
<br>
> +
<br>
> +htmlhelp:
<br>
> + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS)
$(BUILDDIR)/htmlhelp
<br>
> + @echo
<br>
> + @echo "Build finished; now you can run HTML Help
Workshop with the" \
<br>
> + ".hhp project file in $(BUILDDIR)/htmlhelp."
<br>
> +
<br>
> +qthelp:
<br>
> + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS)
$(BUILDDIR)/qthelp
<br>
> + @echo
<br>
> + @echo "Build finished; now you can run
"qcollectiongenerator" with the" \
<br>
> + ".qhcp project file in $(BUILDDIR)/qthelp, like
this:"
<br>
> + @echo "# qcollectiongenerator
$(BUILDDIR)/qthelp/LLVMLabTools.qhcp"
<br>
> + @echo "To view the help file:"
<br>
> + @echo "# assistant -collectionFile
$(BUILDDIR)/qthelp/LLVMLabTools.qhc"
<br>
> +
<br>
> +devhelp:
<br>
> + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS)
$(BUILDDIR)/devhelp
<br>
> + @echo
<br>
> + @echo "Build finished."
<br>
> + @echo "To view the help file:"
<br>
> + @echo "# mkdir -p
$$HOME/.local/share/devhelp/LLVMLabTools"
<br>
> + @echo "# ln -s $(BUILDDIR)/devhelp
$$HOME/.local/share/devhelp/LLVMLabTools"
<br>
> + @echo "# devhelp"
<br>
> +
<br>
> +epub:
<br>
> + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS)
$(BUILDDIR)/epub
<br>
> + @echo
<br>
> + @echo "Build finished. The epub file is in
$(BUILDDIR)/epub."
<br>
> +
<br>
> +latex:
<br>
> + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS)
$(BUILDDIR)/latex
<br>
> + @echo
<br>
> + @echo "Build finished; the LaTeX files are in
$(BUILDDIR)/latex."
<br>
> + @echo "Run \`make' in that directory to run these
through (pdf)latex" \
<br>
> + "(use \`make latexpdf' here to do that
automatically)."
<br>
> +
<br>
> +latexpdf:
<br>
> + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS)
$(BUILDDIR)/latex
<br>
> + @echo "Running LaTeX files through pdflatex..."
<br>
> + $(MAKE) -C $(BUILDDIR)/latex all-pdf
<br>
> + @echo "pdflatex finished; the PDF files are in
$(BUILDDIR)/latex."
<br>
> +
<br>
> +latexpdfja:
<br>
> + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS)
$(BUILDDIR)/latex
<br>
> + @echo "Running LaTeX files through platex and
dvipdfmx..."
<br>
> + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
<br>
> + @echo "pdflatex finished; the PDF files are in
$(BUILDDIR)/latex."
<br>
> +
<br>
> +text:
<br>
> + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS)
$(BUILDDIR)/text
<br>
> + @echo
<br>
> + @echo "Build finished. The text files are in
$(BUILDDIR)/text."
<br>
> +
<br>
> +man:
<br>
> + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS)
$(BUILDDIR)/man
<br>
> + @echo
<br>
> + @echo "Build finished. The manual pages are in
$(BUILDDIR)/man."
<br>
> +
<br>
> +texinfo:
<br>
> + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS)
$(BUILDDIR)/texinfo
<br>
> + @echo
<br>
> + @echo "Build finished. The Texinfo files are in
$(BUILDDIR)/texinfo."
<br>
> + @echo "Run \`make' in that directory to run these
through makeinfo" \
<br>
> + "(use \`make info' here to do that automatically)."
<br>
> +
<br>
> +info:
<br>
> + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS)
$(BUILDDIR)/texinfo
<br>
> + @echo "Running Texinfo files through makeinfo..."
<br>
> + make -C $(BUILDDIR)/texinfo info
<br>
> + @echo "makeinfo finished; the Info files are in
$(BUILDDIR)/texinfo."
<br>
> +
<br>
> +gettext:
<br>
> + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS)
$(BUILDDIR)/locale
<br>
> + @echo
<br>
> + @echo "Build finished. The message catalogs are in
$(BUILDDIR)/locale."
<br>
> +
<br>
> +changes:
<br>
> + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS)
$(BUILDDIR)/changes
<br>
> + @echo
<br>
> + @echo "The overview file is in
$(BUILDDIR)/changes."
<br>
> +
<br>
> +linkcheck:
<br>
> + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS)
$(BUILDDIR)/linkcheck
<br>
> + @echo
<br>
> + @echo "Link check complete; look for any errors in
the above output " \
<br>
> + "or in $(BUILDDIR)/linkcheck/output.txt."
<br>
> +
<br>
> +doctest:
<br>
> + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS)
$(BUILDDIR)/doctest
<br>
> + @echo "Testing of doctests in the sources finished,
look at the " \
<br>
> + "results in $(BUILDDIR)/doctest/output.txt."
<br>
> +
<br>
> +xml:
<br>
> + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS)
$(BUILDDIR)/xml
<br>
> + @echo
<br>
> + @echo "Build finished. The XML files are in
$(BUILDDIR)/xml."
<br>
> +
<br>
> +pseudoxml:
<br>
> + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS)
$(BUILDDIR)/pseudoxml
<br>
> + @echo
<br>
> + @echo "Build finished. The pseudo-XML files are in
$(BUILDDIR)/pseudoxml."
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/docs/builders.rst
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/builders.rst?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/builders.rst?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/docs/builders.rst (added)
<br>
> +++ zorg/trunk/llvmbisect/docs/builders.rst Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,19 @@
<br>
> +.. _builders:
<br>
> +
<br>
> +Adding your builder to llvmlab bisect
<br>
> +=====================================
<br>
> +
<br>
> +llvmlab bisect compilers are stored on Google Cloud
Storage. There is a common
<br>
> +bucket called llvm-build-artifacts, within that
there is a directory for each
<br>
> +build. Builds can be uploaded in two ways, with
authorized credentials with
<br>
> +the gsutil tool, or if the builder is in
lab.llvm.org, from the labmaster2
<br>
> +stageing server.
<br>
> +
<br>
> +On the labmaster2 staging server any builds uploaded
to:
<br>
>
+``/Library/WebServer/Documents/artifacts/<buildername>/``
will be uploaded via
<br>
> +a cron job. Your builders public key will need to be
added to that machine.
<br>
> +Rsync or scp can be used to upload the files.
<br>
> +
<br>
> +llvmbisect uses some regexes in llvmlab.py to parse
the comiler information.
<br>
> +The tar file you upload will need to match those
regexes. For example:
<br>
> +``clang-r249497-t13154-b13154.tar.gz``
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/docs/conf.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/conf.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/conf.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/docs/conf.py (added)
<br>
> +++ zorg/trunk/llvmbisect/docs/conf.py Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,258 @@
<br>
> +# -*- coding: utf-8 -*-
<br>
> +#
<br>
> +# LLVM Lab Tools documentation build configuration
file, created by
<br>
> +# sphinx-quickstart on Tue Oct 6 18:29:53 2015.
<br>
> +#
<br>
> +# This file is execfile()d with the current
directory set to its
<br>
> +# containing dir.
<br>
> +#
<br>
> +# Note that not all possible configuration values
are present in this
<br>
> +# autogenerated file.
<br>
> +#
<br>
> +# All configuration values have a default; values
that are commented out
<br>
> +# serve to show the default.
<br>
> +
<br>
> +import sys
<br>
> +import os
<br>
> +
<br>
> +# If extensions (or modules to document with
autodoc) are in another directory,
<br>
> +# add these directories to sys.path here. If the
directory is relative to the
<br>
> +# documentation root, use os.path.abspath to make it
absolute, like shown here.
<br>
> +#sys.path.insert(0, os.path.abspath('.'))
<br>
> +
<br>
> +# -- General configuration
------------------------------------------------
<br>
> +
<br>
> +# If your documentation needs a minimal Sphinx
version, state it here.
<br>
> +#needs_sphinx = '1.0'
<br>
> +
<br>
> +# Add any Sphinx extension module names here, as
strings. They can be
<br>
> +# extensions coming with Sphinx (named
'sphinx.ext.*') or your custom
<br>
> +# ones.
<br>
> +extensions = []
<br>
> +
<br>
> +# Add any paths that contain templates here,
relative to this directory.
<br>
> +templates_path = ['_templates']
<br>
> +
<br>
> +# The suffix of source filenames.
<br>
> +source_suffix = '.rst'
<br>
> +
<br>
> +# The encoding of source files.
<br>
> +#source_encoding = 'utf-8-sig'
<br>
> +
<br>
> +# The master toctree document.
<br>
> +master_doc = 'index'
<br>
> +
<br>
> +# General information about the project.
<br>
> +project = u'LLVM Lab Tools'
<br>
> +copyright = u'2015, Daniel Dunbar, Chris Matthews'
<br>
> +
<br>
> +# The version info for the project you're
documenting, acts as replacement for
<br>
> +# |version| and |release|, also used in various
other places throughout the
<br>
> +# built documents.
<br>
> +#
<br>
> +# The short X.Y version.
<br>
> +version = '1.0'
<br>
> +# The full version, including alpha/beta/rc tags.
<br>
> +release = '1.0'
<br>
> +
<br>
> +# The language for content autogenerated by Sphinx.
Refer to documentation
<br>
> +# for a list of supported languages.
<br>
> +#language = None
<br>
> +
<br>
> +# There are two options for replacing |today|:
either, you set today to some
<br>
> +# non-false value, then it is used:
<br>
> +#today = ''
<br>
> +# Else, today_fmt is used as the format for a
strftime call.
<br>
> +#today_fmt = '%B %d, %Y'
<br>
> +
<br>
> +# List of patterns, relative to source directory,
that match files and
<br>
> +# directories to ignore when looking for source
files.
<br>
> +exclude_patterns = ['_build']
<br>
> +
<br>
> +# The reST default role (used for this markup:
`text`) to use for all
<br>
> +# documents.
<br>
> +#default_role = None
<br>
> +
<br>
> +# If true, '()' will be appended to :func: etc.
cross-reference text.
<br>
> +#add_function_parentheses = True
<br>
> +
<br>
> +# If true, the current module name will be prepended
to all description
<br>
> +# unit titles (such as .. function::).
<br>
> +#add_module_names = True
<br>
> +
<br>
> +# If true, sectionauthor and moduleauthor directives
will be shown in the
<br>
> +# output. They are ignored by default.
<br>
> +#show_authors = False
<br>
> +
<br>
> +# The name of the Pygments (syntax highlighting)
style to use.
<br>
> +pygments_style = 'sphinx'
<br>
> +
<br>
> +# A list of ignored prefixes for module index
sorting.
<br>
> +#modindex_common_prefix = []
<br>
> +
<br>
> +# If true, keep warnings as "system message"
paragraphs in the built documents.
<br>
> +#keep_warnings = False
<br>
> +
<br>
> +
<br>
> +# -- Options for HTML output
----------------------------------------------
<br>
> +
<br>
> +# The theme to use for HTML and HTML Help pages. See
the documentation for
<br>
> +# a list of builtin themes.
<br>
> +html_theme = 'default'
<br>
> +
<br>
> +# Theme options are theme-specific and customize the
look and feel of a theme
<br>
> +# further. For a list of options available for each
theme, see the
<br>
> +# documentation.
<br>
> +#html_theme_options = {}
<br>
> +
<br>
> +# Add any paths that contain custom themes here,
relative to this directory.
<br>
> +#html_theme_path = []
<br>
> +
<br>
> +# The name for this set of Sphinx documents. If
None, it defaults to
<br>
> +# "<project> v<release> documentation".
<br>
> +#html_title = None
<br>
> +
<br>
> +# A shorter title for the navigation bar. Default is
the same as html_title.
<br>
> +#html_short_title = None
<br>
> +
<br>
> +# The name of an image file (relative to this
directory) to place at the top
<br>
> +# of the sidebar.
<br>
> +#html_logo = None
<br>
> +
<br>
> +# The name of an image file (within the static path)
to use as favicon of the
<br>
> +# docs. This file should be a Windows icon file
(.ico) being 16x16 or 32x32
<br>
> +# pixels large.
<br>
> +#html_favicon = None
<br>
> +
<br>
> +# Add any paths that contain custom static files
(such as style sheets) here,
<br>
> +# relative to this directory. They are copied after
the builtin static files,
<br>
> +# so a file named "default.css" will overwrite the
builtin "default.css".
<br>
> +html_static_path = ['_static']
<br>
> +
<br>
> +# Add any extra paths that contain custom files
(such as robots.txt or
<br>
> +# .htaccess) here, relative to this directory. These
files are copied
<br>
> +# directly to the root of the documentation.
<br>
> +#html_extra_path = []
<br>
> +
<br>
> +# If not '', a 'Last updated on:' timestamp is
inserted at every page bottom,
<br>
> +# using the given strftime format.
<br>
> +#html_last_updated_fmt = '%b %d, %Y'
<br>
> +
<br>
> +# If true, SmartyPants will be used to convert
quotes and dashes to
<br>
> +# typographically correct entities.
<br>
> +#html_use_smartypants = True
<br>
> +
<br>
> +# Custom sidebar templates, maps document names to
template names.
<br>
> +#html_sidebars = {}
<br>
> +
<br>
> +# Additional templates that should be rendered to
pages, maps page names to
<br>
> +# template names.
<br>
> +#html_additional_pages = {}
<br>
> +
<br>
> +# If false, no module index is generated.
<br>
> +#html_domain_indices = True
<br>
> +
<br>
> +# If false, no index is generated.
<br>
> +#html_use_index = True
<br>
> +
<br>
> +# If true, the index is split into individual pages
for each letter.
<br>
> +#html_split_index = False
<br>
> +
<br>
> +# If true, links to the reST sources are added to
the pages.
<br>
> +#html_show_sourcelink = True
<br>
> +
<br>
> +# If true, "Created using Sphinx" is shown in the
HTML footer. Default is True.
<br>
> +#html_show_sphinx = True
<br>
> +
<br>
> +# If true, "(C) Copyright ..." is shown in the HTML
footer. Default is True.
<br>
> +#html_show_copyright = True
<br>
> +
<br>
> +# If true, an OpenSearch description file will be
output, and all pages will
<br>
> +# contain a <link> tag referring to it. The
value of this option must be the
<br>
> +# base URL from which the finished HTML is served.
<br>
> +#html_use_opensearch = ''
<br>
> +
<br>
> +# This is the file name suffix for HTML files (e.g.
".xhtml").
<br>
> +#html_file_suffix = None
<br>
> +
<br>
> +# Output file base name for HTML help builder.
<br>
> +htmlhelp_basename = 'LLVMLabToolsdoc'
<br>
> +
<br>
> +
<br>
> +# -- Options for LaTeX output
---------------------------------------------
<br>
> +
<br>
> +latex_elements = {
<br>
> +# The paper size ('letterpaper' or 'a4paper').
<br>
> +#'papersize': 'letterpaper',
<br>
> +
<br>
> +# The font size ('10pt', '11pt' or '12pt').
<br>
> +#'pointsize': '10pt',
<br>
> +
<br>
> +# Additional stuff for the LaTeX preamble.
<br>
> +#'preamble': '',
<br>
> +}
<br>
> +
<br>
> +# Grouping the document tree into LaTeX files. List
of tuples
<br>
> +# (source start file, target name, title,
<br>
> +# author, documentclass [howto, manual, or own
class]).
<br>
> +latex_documents = [
<br>
> + ('index', 'LLVMLabTools.tex', u'LLVM Lab Tools
Documentation',
<br>
> + u'Daniel Dunbar, Chris Matthews', 'manual'),
<br>
> +]
<br>
> +
<br>
> +# The name of an image file (relative to this
directory) to place at the top of
<br>
> +# the title page.
<br>
> +#latex_logo = None
<br>
> +
<br>
> +# For "manual" documents, if this is true, then
toplevel headings are parts,
<br>
> +# not chapters.
<br>
> +#latex_use_parts = False
<br>
> +
<br>
> +# If true, show page references after internal
links.
<br>
> +#latex_show_pagerefs = False
<br>
> +
<br>
> +# If true, show URL addresses after external links.
<br>
> +#latex_show_urls = False
<br>
> +
<br>
> +# Documents to append as an appendix to all manuals.
<br>
> +#latex_appendices = []
<br>
> +
<br>
> +# If false, no module index is generated.
<br>
> +#latex_domain_indices = True
<br>
> +
<br>
> +
<br>
> +# -- Options for manual page output
---------------------------------------
<br>
> +
<br>
> +# One entry per manual page. List of tuples
<br>
> +# (source start file, name, description, authors,
manual section).
<br>
> +man_pages = [
<br>
> + ('index', 'llvmlabtools', u'LLVM Lab Tools
Documentation',
<br>
> + [u'Daniel Dunbar, Chris Matthews'], 1)
<br>
> +]
<br>
> +
<br>
> +# If true, show URL addresses after external links.
<br>
> +#man_show_urls = False
<br>
> +
<br>
> +
<br>
> +# -- Options for Texinfo output
-------------------------------------------
<br>
> +
<br>
> +# Grouping the document tree into Texinfo files.
List of tuples
<br>
> +# (source start file, target name, title, author,
<br>
> +# dir menu entry, description, category)
<br>
> +texinfo_documents = [
<br>
> + ('index', 'LLVMLabTools', u'LLVM Lab Tools
Documentation',
<br>
> + u'Daniel Dunbar, Chris Matthews', 'LLVMLabTools',
'One line description of project.',
<br>
> + 'Miscellaneous'),
<br>
> +]
<br>
> +
<br>
> +# Documents to append as an appendix to all manuals.
<br>
> +#texinfo_appendices = []
<br>
> +
<br>
> +# If false, no module index is generated.
<br>
> +#texinfo_domain_indices = True
<br>
> +
<br>
> +# How to display URL addresses: 'footnote', 'no', or
'inline'.
<br>
> +#texinfo_show_urls = 'footnote'
<br>
> +
<br>
> +# If true, do not generate a @detailmenu in the
"Top" node's menu.
<br>
> +#texinfo_no_detailmenu = False
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/docs/index.rst
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/index.rst?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/index.rst?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/docs/index.rst (added)
<br>
> +++ zorg/trunk/llvmbisect/docs/index.rst Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,23 @@
<br>
> +.. LLVM Lab Tools documentation master file, created
by
<br>
> + sphinx-quickstart on Tue Oct 6 18:29:53 2015.
<br>
> + You can adapt this file completely to your liking,
but it should at least
<br>
> + contain the root `toctree` directive.
<br>
> +
<br>
> +Welcome to LLVM Lab Tools's documentation!
<br>
> +==========================================
<br>
> +
<br>
> +Contents:
<br>
> +
<br>
> +.. toctree::
<br>
> + :maxdepth: 2
<br>
> +
<br>
> + llvmlab_bisect
<br>
> + builders
<br>
> +
<br>
> +
<br>
> +Indices and tables
<br>
> +==================
<br>
> +
<br>
> +* :ref:`genindex`
<br>
> +* :ref:`modindex`
<br>
> +* :ref:`search`
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst
(added)
<br>
> +++ zorg/trunk/llvmbisect/docs/llvmlab_bisect.rst Thu
Oct 8 16:52:50 2015
<br>
> @@ -0,0 +1,569 @@
<br>
> +.. _llvmlab-bisect:
<br>
> +
<br>
> +Automatic compiler bisecting with llvmlab bisect
<br>
> +==================================================
<br>
> +
<br>
> +``llvmlab bisect`` is a tool for automatically
identifying
<br>
> +when a regression was introduced in any build of
Clang, or LLVM
<br>
> +produced by one of our Buildbots or Jenkins jobs.
<br>
> +
<br>
> +The basics of the tool are very simple, you must
provided it with a "test case"
<br>
> +which will reproduce the problem you are seeing.
Once you have done that, the
<br>
> +tool will automatically download compiler packages
from the cloud
<br>
> +(typically produced by our continuous integration
system) and will check whether
<br>
> +the problem reproduces with that compiler or not.
The tool will then attempt to
<br>
> +narrow down on the first compiler which broke the
test, and will report the last
<br>
> +compiler which worked and the first compiler that
failed.
<br>
> +
<br>
> +Getting the tool
<br>
> +~~~~~~~~~~~~~~~~
<br>
> +
<br>
> +The tool is in our tools repo::
<br>
> + $ svn checkout
<a class="moz-txt-link-freetext" href="https://llvm.org/svn/llvm-project/zorg">https://llvm.org/svn/llvm-project/zorg</a>
<br>
Did you mean <a class="moz-txt-link-freetext" href="https://llvm.org/svn/llvm-project/zorg/trunk">https://llvm.org/svn/llvm-project/zorg/trunk</a>
zorg ? <br>
Otherwise the next command doesn't work.
<br>
<br>
Also I ended up working around the 'sudo' requirement by:
<br>
<br>
svn co <a class="moz-txt-link-freetext" href="https://llvm.org/svn/llvm-project/zorg/trunk/">https://llvm.org/svn/llvm-project/zorg/trunk/</a> zorg
<br>
cd zorg/llvmbisect
<br>
LOCAL_PYTHON_INSTALL_PATH=$(pwd)/local_python_packages/lib/python2.7/site-packages/
<br>
mkdir -p $LOCAL_PYTHON_INSTALL_PATH
<br>
export PYTHONPATH=$LOCAL_PYTHON_INSTALL_PATH:$PYTHONPATH
<br>
python setup.py install
--prefix=$(pwd)/local_python_packages
<br>
export PATH=$(pwd)/bin:$PATH
<br>
<br>
Do you think it is worth mentioning in the docs?
<br>
<br>
--Vassil
<br>
> + $ cd zorg/llvmbisect
<br>
> + $ sudo python setup.py install
<br>
> + $ llvmlab ls
<br>
> +
<br>
> +
<br>
> +The Bisection Process
<br>
> +~~~~~~~~~~~~~~~~~~~~~
<br>
> +
<br>
> +There are several parts of the bisection process you
should understand to use
<br>
> +``llvmlab bisect`` effectively:
<br>
> +
<br>
> + * How the tool gets compiler builds.
<br>
> +
<br>
> + * How tests (bisection predicates) are run.
<br>
> +
<br>
> + * How the bisect process sandboxes tests.
<br>
> +
<br>
> + * Test filters.
<br>
> +
<br>
> +Compiler Packages
<br>
> ++++++++++++++++++
<br>
> +
<br>
> +Bisection uses packages produced by the continuous
integration
<br>
> +system. Currently, it will only consider packages
which are produced by one
<br>
> +particular "builder". The default is to use the
``clang-stage1-configure-RA_build``
<br>
> +line of packages, because that is produced by our
mini farm and has a very high
<br>
> +granularity and a long history.
<br>
> +
<br>
> +You can tell the tool to use a particular line of
builds using the ``-b`` or
<br>
> +``--build`` command line option.
<br>
> +
<br>
> +You can see a list of the kinds of builds which are
published using::
<br>
> +
<br>
> + $ ./llvmlab ls
<br>
> + clang-stage1-configure-RA_build
<br>
> + clang-stage2-foo
<br>
> +
<br>
> +Each of these corresponds to a particular
buildbot/Jenkins builder
<br>
> +which is constantly building new revisions and
uploading them to
<br>
> +the cloud.
<br>
> +
<br>
> +The important thing to understand is that the
particular compiler package in use
<br>
> +may impact your test. For example,
``clang-stage1-configure-RA_build`` builds
<br>
> +are x86_64 compilers built on Yosemite in release
asserts mode. Generally, you
<br>
> +should make sure your test explicitly sets anything
which could impact the test
<br>
> +(like the architecture).
<br>
> +
<br>
> +The other way this impacts your tests is that some
packages are layed out
<br>
> +differently than others. Most compiler packages are
layed out in a "Unix" style,
<br>
> +with ``bin`` and ``lib`` subdirectories. One easy
way to see the package layout
<br>
> +is to use ``llvmlab fetch`` to grab a build from the
particular builder you
<br>
> +are using and poke at it. For example::
<br>
> +
<br>
> + $ llvmlab fetch clang-i386-darwin9-snt
<br>
> + downloaded root: clang-r128299-b6960.tgz
<br>
> + extracted path :
<br>
> + $ ls clang-r128299-b6960
<br>
> + bin docs lib share
<br>
> +
<br>
> +See ``llvmlab fetch --help`` for more information on
the ``fetch`` tool.
<br>
> +
<br>
> +The main exception to remember is that Apple style
builds generally will have
<br>
> +"root" style layouts, where the package is meant to
be installed directly into
<br>
> +``/``, and will be layed out with ``usr/bin`` and
``Developer/usr/bin``
<br>
> +subdirectories.
<br>
> +
<br>
> +
<br>
> +The Build Cache
<br>
> ++++++++++++++++
<br>
> +
<br>
> +``llvmlab bisect`` can be configured to cache
downloaded archives. This is
<br>
> +useful for users who frequently bisect things and
want the command to run as
<br>
> +fast as possible. Note that the tool doesn't try and
do anything smart about
<br>
> +minimizing the amount of disk space the cache uses,
so use this at your own
<br>
> +risk.
<br>
> +
<br>
> +To enable the cache::
<br>
> + $ mkdir -p ~/.llvmlab
<br>
> + $ echo "[ci]" > ~/.llvmlab/config
<br>
> + $ echo "cache_builds = True" > ~/.llvmlab/config
<br>
> +
<br>
> +
<br>
> +Bisection Predicates
<br>
> +++++++++++++++++++++
<br>
> +
<br>
> +Like most bisection tools, ``llvmlab bisect`` needs
to have a way to test
<br>
> +whether a particular build "passes" or "fails".
``llvmlab bisect`` uses a
<br>
> +format which allows writing most bisection commands
on a single command line
<br>
> +without having to write extra shell scripts.
<br>
> +
<br>
> +``llvmlab ci exec`` is an invaluable tool for
checking bisection
<br>
> +predicates. It accepts the exact same syntax as
llvmlab bisect, but prints a
<br>
> +bit more information by default and only runs a
single command. This is useful
<br>
> +for vetting bisection predicates before running a
full bisection process.
<br>
> +
<br>
> +Predicates are written as commands which are
expected to exit successfully
<br>
> +(i.e., return 0 as the exit code) when the test
succeeds
<br>
> +[#predicate_tense]_. The command will be run once
for each downloaded package to
<br>
> +determine if the test passes or fails on that
particular build.
<br>
> +
<br>
> +``llvmlab bisect`` treats all non-optional command
line arguments as the
<br>
> +command to be run. Each argument will be rewritten
to possibly substitute
<br>
> +variables, and then the entire command line will be
run (i.e., ``exec()``'d) to
<br>
> +determine whether the test passes or fails.
<br>
> +
<br>
> +.. _string:
<a class="moz-txt-link-freetext" href="http://docs.python.org/library/stdtypes.html#string-formatting">http://docs.python.org/library/stdtypes.html#string-formatting</a>
<br>
> +
<br>
> +Bisection downloads each package into a separate
directory inside a sandbox and
<br>
> +provides a mechanism for substituting the path to
package into the command to be
<br>
> +run. Variables are substituted using the Python
syntax with string_ formatting
<br>
> +named keys. For the most part, the syntax is like
``printf`` but variable names
<br>
> +are written in parentheses before the format
specifier [#sh_parens]_.
<br>
> +
<br>
> +The most important variable is "path", which will be
set to the path to the
<br>
> +downloaded package. For example::
<br>
> +
<br>
> + %(path)s/bin/clang
<br>
> +
<br>
> +would typically be expanded to something like::
<br>
> +
<br>
> + .../<sandbox>/clang-r128289-b6957/bin/clang
<br>
> +
<br>
> +before the command is run. You can use the ``-v``
(``--verbose``) command line
<br>
> +option to have ``llvmlab bisect`` print the command
lines it is running after
<br>
> +substitution.
<br>
> +
<br>
> +The tool provides a few other variables but "path"
is the only one needed for
<br>
> +all but the rarest bisections. You can see the
others in ``llvmlab bisect
<br>
> +--help``.
<br>
> +
<br>
> +The tool optimizes for the situation where
downloaded packages include command
<br>
> +line executable which are going to be used in the
tests, by automatically
<br>
> +extending the PATH and DYLD_LIBRARY_PATH variables
to point into the downloaded
<br>
> +build directory whenever it sees that the downloaded
package has ``bin`` or
<br>
> +``lib`` directories (the tool will also look for
``/Developer/usr/...``
<br>
> +directories). This environment extensions mean that
it is usually possible to
<br>
> +write simple test commands without requiring any
substitutions.
<br>
> +
<br>
> +For some bisection scenarios, it is easier to write
a test script than to try
<br>
> +and come up with a single predicate command. For
these scenarioes, ``llvmlab
<br>
> +bisect`` also makes all of the substitution
variables available in the command's
<br>
> +environment. Each variable is injected into the
environment as
<br>
> +``TEST_<variable>``. As an example, the
following script could be used as a test
<br>
> +predicate which just checks that the compile
succeeds::
<br>
> +
<br>
> + #!/bin/sh
<br>
> +
<br>
> + $TEST_PATH/bin/clang -c t.c
<br>
> +
<br>
> +Even though llvmlab bisect itself will only run one
individual command per
<br>
> +build, you can write arbitrarily complicated test
predicates by either (a)
<br>
> +writing external test scripts, or (b) writing shell
"one-liners" and using
<br>
> +``/bin/sh -c`` to execute them. For example, the
following bisect will test that
<br>
> +a particular source file both compiles and executes
successfully::
<br>
> +
<br>
> + llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
&& ./a.out'
<br>
> +
<br>
> +llvmlab bisect also supports a shortcut for this
particular pattern. Separate
<br>
> +test commands can be separated on the command by a
literal "----" command line
<br>
> +argument. Each command will be substituted as usual,
but will they will be run
<br>
> +separately in order and if any command fails the
entire test will fail.
<br>
> +
<br>
> +.. [#predicate_tense] Note that ``llvmlab bisect``
always looks for the latest
<br>
> + build where a predicate *passes*. This means that
it
<br>
> + generally expects the predicate to fail on any
recent
<br>
> + build. If you are used to using tools like
``delta`` you
<br>
> + may be used to the predicate having the opposite
tense --
<br>
> + however, for regression analysis usually one is
<br>
> + investigating a failure, and so one expects the
test to
<br>
> + currently fail.
<br>
> +
<br>
> +.. [#sh_parens] Most shells will assign a syntax to
(foo) so you generally have
<br>
> + to quote arguments which require substitution. One
day I'll
<br>
> + think of a clever way I like to commands even
easier to
<br>
> + write. Until then, quote away!
<br>
> +
<br>
> +
<br>
> +The Bisection Sandbox
<br>
> ++++++++++++++++++++++
<br>
> +
<br>
> +``llvmlab bisect`` tries to be very lightweight and
not modify your working
<br>
> +directory or leave stray files around unless asked
to. For that reason, it
<br>
> +downloads all of the packages and runs all of the
tests inside a sandbox. By
<br>
> +default, the tool uses a sandbox inside ``/tmp`` and
will destroy the sandbox
<br>
> +when it is done running tests.
<br>
> +
<br>
> +The tool also tries to be quiet and minimize command
output, so the output of
<br>
> +each individual test run is also stored inside the
sandbox. Unfortunately, this
<br>
> +means when the sandbox is destroyed you will no
longer have access to the log
<br>
> +files if you think the predicate was not working
correctly.
<br>
> +
<br>
> +For long running or complicated bisects, it is
recommended to use the ``-s`` or
<br>
> +``--sandbox`` to tell the tool where to put the
sandbox. If this option is used,
<br>
> +the sandbox will not be destroyed and you can
investigate the log files for each
<br>
> +predicate run and the downloaded packages at your
leisure.
<br>
> +
<br>
> +Predicates commands themselves are **NOT** run
inside the sandbox, they are
<br>
> +always run in the current working directory. This is
useful for referring to
<br>
> +test input files, but may be a problem if you wish
to store the outputs of each
<br>
> +individual test run (for example, to analyze later).
For that case, one method
<br>
> +is to store the test outputs inside the download
package directories. The
<br>
> +following example will store each generated
executable inside the build
<br>
> +directory for testing later::
<br>
> +
<br>
> + llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
-o %(path)s/foo && %(path)s/foo'
<br>
> +
<br>
> +
<br>
> +Environment Extensions
<br>
> +++++++++++++++++++++++
<br>
> +
<br>
> +``llvmlab bisect`` tries to optimize for the common
case where build product
<br>
> +have executables or libraries to test, by
automatically extending the ``PATH``
<br>
> +and ``DYLD_LIBRARY_PATH`` variables when it
recognizes that the build package
<br>
> +has ``bin`` or ``lib`` subdirectories.
<br>
> +
<br>
> +For almost all common bisection tasks, this makes it
possible to run the tool
<br>
> +without having to explicitly specify the
substitution variables.
<br>
> +
<br>
> +For example::
<br>
> +
<br>
> + llvmlab bisect '%(path)s/bin/clang' -c t.c
<br>
> +
<br>
> +could just be written as::
<br>
> +
<br>
> + llvmlab bisect clang -c t.c
<br>
> +
<br>
> +because the ``clang`` binary in the downloaded
package will be found first in
<br>
> +the environment lookup.
<br>
> +
<br>
> +
<br>
> +Test Filters
<br>
> +++++++++++++
<br>
> +
<br>
> +For more advanced uses, llvmlab bisect has a syntax
for specifying "filters"
<br>
> +on individual commands. The syntax for filters is
that they should be specified
<br>
> +at the start of the command using arguments like
"%%<filter expression>%%".
<br>
> +
<br>
> +The filters are used as a way to specify additional
parameters which only apply
<br>
> +to particular test commands. The expressions
themselves are just Python
<br>
> +expressions which should evaluate to a boolean
result, which becomes the result
<br>
> +of the test.
<br>
> +
<br>
> +The Python expressions are evaluate in an
environment which contains the
<br>
> +following predefined variables:
<br>
> +
<br>
> +``result``
<br>
> +
<br>
> + The current boolean result of the test predicate
(that is, true if the test is
<br>
> + "passing"). This may have been modified by
preceeding filters.
<br>
> +
<br>
> +``user_time``, ``sys_time``, ``wall_time``
<br>
> +
<br>
> + The user, system, and wall time the command took to
execute, respectively.
<br>
> +
<br>
> +These variables can be used to easily construct
predicates which fail based on
<br>
> +more complex criterion. For example, here is a
filter to look for the latest
<br>
> +build where the compiler succeeds in less than .5
seconds::
<br>
> +
<br>
> + llvmlab bisect "%% result and user_time < .5 %%"
clang -c t.c
<br>
> +
<br>
> +
<br>
> +Using ``llvmlab bisect``
<br>
> +~~~~~~~~~~~~~~~~~~~~~~~~~~
<br>
> +
<br>
> +``llvmlab bisect`` is very flexible but takes some
getting used to. The
<br>
> +following section has example bisection commands for
many common scenarios.
<br>
> +
<br>
> +Compiler Crashes
<br>
> +++++++++++++++++
<br>
> +
<br>
> +This is the simplest case, a bisection for a
compiler crash or assertion failure
<br>
> +usually looks like::
<br>
> +
<br>
> + $ llvmlab bisect '%(path)s'/bin/clang -c t.c ...
compiler flags ...
<br>
> +
<br>
> +because when the compiler crashes it will have a
non-zero exit code. *For
<br>
> +bisecting assertion failures, you should make sure
the build being tested has
<br>
> +assertions compiled in!*
<br>
> +
<br>
> +Suppose you are investigating a crash which has been
fixed, and you want to know
<br>
> +where. Just use the LLVM ``not`` tool to reverse the
test:
<br>
> +
<br>
> + $ llvmlab bisect not '%(path)s'/bin/clang -c t.c
... compiler flags ...
<br>
> +
<br>
> +By looking for the latest build where ``not clang
...`` *passes* we are
<br>
> +effectively looking for the latest broken build. The
next build will generally
<br>
> +be the one which fixed the problem.
<br>
> +
<br>
> +
<br>
> +Miscompiles
<br>
> ++++++++++++
<br>
> +
<br>
> +Miscompiles usually involve compiling and running
the output.
<br>
> +
<br>
> +The simplest scenario is when the program crashes
when run. In that case the
<br>
> +simplest method is to use the ``/bin/sh -c "...
arguments ..."`` trick to
<br>
> +combine the compile and execute steps into one
command line::
<br>
> +
<br>
> + $ llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
&& ./a.out'
<br>
> +
<br>
> +Note that because we are already quoting the shell
command, we can just move the
<br>
> +quotes around the entire line and not worry about
quoting individual arguments
<br>
> +(unless they have spaces!).
<br>
> +
<br>
> +A more complex scenario is when the program runs but
has bad output. Usually
<br>
> +this just means you need to grep the output for
correct output. For example, to
<br>
> +bisect a program which is supposed to print "OK"
(but isn't currently) we could
<br>
> +use::
<br>
> +
<br>
> + $ llvmlab bisect /bin/sh -c '%(path)s/bin/clang t.c
&& ./a.out | grep "OK"'
<br>
> +
<br>
> +Beware the pitfalls of exit codes and pipes, and use
temporary files if you
<br>
> +aren't sure of what you are doing!
<br>
> +
<br>
> +
<br>
> +Overlapped Failures
<br>
> ++++++++++++++++++++
<br>
> +
<br>
> +If you are used to using a test case reduction tool
like ``delta`` or
<br>
> +``bugpoint``, you are probably familiar with the
problem of running the tool for
<br>
> +hours, only to find that it found a very nice test
case for a different problem
<br>
> +than what you were looking for.
<br>
> +
<br>
> +The same problem happens when bisecting a program
which was previously broken
<br>
> +for a different reason. If you run the tool but the
results don't seem to make
<br>
> +sense, I recommend saving the sandbox (e.g.,
``llvmlab bisect -s /tmp/foo
<br>
> +...``) and investigating the log files to make sure
bisection looked for the
<br>
> +problem you are interested in. If it didn't, usually
you should make your
<br>
> +predicate more precise, for example by using
``grep`` to search the output for a
<br>
> +more precise failure message (like an assertion
failure string).
<br>
> +
<br>
> +
<br>
> +Infinite Loops
<br>
> +++++++++++++++
<br>
> +
<br>
> +On occasion, you will want to bisect something that
infinite loops or takes
<br>
> +much longer than usual. This is a problem because
you usually don't want to wait
<br>
> +for a long time (or infinity) for the predicate to
complete.
<br>
> +
<br>
> +One simple trick which can work is to use the
``ulimit`` command to set a time
<br>
> +limit. The following command will look for the
latest build where the compiler
<br>
> +runs in less than 10 seconds on the given input::
<br>
> +
<br>
> + $ llvmlab bisect /bin/sh -c 'ulimit -t 10;
%(path)s/bin/clang -c t.c'
<br>
> +
<br>
> +
<br>
> +Performance Regressions
<br>
> ++++++++++++++++++++++++
<br>
> +
<br>
> +Bisecting performance regressions is done most
easily using the filter
<br>
> +expressions. Usually you would start by determining
what an approximate upper
<br>
> +bound on the expected time of the command is. Then,
use a ``max_time`` filter
<br>
> +with that time to cause any test running longer than
that to fail.
<br>
> +
<br>
> +For example, the following example shows a real
bisection of a performance
<br>
> +regression on the ``telecom-gsm`` benchmark::
<br>
> +
<br>
> + llvmlab bisect \
<br>
> + '%(path)s/bin/clang' -o telecomm-gsm.exe -w -arch
x86_64 -O3 \
<br>
> +
~/llvm-test-suite/MultiSource/Benchmarks/MiBench/telecomm-gsm/*.c
\
<br>
> + -lm -DSTUPID_COMPILER -DNeedFunctionPrototypes=1
-DSASR \
<br>
> + ---- \
<br>
> + "%% user_time < 0.25 %%" ./telecomm-gsm.exe -fps
-c \
<br>
> +
~/llvm-test-suite/MultiSource/Benchmarks/MiBench/telecomm-gsm/large.au
<br>
> +
<br>
> +
<br>
> +Nightly Test Failures
<br>
> ++++++++++++++++++++++
<br>
> +
<br>
> +If you are bisecting a nightly test failure, it
commonly helps to leverage the
<br>
> +existing nightly test Makefiles rather than try to
write your own step to build
<br>
> +or test an executable against the expected output.
In particular, the Makefiles
<br>
> +generate report files which say whether the test
passed or failed.
<br>
> +
<br>
> +For example, if you are using LNT to run your
nightly tests, then the top line
<br>
> +the ``test.log`` file shows the exact command used
to run the tests. You can
<br>
> +always rerun this command in any subdirectory. For
example, here is an example
<br>
> +from an i386 Clang run::
<br>
> +
<br>
> + 2010-10-12 08:54:39: running: "make" "-k" "-j" "1"
"report" "report.simple.csv" \
<br>
> +
"TARGET_LLVMGCC=/Users/ddunbar/llvm.ref/2010-10-12_00-01.install/bin/clang"
\
<br>
> + "CC_UNDER_TEST_TARGET_IS_I386=1"
"ENABLE_HASHED_PROGRAM_OUTPUT=1" "TARGET_CXX=None" \
<br>
> + "LLI_OPTFLAGS=-O0" "TARGET_CC=None" \
<br>
> +
"TARGET_LLVMGXX=/Users/ddunbar/llvm.ref/2010-10-12_00-01.install/bin/clang++"
\
<br>
> + "TEST=simple" "CC_UNDER_TEST_IS_CLANG=1"
"TARGET_LLCFLAGS=" "TARGET_FLAGS=-g -arch i386" \
<br>
> + "USE_REFERENCE_OUTPUT=1" "OPTFLAGS=-O0"
"SMALL_PROBLEM_SIZE=1" "LLC_OPTFLAGS=-O0" \
<br>
> + "ENABLE_OPTIMIZED=1" "ARCH=x86" "DISABLE_CBE=1"
"DISABLE_JIT=1"
<br>
> +
<br>
> +Suppose we wanted to bisect a test failure on
something complicated, like
<br>
> +``254.gap``. The "easiest" thing to do is:
<br>
> +
<br>
> + #. Replace the compiler paths with "%(path)s" so
that we use the right compiler to test.
<br>
> +
<br>
> + #. Change into the test directory, in this case
``External/SPEC/CINT2000/254.gap``.
<br>
> +
<br>
> + #. Each test produces a ``<test
name>.simple.execute.report.txt`` text file which will
have a line that looks like::
<br>
> +
<br>
> + TEST-FAIL: exec
/Users/ddunbar/nt/clang.i386.O0.g/test-2011-03-25_06-35-35/External/SPEC/CINT2000/254.gap/254.gap<br>
> +
<br>
> + because the tests are make driven, we can tell make
to only build this
<br>
> + file. In SingleSource directories, this would make
sure we don't run any
<br>
> + tests we don't need to.
<br>
> +
<br>
> + In this case, replace the "report" and
"report.simple.csv" make targest on
<br>
> + the command line with
"Output/254.gap.simple.exec.txt".
<br>
> +
<br>
> + #. Make sure your test predicate removes the Output
directory and any ``report...`` files (if
<br>
> + you forget this, you won't end up rebuilding the
test with the right compiler).
<br>
> +
<br>
> + #. Add a grep for "TEST-PASS" of the report file.
<br>
> +
<br>
> +An example of what the final bisect command might
look like::
<br>
> +
<br>
> + $ llvmlab bisect /bin/sh -c \
<br>
> + 'rm -rf report.* Output && \
<br>
> + "make" "-k" "-j" "1"
"Output/254.gap.simple.exec.txt" \
<br>
> + "TARGET_LLVMGCC=%(path)s/bin/clang" \
<br>
> + "CC_UNDER_TEST_TARGET_IS_I386=1"
"ENABLE_HASHED_PROGRAM_OUTPUT=1" "TARGET_CXX=None" \
<br>
> + "LLI_OPTFLAGS=-O0" "TARGET_CC=None" \
<br>
> + "TARGET_LLVMGXX=%(path)s/bin/clang++" \
<br>
> + "TEST=simple" "CC_UNDER_TEST_IS_CLANG=1"
"TARGET_LLCFLAGS=" "TARGET_FLAGS=-g -arch i386" \
<br>
> + "USE_REFERENCE_OUTPUT=1" "OPTFLAGS=-O0"
"SMALL_PROBLEM_SIZE=1" "LLC_OPTFLAGS=-O0" \
<br>
> + "ENABLE_OPTIMIZED=1" "ARCH=x86" "DISABLE_CBE=1"
"DISABLE_JIT=1" && \
<br>
> + grep "TEST-PASS" "Output/254.gap.simple.exec.txt"'
<br>
> +
<br>
> +
<br>
> +Nightly Test Performance Regressions
<br>
> +++++++++++++++++++++++++++++++++++++
<br>
> +
<br>
> +This is similar to the problem of bisecting nightly
test above, but made more
<br>
> +complicated because the test predicate needs to do a
comparison on the
<br>
> +performance result.
<br>
> +
<br>
> +One way to do this is to extract a script which
reproduces the performance
<br>
> +regression, and use a filter expression as described
previously. However, this
<br>
> +requires extracting the exact commands which are run
by the ``test-suite``
<br>
> +Makefiles.
<br>
> +
<br>
> +A simpler way is to use the
``test-suite/tools/get-report-time`` script in
<br>
> +conjunction with a standard Unix command line tool
like ``expr`` to do the
<br>
> +performance comparison.
<br>
> +
<br>
> +The basic process is similar to the one above, the
differences are that instead
<br>
> +of just using ``grep`` to check the output, we use
the ``get-report-time`` tool
<br>
> +and a quick script using ``bc`` to compare the
result. Here is an example::
<br>
> +
<br>
> + $ llvmlab bisect -s sandbox /bin/sh -c \
<br>
> + 'set -ex; \
<br>
> + rm -rf Output && \
<br>
> + "make" "-k" "-j" "1"
"Output/security-rijndael.simple.compile.report.txt" \
<br>
> + "TARGET_LLVMGCC=%(path)s/bin/clang"
"ENABLE_HASHED_PROGRAM_OUTPUT=1" "TARGET_CXX=None" \
<br>
> + "LLI_OPTFLAGS=-O0" "TARGET_CC=None" \
<br>
> + "TARGET_LLVMGXX=%(path)s/bin/clang++" \
<br>
> + "TEST=simple" "CC_UNDER_TEST_IS_CLANG=1"
"ENABLE_PARALLEL_REPORT=1" "TARGET_FLAGS=-g" \
<br>
> + "USE_REFERENCE_OUTPUT=1"
"CC_UNDER_TEST_TARGET_IS_X86_64=1" "OPTFLAGS=-O0" \
<br>
> + "LLC_OPTFLAGS=-O0" "ENABLE_OPTIMIZED=1"
"ARCH=x86_64" "DISABLE_CBE=1" "DISABLE_JIT=1" && \
<br>
> + ./check-value.sh'
<br>
> +
<br>
> +Where ``check-value.sh`` looks like this::
<br>
> +
<br>
> + #!/bin/sh -x
<br>
> +
<br>
> +
cmd1=`/Volumes/Data/sources/llvm/projects/test-suite/tools/get-report-time
\
<br>
> + Output/security-rijndael.simple.compile.report.txt`
<br>
> + cmd2=`echo "$cmd1 < 0.42" | bc -l`
<br>
> +
<br>
> + if [ $cmd2 == '1' ]; then
<br>
> + exit 0
<br>
> + fi
<br>
> +
<br>
> + exit 1
<br>
> +
<br>
> +Another trick this particular example uses is using
the bash ``set -x`` command
<br>
> +to log the commands which get run. In this case,
this allows us to inspect the
<br>
> +log files in the ``sandbox`` directory and see what
the time used in the
<br>
> +``expr`` comparison was. This is handy in case we
aren't exactly sure if the
<br>
> +comparison time we used is correct.
<br>
> +
<br>
> +
<br>
> +Tests With Interactive Steps
<br>
> +++++++++++++++++++++++++++++
<br>
> +
<br>
> +Sometimes test predicates require some steps that
must be performed
<br>
> +interactively or are too hard to automate in a test
script.
<br>
> +
<br>
> +In such cases its still possible to use llvmlab
bisect by writing the test
<br>
> +script in such a way that it will wait for the user
to inform it whether the
<br>
> +test passed or failed. For example, here is a real
test script that was used to
<br>
> +bisect where I was running a GUI app to check for
distorted colors as part of
<br>
> +the test step.
<br>
> +
<br>
> +After each step, the GUI app would be launched, I
would check the colors, and
<br>
> +then type in "yes" or "no" based on whether the app
worked or not. Note that
<br>
> +because llvmlab bisect hides the test output by
default, the prompt itself
<br>
> +doesn't show up, but the command still can read
stdin.
<br>
> +
<br>
> +Here is the test script::
<br>
> +
<br>
> + #!/bin/sh
<br>
> +
<br>
> + git reset --hard
<br>
> +
<br>
> + CC=clang
<br>
> + COMPILE HERE
<br>
> + sudo ditto built_files/ /
<br>
> +
<br>
> + open /Applications/GUIApp
<br>
> +
<br>
> + while true; do
<br>
> + read -p "OK?" is_ok
<br>
> + if [ "$is_ok" == "yes" ]; then
<br>
> + echo "OK!"
<br>
> + exit 0
<br>
> + elif [ "$is_ok" == "no" ]; then
<br>
> + echo "FAILED!"
<br>
> + exit 1
<br>
> + else
<br>
> + echo "Answer yes or no you!";
<br>
> + fi
<br>
> + done
<br>
> +
<br>
> +And here is log showing the transcript of the
bisect::
<br>
> +
<br>
> + bash-3.2# ~admin/zorg/utils/llvmlab bisect
--max-rev 131837 ./test.sh
<br>
> + no
<br>
> + FAIL: clang-r131837-b8165
<br>
> + no
<br>
> + FAIL: clang-r131835-b8164
<br>
> + no
<br>
> + FAIL: clang-r131832-b8162
<br>
> + no
<br>
> + FAIL: clang-r131828-b8158
<br>
> + yes
<br>
> + PASS: clang-r131795-b8146
<br>
> + no
<br>
> + FAIL: clang-r131809-b8151
<br>
> + no
<br>
> + FAIL: clang-r131806-b8149
<br>
> + no
<br>
> + FAIL: clang-r131801-b8147
<br>
> + clang-r131795-b8146: first working build
<br>
> + clang-r131801-b8147: next failing build
<br>
> +
<br>
> +Note that it is very easy to make a mistake and type
the wrong answer when
<br>
> +following this process, in which case the bisect
will come up with the wrong
<br>
> +answer. It's always worth sanity checking the
results (e.g., using ``llvmlab
<br>
> +ci exec``) after the bisect is complete.
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/__init__.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/__init__.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/__init__.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/__init__.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/__init__.py Thu Oct
8 16:52:50 2015
<br>
> @@ -0,0 +1 @@
<br>
> +""""""
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/algorithm.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/algorithm.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/algorithm.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/algorithm.py
(added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/algorithm.py Thu
Oct 8 16:52:50 2015
<br>
> @@ -0,0 +1,85 @@
<br>
> +"""Handy algorithms."""
<br>
> +
<br>
> +
<br>
> +def bisect(predicate, list):
<br>
> + """
<br>
> + bisect(predicate, list) -> item or None
<br>
> +
<br>
> + Given a test predicate and a list of items, search
return the first item in
<br>
> + the list for which the predicate succeeds, or None
if no such item is
<br>
> + found.
<br>
> +
<br>
> + The list is assumed to be ordered such that
(predicate(i) for i in list) is
<br>
> + monotonic. If this condition is not met, the
returned item is guaranteed to
<br>
> + satisfy the predicate and the item preceeding it is
guaranteed to fail the
<br>
> + predicate, but that is all. Additionally, if the
last item does not pass
<br>
> + the predicate, such an item might not be found.
<br>
> +
<br>
> + This function is optimized for the case where the
searched for item is near
<br>
> + the beginning of the list.
<br>
> + """
<br>
> +
<br>
> + if not list:
<br>
> + return None
<br>
> +
<br>
> + lo = 0
<br>
> + hi = len(list)-1
<br>
> +
<br>
> + # Check first item immediately.
<br>
> + if predicate(list[lo]):
<br>
> + return list[lo]
<br>
> +
<br>
> + # Invariants:
<br>
> + # not predicate(list[lo])
<br>
> + # predicate(list[hi])
<br>
> +
<br>
> + # Binary search region.
<br>
> + while lo + 1 != hi:
<br>
> + mid = (lo + hi) // 2
<br>
> + if predicate(list[mid]):
<br>
> + hi = mid
<br>
> + else:
<br>
> + lo = mid
<br>
> +
<br>
> + return list[hi]
<br>
> +
<br>
> +
<br>
> +def gallop(predicate, list):
<br>
> + """
<br>
> + gallop(predicate, list) -> list or None
<br>
> +
<br>
> + Given a test predicate and a list of items, reduce
the search space
<br>
> + assuming the searched for item is near the
beginning of the list.
<br>
> +
<br>
> + The list is assumed to be ordered such that
(predicate(i) for i in list) is
<br>
> + monotonic. If this condition is not met, the
returned item is guaranteed to
<br>
> + satisfy the predicate and the item preceeding it is
guaranteed to fail the
<br>
> + predicate, but that is all. Additionally, if the
last item does not pass
<br>
> + the predicate, such an item might not be found.
<br>
> + """
<br>
> +
<br>
> + if not list:
<br>
> + return None
<br>
> +
<br>
> + # Check first item immediately.
<br>
> + if predicate(list[0]):
<br>
> + return list[0:1]
<br>
> +
<br>
> + # Invariants:
<br>
> + # not predicate(list[lo])
<br>
> +
<br>
> + # Gallop to find initial search range, under the
assumption that we are
<br>
> + # most likely looking for something at the head of
this list.
<br>
> + lo = 0
<br>
> + hi = 1
<br>
> + while hi < len(list):
<br>
> + if predicate(list[hi]):
<br>
> + break
<br>
> + lo, hi = hi, hi + (hi - lo)*2
<br>
> +
<br>
> + # If we galloped past the end, limit the hi range.
<br>
> + if hi >= len(list):
<br>
> + hi = len(list) - 1
<br>
> + if hi == lo or not predicate(list[hi]):
<br>
> + return None
<br>
> + return list[lo:hi+1]
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/ci.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/ci.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/ci.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/ci.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/ci.py Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,650 @@
<br>
> +"""
<br>
> +Tools for working with llvmlab CI infrastructure.
<br>
> +"""
<br>
> +
<br>
> +import errno
<br>
> +import os
<br>
> +import resource
<br>
> +import shutil
<br>
> +import subprocess
<br>
> +import sys
<br>
> +import tempfile
<br>
> +import time
<br>
> +
<br>
> +
<br>
> +from . import shell
<br>
> +from . import algorithm
<br>
> +from . import llvmlab
<br>
> +from . import util
<br>
> +from util import warning, fatal, note
<br>
> +from . import scripts
<br>
> +from . import util
<br>
> +
<br>
> +from optparse import OptionParser
<br>
> +
<br>
> +
<br>
> +class Command(object):
<br>
> + class Filter(object):
<br>
> + def __init__(self):
<br>
> + pass
<br>
> +
<br>
> + def evaluate(self, command):
<br>
> + raise RuntimeError("Abstract method.")
<br>
> +
<br>
> + class NotFilter(Filter):
<br>
> + def evaluate(self, command):
<br>
> + warning("'negate' filter is deprecated, use 'not
result' "
<br>
> + "filter expression")
<br>
> + command.result = not command.result
<br>
> +
<br>
> + class MaxTimeFilter(Filter):
<br>
> + def __init__(self, value):
<br>
> + try:
<br>
> + self.value = float(value)
<br>
> + except:
<br>
> + fatal("invalid argument: %r" % time)
<br>
> + warning("'max_time' filter is deprecated, use "
<br>
> + "'user_time < %.4f' filter expression" %
self.value)
<br>
> +
<br>
> + def evaluate(self, command):
<br>
> + if command.metrics["user_time"] >= self.value:
<br>
> + command.result = False
<br>
> +
<br>
> + available_filters = {"negate": NotFilter(), # note
this is an instance.
<br>
> + "max_time": MaxTimeFilter}
<br>
> +
<br>
> + def __init__(self, command, stdout_path,
stderr_path, env):
<br>
> + self.command = command
<br>
> + self.stdout_path = stdout_path
<br>
> + self.stderr_path = stderr_path
<br>
> + self.env = env
<br>
> +
<br>
> + # Test data.
<br>
> + self.metrics = {}
<br>
> + self.result = None
<br>
> +
<br>
> + def execute(self, verbose=False):
<br>
> + if verbose:
<br>
> + note('executing: %s' % ' '.join("'%s'" % arg
<br>
> + for arg in self.command))
<br>
> +
<br>
> + start_rusage =
resource.getrusage(resource.RUSAGE_CHILDREN)
<br>
> + start_time = time.time()
<br>
> +
<br>
> + p = subprocess.Popen(self.command,
<br>
> + stdout=open(self.stdout_path, 'w'),
<br>
> + stderr=open(self.stderr_path, 'w'),
<br>
> + env=self.env)
<br>
> + self.result = p.wait() == 0
<br>
> +
<br>
> + end_time = time.time()
<br>
> + end_rusage =
resource.getrusage(resource.RUSAGE_CHILDREN)
<br>
> + self.metrics["user_time"] = end_rusage.ru_utime -
start_rusage.ru_utime
<br>
> + self.metrics["sys_time"] = end_rusage.ru_stime -
start_rusage.ru_stime
<br>
> + self.metrics["wall_time"] = end_time - start_time
<br>
> +
<br>
> + if verbose:
<br>
> + note("command executed in -- "
<br>
> + "user: %.4fs, wall: %.4fs, sys: %.4fs" % (
<br>
> + self.metrics["user_time"],
self.metrics["wall_time"],
<br>
> + self.metrics["sys_time"]))
<br>
> +
<br>
> + def evaluate_filter_spec(self, spec):
<br>
> + # Run the filter in an environment with the builtin
filters and the
<br>
> + # metrics.
<br>
> + env = {"result": self.result}
<br>
> + env.update(self.available_filters)
<br>
> + env.update(self.metrics)
<br>
> + result = eval(spec, {}, env)
<br>
> +
<br>
> + # If the result is a filter object, evaluate it.
<br>
> + if isinstance(result, Command.Filter):
<br>
> + result.evaluate(self)
<br>
> + return
<br>
> +
<br>
> + # Otherwise, treat the result as a boolean
predicate.
<br>
> + self.result = bool(result)
<br>
> +
<br>
> +
<br>
> +def execute_sandboxed_test(sandbox, builder, build,
args,
<br>
> + verbose=False, very_verbose=False,
<br>
> + add_path_variables=True,
<br>
> + show_command_output=False,
<br>
> + reuse_sandbox=False):
<br>
> +
<br>
> + def split_command_filters(command):
<br>
> + for i, arg in enumerate(command):
<br>
> + if arg[:2] != "%%" or arg[-2:] != "%%":
<br>
> + break
<br>
> + else:
<br>
> + fatal("invalid command: %s, only contains filter "
<br>
> + "specifications" % ("".join('"%s"' % a for a in
command)))
<br>
> +
<br>
> + return ([a[2:-2] for a in command[:i]],
<br>
> + command[i:])
<br>
> +
<br>
> + path = build.tobasename(include_suffix=False)
<br>
> + fullpath = build.tobasename()
<br>
> +
<br>
> + if verbose:
<br>
> + note('testing %r' % path)
<br>
> +
<br>
> + # Create the sandbox directory, if it doesn't
exist.
<br>
> + is_temp = False
<br>
> + if sandbox is None:
<br>
> + sandbox = tempfile.mkdtemp()
<br>
> + is_temp = True
<br>
> + else:
<br>
> + # Make absolute.
<br>
> + sandbox = os.path.abspath(sandbox)
<br>
> + if not os.path.exists(sandbox):
<br>
> + os.mkdir(sandbox)
<br>
> +
<br>
> + # Compute paths and make sure sandbox is clean.
<br>
> + root_path = os.path.join(sandbox, fullpath)
<br>
> + builddir_path = os.path.join(sandbox, path)
<br>
> + need_build = True
<br>
> + if reuse_sandbox and (os.path.exists(root_path) and
<br>
> + os.path.exists(builddir_path)):
<br>
> + need_build = False
<br>
> + else:
<br>
> + for p in (root_path, builddir_path):
<br>
> + if os.path.exists(p):
<br>
> + fatal('sandbox is not clean, %r exists' % p)
<br>
> +
<br>
> + # Fetch and extract the build.
<br>
> + if need_build:
<br>
> + start_time = time.time()
<br>
> + llvmlab.fetch_build_to_path(builder, build,
root_path, builddir_path)
<br>
> + if very_verbose:
<br>
> + note("extracted build in %.2fs" % (time.time() -
start_time,))
<br>
> +
<br>
> + # Attempt to find clang/clang++ in the downloaded
build.
<br>
> + def find_binary(name):
<br>
> + x = subprocess.check_output(['find', builddir_path,
'-name', name])\
<br>
> + .strip().split("\n")[0]
<br>
> + if x == '':
<br>
> + x = None
<br>
> + return x
<br>
> +
<br>
> + clang_path = find_binary('clang')
<br>
> + clangpp_path = find_binary('clang++')
<br>
> + liblto_path = find_binary('libLTO.dylib')
<br>
> + if liblto_path is not None:
<br>
> + liblto_dir = os.path.dirname(liblto_path)
<br>
> + else:
<br>
> + liblto_dir = None
<br>
> +
<br>
> + # Construct the interpolation variables.
<br>
> + options = {'sandbox': sandbox,
<br>
> + 'path': builddir_path,
<br>
> + 'revision': build.revision,
<br>
> + 'build': build.build,
<br>
> + 'clang': clang_path,
<br>
> + 'clang++': clangpp_path,
<br>
> + 'libltodir': liblto_dir}
<br>
> +
<br>
> + # Inject environment variables.
<br>
> + env = os.environ.copy()
<br>
> + for key, value in options.items():
<br>
> + env['TEST_%s' % key.upper()] = str(value)
<br>
> +
<br>
> + # Extend the environment to include the path to the
extracted build.
<br>
> + #
<br>
> + # FIXME: Ideally, we would be able to read some
kind of configuration
<br>
> + # notermation about a builder so that we could just
set this up, it doesn't
<br>
> + # necessarily here as hard-coded notermation.
<br>
> + if add_path_variables:
<br>
> + path_extensions = []
<br>
> + dyld_library_path_extensions = []
<br>
> + toolchains_dir = os.path.join(builddir_path,
<br>
> + ('Applications/Xcode.app/Contents/'
<br>
> + 'Developer/Toolchains'))
<br>
> + toolchain_paths = []
<br>
> + if os.path.exists(toolchains_dir):
<br>
> + toolchain_paths = [os.path.join(toolchains_dir,
name, 'usr')
<br>
> + for name in os.listdir(toolchains_dir)]
<br>
> + for package_root in ['', 'Developer/usr/'] +
toolchain_paths:
<br>
> + p = os.path.join(builddir_path, package_root,
'bin')
<br>
> + if os.path.exists(p):
<br>
> + path_extensions.append(p)
<br>
> + p = os.path.join(builddir_path, package_root,
'lib')
<br>
> + if os.path.exists(p):
<br>
> + dyld_library_path_extensions.append(p)
<br>
> + if path_extensions:
<br>
> + env['PATH'] = os.pathsep.join(
<br>
> + path_extensions + [os.environ.get('PATH', '')])
<br>
> + if dyld_library_path_extensions:
<br>
> + env['DYLD_LIBRARY_PATH'] = os.pathsep.join(
<br>
> + dyld_library_path_extensions + [
<br>
> + os.environ.get('DYLD_LIBRARY_PATH', '')])
<br>
> +
<br>
> + # Split the arguments into distinct commands.
<br>
> + #
<br>
> + # Extended command syntax allows running multiple
commands by separating
<br>
> + # them with '----'.
<br>
> + test_commands = util.list_split(args, "----")
<br>
> +
<br>
> + # Split command specs into filters and commands.
<br>
> + test_commands = [split_command_filters(spec) for
spec in test_commands]
<br>
> +
<br>
> + # Execute the test.
<br>
> + command_objects = []
<br>
> + interpolated_variables = False
<br>
> + for i, (filters, command) in
enumerate(test_commands):
<br>
> + # Interpolate arguments.
<br>
> + old_command = command
<br>
> + command = [a % options for a in command]
<br>
> + if old_command != command:
<br>
> + interpolated_variables = True
<br>
> +
<br>
> + # Create the command object...
<br>
> + stdout_log_path = os.path.join(sandbox,
'%s.%d.stdout' % (path, i))
<br>
> + stderr_log_path = os.path.join(sandbox,
'%s.%d.stderr' % (path, i))
<br>
> + cmd_object = Command(command, stdout_log_path,
stderr_log_path, env)
<br>
> + command_objects.append(cmd_object)
<br>
> +
<br>
> + # Execute the command.
<br>
> + try:
<br>
> + cmd_object.execute(verbose=verbose)
<br>
> + except OSError, e:
<br>
> + # Python's exceptions are horribly to read, and
this one is
<br>
> + # incredibly common when people don't use the right
syntax (or
<br>
> + # misspell something) when writing a predicate.
Detect this and
<br>
> + # notify the user.
<br>
> + if e.errno == errno.ENOENT:
<br>
> + fatal("invalid command, executable doesn't exist:
%r" % (
<br>
> + cmd_object.command[0],))
<br>
> + elif e.errno == errno.ENOEXEC:
<br>
> + fatal("invalid command, executable has a bad
format. Did you "
<br>
> + "forget to put a #! at the top of a script?: %r"
<br>
> + % (cmd_object.command[0],))
<br>
> + else:
<br>
> + # Otherwise raise the error again.
<br>
> + raise e
<br>
> +
<br>
> + # Evaluate the filters.
<br>
> + for filter in filters:
<br>
> + cmd_object.evaluate_filter_spec(filter)
<br>
> +
<br>
> + if show_command_output:
<br>
> + for p, type in ((stdout_log_path, "stdout"),
<br>
> + (stderr_log_path, "stderr")):
<br>
> + if not os.path.exists(p):
<br>
> + continue
<br>
> +
<br>
> + f = open(p)
<br>
> + data = f.read()
<br>
> + f.close()
<br>
> + if data:
<br>
> + print ("-- command %s (note: suppressed by default,
"
<br>
> + "see sandbox dir for log files) --" % (type))
<br>
> + print "--\n%s--\n" % data
<br>
> +
<br>
> + test_result = cmd_object.result
<br>
> + if not test_result:
<br>
> + break
<br>
> + if not interpolated_variables:
<br>
> + warning('no substitutions found. Fetched root
ignored?')
<br>
> +
<br>
> + # Remove the temporary directory.
<br>
> + if is_temp:
<br>
> + if shell.execute(['rm', '-rf', sandbox]) != 0:
<br>
> + note('unable to remove sandbox dir %r' % path)
<br>
> +
<br>
> + return test_result, command_objects
<br>
> +
<br>
> +
<br>
> +def get_best_match(builds, name, key=lambda x: x):
<br>
> + builds = list(builds)
<br>
> + builds.sort(key=key)
<br>
> +
<br>
> + if name is None and builds:
<br>
> + return builds[-1]
<br>
> +
<br>
> + to_find = llvmlab.Build.frombasename(name, None)
<br>
> +
<br>
> + best = None
<br>
> + for item in builds:
<br>
> + build = key(item)
<br>
> + # Check for a prefix match.
<br>
> + path = build.tobasename()
<br>
> + if path.startswith(name):
<br>
> + return item
<br>
> +
<br>
> + # Check for a revision match.
<br>
> + if build.revision == to_find.revision and
build.revision is not None:
<br>
> + return item
<br>
> +
<br>
> + # Otherwise, stop when we aren't getting closer.
<br>
> + if build > to_find:
<br>
> + break
<br>
> + best = item
<br>
> +
<br>
> + return best
<br>
> +
<br>
> +
<br>
> +def action_fetch(name, args):
<br>
> + """fetch a build from the server"""
<br>
> +
<br>
> + parser = OptionParser("""\
<br>
> +usage: %%prog %(name)s [options] builder
[build-name]
<br>
> +
<br>
> +Fetch the build from the named builder which matchs
build-name. If no match is
<br>
> +found, get the first build before the given name. If
no build name is given,
<br>
> +the most recent build is fetched.
<br>
> +
<br>
> +The available builders can be listed using:
<br>
> +
<br>
> + %%prog ci ls""" % locals())
<br>
> + parser.add_option("-f", "--force", dest="force",
<br>
> + help=("always download and extract, overwriting
any"
<br>
> + "existing files"),
<br>
> + action="store_true", default=False)
<br>
> + parser.add_option("", "--update-link",
dest="update_link", metavar="PATH",
<br>
> + help=("update a symbolic link at PATH to point to
the "
<br>
> + "fetched build (on success)"),
<br>
> + action="store", default=None)
<br>
> + parser.add_option("-d", "--dry-run",
dest='dry_run',
<br>
> + help=("Perform all operations except the actual "
<br>
> + "downloading and extracting of any files"),
<br>
> + action="store_true", default=False)
<br>
> +
<br>
> + (opts, args) = parser.parse_args(args)
<br>
> +
<br>
> + if len(args) == 0:
<br>
> + parser.error("please specify a builder name")
<br>
> + elif len(args) == 1:
<br>
> + builder, = args
<br>
> + build_name = None
<br>
> + elif len(args) == 2:
<br>
> + builder, build_name = args
<br>
> + else:
<br>
> + parser.error("invalid number of arguments")
<br>
> +
<br>
> + builds = list(llvmlab.fetch_builds(builder))
<br>
> + if not builds:
<br>
> + parser.error("no builds for builder: %r" % builder)
<br>
> +
<br>
> + build = get_best_match(builds, build_name)
<br>
> + if not build:
<br>
> + parser.error("no match for build %r" % build_name)
<br>
> +
<br>
> + path = build.tobasename()
<br>
> + if build_name is not None and not
path.startswith(build_name):
<br>
> + note('no exact match, fetching %r' % path)
<br>
> +
<br>
> + # Get the paths to extract to.
<br>
> + root_path = path
<br>
> + builddir_path =
build.tobasename(include_suffix=False)
<br>
> +
<br>
> + if not opts.dry_run:
<br>
> + # Check that the download and extract paths are
clean.
<br>
> + for p in (root_path, builddir_path):
<br>
> + if os.path.exists(p):
<br>
> + # If we are using --force, then clean the path.
<br>
> + if opts.force:
<br>
> + shutil.rmtree(p, ignore_errors=True)
<br>
> + continue
<br>
> + fatal('current directory is not clean, %r exists' %
p)
<br>
> + llvmlab.fetch_build_to_path(builder, build,
root_path, builddir_path)
<br>
> +
<br>
> + print 'downloaded root: %s' % root_path
<br>
> + print 'extracted path : %s' % builddir_path
<br>
> +
<br>
> + # Update the symbolic link, if requested.
<br>
> + if not opts.dry_run and opts.update_link:
<br>
> + # Remove the existing path.
<br>
> + try:
<br>
> + os.unlink(opts.update_link)
<br>
> + except OSError as e:
<br>
> + if e.errno != errno.ENOENT:
<br>
> + fatal('unable to update symbolic link at %r, cannot
unlink' % (
<br>
> + opts.update_link))
<br>
> +
<br>
> + # Create the symbolic link.
<br>
> + os.symlink(os.path.abspath(builddir_path),
opts.update_link)
<br>
> + print 'updated link at: %s' % opts.update_link
<br>
> + return os.path.abspath(builddir_path)
<br>
> +
<br>
> +
<br>
> +def action_ls(name, args):
<br>
> + """list available build names or builds"""
<br>
> +
<br>
> + parser = OptionParser("""\
<br>
> +usage: %%prog %s [build-name]
<br>
> +
<br>
> +With no arguments, list the available build names on
'llvmlab'. With a build
<br>
> +name, list the available builds for that builder.\
<br>
> +""" % name)
<br>
> +
<br>
> + (opts, args) = parser.parse_args(args)
<br>
> +
<br>
> + if not len(args):
<br>
> + available_buildnames = llvmlab.fetch_builders()
<br>
> + available_buildnames.sort()
<br>
> + for item in available_buildnames:
<br>
> + print item
<br>
> + return available_buildnames
<br>
> +
<br>
> + for name in args:
<br>
> + if len(args) > 1:
<br>
> + if name is not args[0]:
<br>
> + print
<br>
> + print '%s:' % name
<br>
> + available_builds = list(llvmlab.fetch_builds(name))
<br>
> + available_builds.sort()
<br>
> + available_builds.reverse()
<br>
> + for build in available_builds:
<br>
> + print build.tobasename(include_suffix=False)
<br>
> + min_rev = min([x.revision for x in
available_builds])
<br>
> + max_rev = max([x.revision for x in
available_builds])
<br>
> + note("Summary: found {} builds:
r{}-r{}".format(len(available_builds),
<br>
> + min_rev, max_rev))
<br>
> + return available_builds
<br>
> +
<br>
> +DEFAULT_BUILDER = "clang-stage1-configure-RA_build"
<br>
> +
<br>
> +
<br>
> +def action_bisect(name, args):
<br>
> + """find first failing build using binary search"""
<br>
> +
<br>
> + parser = OptionParser("""\
<br>
> +usage: %%prog %(name)s [options] ... test command
args ...
<br>
> +
<br>
> +Look for the first published build where a test
failed, using the builds on
<br>
> +llvmlab. The command arguments are executed once per
build tested, but each
<br>
> +argument is first subject to string interpolation.
The syntax is
<br>
> +"%%(VARIABLE)FORMAT" where FORMAT is a standard
printf format, and VARIABLE is
<br>
> +one of:
<br>
> +
<br>
> + 'sandbox' - the path to the sandbox directory.
<br>
> + 'path' - the path to the build under test.
<br>
> + 'revision' - the revision number of the build.
<br>
> + 'build' - the build number of the build under test.
<br>
> + 'clang' - the path to the clang binary of the build
if it exists.
<br>
> + 'clang++' - the path to the clang++ binary of the
build if it exists.
<br>
> + 'libltodir' - the path to the directory containing
libLTO.dylib, if it
<br>
> + exists.
<br>
> +
<br>
> +Each test is run in a sandbox directory. By default,
sandbox directories are
<br>
> +temporary directories which are created and
destroyed for each test (see
<br>
> +--sandbox).
<br>
> +
<br>
> +For use in auxiliary test scripts, each test is also
run with each variable
<br>
> +available in the environment as TEST_<variable
name> (variables are converted
<br>
> +to uppercase). For example, a test script could use
"TEST_PATH" to find the
<br>
> +path to the build under test.
<br>
> +
<br>
> +The stdout and stderr of the command are logged to
files inside the sandbox
<br>
> +directory. Use an explicit sandbox directory if you
would like to look at
<br>
> +them.
<br>
> +
<br>
> +It is possible to run multiple distinct commands for
each test by separating
<br>
> +them in the command line arguments by '----'. The
failure of any command causes
<br>
> +the entire test to fail.\
<br>
> +""" % locals())
<br>
> +
<br>
> + parser.add_option("-b", "--build",
dest="build_name", metavar="STR",
<br>
> + help="name of build to fetch",
<br>
> + action="store", default=DEFAULT_BUILDER)
<br>
> + parser.add_option("-s", "--sandbox",
dest="sandbox",
<br>
> + help="directory to use as a sandbox",
<br>
> + action="store", default=None)
<br>
> + parser.add_option("-v", "--verbose",
dest="verbose",
<br>
> + help="output more test notermation",
<br>
> + action="store_true", default=False)
<br>
> + parser.add_option("-V", "--very-verbose",
dest="very_verbose",
<br>
> + help="output even more test notermation",
<br>
> + action="store_true", default=False)
<br>
> + parser.add_option("", "--show-output",
dest="show_command_output",
<br>
> + help="display command output",
<br>
> + action="store_true", default=False)
<br>
> + parser.add_option("", "--single-step",
dest="single_step",
<br>
> + help="single step instead of binary stepping",
<br>
> + action="store_true", default=False)
<br>
> + parser.add_option("", "--min-rev", dest="min_rev",
<br>
> + help="minimum revision to test",
<br>
> + type="int", action="store", default=None)
<br>
> + parser.add_option("", "--max-rev", dest="max_rev",
<br>
> + help="maximum revision to test",
<br>
> + type="int", action="store", default=None)
<br>
> +
<br>
> + parser.disable_interspersed_args()
<br>
> +
<br>
> + (opts, args) = parser.parse_args(args)
<br>
> +
<br>
> + if opts.build_name is None:
<br>
> + parser.error("no build name given (see --build)")
<br>
> +
<br>
> + # Very verbose implies verbose.
<br>
> + opts.verbose |= opts.very_verbose
<br>
> +
<br>
> + start_time = time.time()
<br>
> + available_builds =
list(llvmlab.fetch_builds(opts.build_name))
<br>
> + available_builds.sort()
<br>
> + available_builds.reverse()
<br>
> + if opts.very_verbose:
<br>
> + note("fetched builds in %.2fs" % (time.time() -
start_time,))
<br>
> +
<br>
> + if opts.min_rev is not None:
<br>
> + available_builds = [b for b in available_builds
<br>
> + if b.revision >= opts.min_rev]
<br>
> + if opts.max_rev is not None:
<br>
> + available_builds = [b for b in available_builds
<br>
> + if b.revision <= opts.max_rev]
<br>
> +
<br>
> + def predicate(item):
<br>
> + # Run the sandboxed test.
<br>
> + test_result, _ = execute_sandboxed_test(
<br>
> + opts.sandbox, opts.build_name, item, args,
verbose=opts.verbose,
<br>
> + very_verbose=opts.very_verbose,
<br>
> + show_command_output=opts.show_command_output or
opts.very_verbose)
<br>
> +
<br>
> + # Print status.
<br>
> + print '%s: %s' % (('FAIL', 'PASS')[test_result],
<br>
> + item.tobasename(include_suffix=False))
<br>
> +
<br>
> + return test_result
<br>
> +
<br>
> + if opts.single_step:
<br>
> + for item in available_builds:
<br>
> + if predicate(item):
<br>
> + break
<br>
> + else:
<br>
> + item = None
<br>
> + else:
<br>
> + if opts.min_rev is None or opts.max_rev is None:
<br>
> + # Gallop to find initial search range, under the
assumption that we
<br>
> + # are most likely looking for something at the head
of this list.
<br>
> + search_space = algorithm.gallop(predicate,
available_builds)
<br>
> + else:
<br>
> + # If both min and max revisions are specified,
<br>
> + # don't gallop - bisect the given range.
<br>
> + search_space = available_builds
<br>
> + item = algorithm.bisect(predicate, search_space)
<br>
> +
<br>
> + if item is None:
<br>
> + fatal('unable to find any passing build!')
<br>
> +
<br>
> + print '%s: first working build' %
item.tobasename(include_suffix=False)
<br>
> + index = available_builds.index(item)
<br>
> + if index == 0:
<br>
> + print 'no failing builds!?'
<br>
> + else:
<br>
> + print '%s: next failing build' %
available_builds[index-1].tobasename(
<br>
> + include_suffix=False)
<br>
> +
<br>
> +
<br>
> +def action_exec(name, args):
<br>
> + """execute a command against a published root"""
<br>
> +
<br>
> + parser = OptionParser("""\
<br>
> +usage: %%prog %(name)s [options] ... test command
args ...
<br>
> +
<br>
> +Executes the given command against the latest
published build. The syntax for
<br>
> +commands (and exit code) is exactly the same as for
the 'bisect' tool, so this
<br>
> +command is useful for testing bisect test commands.
<br>
> +
<br>
> +See 'bisect' for more notermation on the exact test
syntax.\
<br>
> +""" % locals())
<br>
> +
<br>
> + parser.add_option("-b", "--build",
dest="build_name", metavar="STR",
<br>
> + help="name of build to fetch",
<br>
> + action="store", default=DEFAULT_BUILDER)
<br>
> + parser.add_option("-s", "--sandbox",
dest="sandbox",
<br>
> + help="directory to use as a sandbox",
<br>
> + action="store", default=None)
<br>
> + parser.add_option("", "--min-rev", dest="min_rev",
<br>
> + help="minimum revision to test",
<br>
> + type="int", action="store", default=None)
<br>
> + parser.add_option("", "--max-rev", dest="max_rev",
<br>
> + help="maximum revision to test",
<br>
> + type="int", action="store", default=None)
<br>
> + parser.add_option("", "--near", dest="near_build",
<br>
> + help="use a build near NAME",
<br>
> + type="str", action="store", metavar="NAME",
default=None)
<br>
> +
<br>
> + parser.disable_interspersed_args()
<br>
> +
<br>
> + (opts, args) = parser.parse_args(args)
<br>
> +
<br>
> + if opts.build_name is None:
<br>
> + parser.error("no build name given (see --build)")
<br>
> +
<br>
> + available_builds =
list(llvmlab.fetch_builds(opts.build_name))
<br>
> + available_builds.sort()
<br>
> + available_builds.reverse()
<br>
> +
<br>
> + if opts.min_rev is not None:
<br>
> + available_builds = [b for b in available_builds
<br>
> + if b.revision >= opts.min_rev]
<br>
> + if opts.max_rev is not None:
<br>
> + available_builds = [b for b in available_builds
<br>
> + if b.revision <= opts.max_rev]
<br>
> +
<br>
> + if len(available_builds) == 0:
<br>
> + fatal("No builds available for builder name: %s" %
opts.build_name)
<br>
> +
<br>
> + # Find the best match, if requested.
<br>
> + if opts.near_build:
<br>
> + build = get_best_match(available_builds,
opts.near_build)
<br>
> + if not build:
<br>
> + parser.error("no match for build %r" %
opts.near_build)
<br>
> + else:
<br>
> + # Otherwise, take the latest build.
<br>
> + build = available_builds[0]
<br>
> +
<br>
> + test_result, _ = execute_sandboxed_test(
<br>
> + opts.sandbox, opts.build_name, build, args,
verbose=True,
<br>
> + show_command_output=True)
<br>
> +
<br>
> + print '%s: %s' % (('FAIL', 'PASS')[test_result],
<br>
> + build.tobasename(include_suffix=False))
<br>
> +
<br>
> + raise SystemExit(test_result != True)
<br>
> +
<br>
> +
<br>
> +def action_test(name, args):
<br>
> + from . import test_llvmlab
<br>
> + test_llvmlab.run_tests()
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/clang_link
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/clang_link?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/clang_link?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/clang_link (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/clang_link Thu Oct
8 16:52:50 2015
<br>
> @@ -0,0 +1 @@
<br>
> +link
/Users/cmatthews/src/zorg/llvmbisect/llvmlab/clang-r219899-t2014-10-15_20-42-53-b808<br>
> \ No newline at end of file
<br>
>
<br>
> Propchange: zorg/trunk/llvmbisect/llvmlab/clang_link
<br>
>
------------------------------------------------------------------------------<br>
> svn:special = *
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/gcs.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/gcs.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/gcs.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/gcs.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/gcs.py Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,53 @@
<br>
> +"""Integration with Google Cloud Storage.
<br>
> +
<br>
> +"""
<br>
> +import os
<br>
> +import requests
<br>
> +
<br>
> +# Root URL to use for our queries.
<br>
> +GCS = <a class="moz-txt-link-rfc2396E" href="https://www.googleapis.com/storage/v1/">"https://www.googleapis.com/storage/v1/"</a>
<br>
> +
<br>
> +DEFAULT_BUCKET = "llvm-build-artifacts"
<br>
> +
<br>
> +BUCKET = os.getenv("BUCKET", DEFAULT_BUCKET)
<br>
> +
<br>
> +
<br>
> +def fetch_builders():
<br>
> + """Each build kind is stored as a folder in the GCS
bucket.
<br>
> + List all the folders in the bucket, which is our
list of possible
<br>
> + compilers.
<br>
> + """
<br>
> + params = {'delimiter': "/", 'fields': "prefixes"}
<br>
> + r = requests.get(GCS + "b/" + BUCKET + "/o",
params=params)
<br>
> + r.raise_for_status()
<br>
> + reply_data = r.json()
<br>
> + folders = reply_data['prefixes']
<br>
> + no_slashes = [x.replace("/", "") for x in folders]
<br>
> + return no_slashes
<br>
> +
<br>
> +
<br>
> +def fetch_builds(project):
<br>
> + """Given a builder name, get the list of all the
files stored for that
<br>
> + builder.
<br>
> + """
<br>
> + assert project is not None
<br>
> + params = {'delimiter': "/",
<br>
> + "fields": "kind,items(name, mediaLink)",
<br>
> + 'prefix': project + "/"}
<br>
> + r = requests.get(GCS + "b/" + BUCKET + "/o",
params=params)
<br>
> + r.raise_for_status()
<br>
> + reply_data = r.json()
<br>
> + return reply_data
<br>
> +
<br>
> +# Dunno what this could be moved up to?
<br>
> +CHUNK_SIZE = 5124288
<br>
> +
<br>
> +
<br>
> +def get_compiler(url, filename):
<br>
> + """Get the compiler at the url, and save to
filename."""
<br>
> + r = requests.get(url)
<br>
> + r.raise_for_status()
<br>
> + with open(filename, 'wb') as fd:
<br>
> + for chunk in r.iter_content(CHUNK_SIZE):
<br>
> + fd.write(chunk)
<br>
> + return filename
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/llvmlab.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/llvmlab.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/llvmlab.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/llvmlab.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/llvmlab.py Thu Oct
8 16:52:50 2015
<br>
> @@ -0,0 +1,272 @@
<br>
> +"""Utilities for accessing stuff from llvmlab."""
<br>
> +
<br>
> +import json
<br>
> +import os
<br>
> +import re
<br>
> +import shutil
<br>
> +import time
<br>
> +
<br>
> +from . import shell
<br>
> +from . import util
<br>
> +from . import gcs
<br>
> +
<br>
> +from util import fatal
<br>
> +
<br>
> +
<br>
> +class BuilderMap(object):
<br>
> + # Expire the buildermap after 24 hours.
<br>
> + expiration_time = 24 * 60 * 60
<br>
> +
<br>
> + @classmethod
<br>
> + def frompath(klass, path):
<br>
> + with open(path) as f:
<br>
> + data = json.load(f)
<br>
> + return klass(data['builders'], data['timestamp'])
<br>
> +
<br>
> + def __init__(self, builders, timestamp):
<br>
> + self.builders = builders
<br>
> + self.timestamp = timestamp
<br>
> +
<br>
> + def topath(self, path):
<br>
> + with open(path, 'w') as f:
<br>
> + data = {'builders': self.builders,
<br>
> + 'timestamp': self.timestamp}
<br>
> + json.dump(data, f, indent=2)
<br>
> +
<br>
> + def is_expired(self):
<br>
> + return time.time() > self.timestamp +
self.expiration_time
<br>
> +
<br>
> +BUILD_NAME_REGEX = re.compile(
<br>
> +
r"((apple-)?clang)-([0-9]+)(\.([0-9]+))?(\.([0-9]+))?"
<br>
> + r"-([A-Z][A-Za-z]+)(\.(.*))?")
<br>
> +
<br>
> +
<br>
> +class Build(object):
<br>
> + @staticmethod
<br>
> + def frombasename(str, url=None):
<br>
> +
<br>
> + str = os.path.basename(str)
<br>
> + revision = timestamp = build = None
<br>
> +
<br>
> + # Check if this is a BNI style build.
<br>
> + m = BUILD_NAME_REGEX.match(str)
<br>
> + if m:
<br>
> + name, _, major_str, _, minor_str, _, micro_str,
build, \
<br>
> + _, suffix = m.groups()
<br>
> + revision = [int(major_str)]
<br>
> + if minor_str:
<br>
> + revision.append(int(minor_str))
<br>
> + if micro_str:
<br>
> + revision.append(int(micro_str))
<br>
> + return Build(name, tuple(revision), None, build,
suffix)
<br>
> +
<br>
> + if '.' in str:
<br>
> + str, suffix = str.split('.', 1)
<br>
> + else:
<br>
> + suffix = None
<br>
> +
<br>
> + m = re.match(r'(.*)-b([0-9]+)', str)
<br>
> + if m:
<br>
> + str, build = m.groups()
<br>
> + build = int(build)
<br>
> +
<br>
> + m = re.match(r'(.*)-t([0-9-]{8,10}_[0-9-]{6,8})',
str)
<br>
> + if m:
<br>
> + str, timestamp = m.groups()
<br>
> +
<br>
> + m = re.match(r'(.*)-r([0-9]+)', str)
<br>
> + if m:
<br>
> + str, revision = m.groups()
<br>
> + revision = int(revision)
<br>
> +
<br>
> + return Build(str, revision, timestamp, build,
suffix, url)
<br>
> +
<br>
> + @staticmethod
<br>
> + def fromdata(data):
<br>
> + return Build(data['name'], data['revision'],
data['timestamp'],
<br>
> + data['build'], data['suffix'])
<br>
> +
<br>
> + def todata(self):
<br>
> + return {'name': self.name,
<br>
> + 'revision': self.revision,
<br>
> + 'timestamp': self.timestamp,
<br>
> + 'build': self.build,
<br>
> + 'suffix': self.suffix}
<br>
> +
<br>
> + def __init__(self, name, revision, timestamp,
build, suffix, url):
<br>
> + self.name = name
<br>
> + self.revision = revision
<br>
> + self.timestamp = timestamp
<br>
> + self.build = build
<br>
> + self.suffix = suffix
<br>
> + self.url = url
<br>
> +
<br>
> + def tobasename(self, include_suffix=True):
<br>
> + basename = self.name
<br>
> + if self.revision is not None:
<br>
> + if isinstance(self.revision, (tuple, list)):
<br>
> + basename += '-' + '.'.join(str(r) for r in
self.revision)
<br>
> + else:
<br>
> + assert isinstance(self.revision, int)
<br>
> + basename += '-r%d' % self.revision
<br>
> + if self.timestamp is not None:
<br>
> + basename += '-t%s' % self.timestamp
<br>
> + if self.build is not None:
<br>
> + if isinstance(self.build, str):
<br>
> + basename += '-' + self.build
<br>
> + else:
<br>
> + basename += '-b%d' % self.build
<br>
> + if include_suffix and self.suffix is not None:
<br>
> + basename += '.%s' % self.suffix
<br>
> + return basename
<br>
> +
<br>
> + def __repr__(self):
<br>
> + return "%s%r" % (self.__class__.__name__,
<br>
> + (self.name, self.revision, self.timestamp,
self.build,
<br>
> + self.suffix))
<br>
> +
<br>
> + def __cmp__(self, other):
<br>
> + return cmp((self.revision, self.timestamp,
<br>
> + self.build, self.suffix, self.name),
<br>
> + ((other.revision, other.timestamp,
<br>
> + other.build, other.suffix, other.name)))
<br>
> +
<br>
> +
<br>
> +def load_builder_map(reload=False):
<br>
> + """
<br>
> + load_builder_map() -> BuilderMap
<br>
> +
<br>
> + Load a map of builder names to the server url that
holds those artifacts.
<br>
> + """
<br>
> +
<br>
> + prefs = util.get_prefs()
<br>
> +
<br>
> + # Load load the builder map if present (and not
reloading)
<br>
> + data_path = os.path.join(prefs.path, "ci")
<br>
> + buildermap_path = os.path.join(data_path,
"build_map.json")
<br>
> + if not reload and os.path.exists(buildermap_path):
<br>
> + buildermap = BuilderMap.frompath(buildermap_path)
<br>
> +
<br>
> + # If the buildermap is not out-of-date, return it.
<br>
> + if not buildermap.is_expired():
<br>
> + return buildermap
<br>
> +
<br>
> + # Otherwise, we didn't have a buildermap or it is
out of date, compute it.
<br>
> + builders = {}
<br>
> + for build in gcs.fetch_builders():
<br>
> + builders[build] = build
<br>
> +
<br>
> + # Create the buildermap and save it.
<br>
> + buildermap = BuilderMap(builders, time.time())
<br>
> + if not os.path.exists(data_path):
<br>
> + shell.mkdir_p(data_path)
<br>
> + buildermap.topath(buildermap_path)
<br>
> +
<br>
> + return buildermap
<br>
> +
<br>
> +
<br>
> +def fetch_builders():
<br>
> + """
<br>
> + fetch_builders() -> [builder-name, ...]
<br>
> +
<br>
> + Get a list of available builders.
<br>
> + """
<br>
> +
<br>
> + # Handle only_use_cache setting.
<br>
> + prefs = util.get_prefs()
<br>
> + if prefs.getboolean("ci", "only_use_cache"):
<br>
> + cache_path = os.path.join(prefs.path, "ci",
"build_cache")
<br>
> + return sorted(os.listdir(cache_path))
<br>
> +
<br>
> + # Otherwise, fetch the builder map.
<br>
> + return sorted(load_builder_map().builders.keys())
<br>
> +
<br>
> +
<br>
> +def fetch_builds(name):
<br>
> + """
<br>
> + fetch_builds(name) -> [(path, revision, build),
...]
<br>
> +
<br>
> + Get a list of available builds for the named
builder.
<br>
> + """
<br>
> + # Handle only_use_cache setting.
<br>
> + prefs = util.get_prefs()
<br>
> + if prefs.getboolean("ci", "only_use_cache"):
<br>
> + cache_path = os.path.join(prefs.path, "ci",
"build_cache")
<br>
> + cache_build_path = os.path.join(cache_path, name)
<br>
> + items = os.listdir(cache_build_path)
<br>
> + assert False, "Unimplemented?" + str(items)
<br>
> + # Otherwise, load the builder map.
<br>
> + buildermap = load_builder_map()
<br>
> +
<br>
> + # If the builder isn't in the builder map, do a
forced load of the builder
<br>
> + # map.
<br>
> + if name not in buildermap.builders:
<br>
> + buildermap = load_builder_map(reload=True)
<br>
> +
<br>
> + # If the builder doesn't exist, report an error.
<br>
> + builder_artifacts = buildermap.builders.get(name)
<br>
> + if builder_artifacts is None:
<br>
> + fatal("unknown builder name: %r" % (name,))
<br>
> +
<br>
> + # Otherwise, load the builder list.
<br>
> + server_builds = gcs.fetch_builds(builder_artifacts)
<br>
> + builds = []
<br>
> + for path in server_builds['items']:
<br>
> + build = Build.frombasename(path['name'],
path['mediaLink'])
<br>
> +
<br>
> + # Ignore any links which don't at least have a
revision component.
<br>
> + if build.revision is not None:
<br>
> + builds.append(build)
<br>
> +
<br>
> + # If there were no builds, report an error.
<br>
> + if not builds:
<br>
> + fatal("builder %r may be misconfigured (no items)"
% (name,))
<br>
> +
<br>
> + # Sort the builds, to make sure we return them
ordered properly.
<br>
> + builds.sort()
<br>
> +
<br>
> + return builds
<br>
> +
<br>
> +
<br>
> +def fetch_build_to_path(builder, build, root_path,
builddir_path):
<br>
> + path = build.tobasename()
<br>
> +
<br>
> + # Check whether we are using a build cache and get
the cached build path if
<br>
> + # so.
<br>
> + prefs = util.get_prefs()
<br>
> + cache_build_path = None
<br>
> + if prefs.getboolean("ci", "cache_builds"):
<br>
> + cache_path = os.path.join(prefs.path, "ci",
"build_cache")
<br>
> + cache_build_path = os.path.join(cache_path,
builder, path)
<br>
> +
<br>
> + # Copy the build from the cache or download it.
<br>
> + if cache_build_path and
os.path.exists(cache_build_path):
<br>
> + shutil.copy(cache_build_path, root_path)
<br>
> + else:
<br>
> + # Load the builder map.
<br>
> + buildermap = load_builder_map()
<br>
> +
<br>
> + # If the builder isn't in the builder map, do a
forced reload of the
<br>
> + # builder map.
<br>
> + if builder not in buildermap.builders:
<br>
> + buildermap = load_builder_map(reload=True)
<br>
> +
<br>
> + # If the builder doesn't exist, report an error.
<br>
> + builder_artifacts =
buildermap.builders.get(builder)
<br>
> + if builder_artifacts is None:
<br>
> + fatal("unknown builder name: %r" % (builder,))
<br>
> +
<br>
> + # Otherwise create the build url.
<br>
> + gcs.get_compiler(build.url, root_path)
<br>
> +
<br>
> + # Copy the build into the cache, if enabled.
<br>
> + if cache_build_path is not None:
<br>
> + shell.mkdir_p(os.path.dirname(cache_build_path))
<br>
> + shutil.copy(root_path, cache_build_path)
<br>
> +
<br>
> + # Create the directory for the build.
<br>
> + os.mkdir(builddir_path)
<br>
> +
<br>
> + # Extract the build.
<br>
> + if shell.execute(['tar', '-xf', root_path, '-C',
builddir_path]):
<br>
> + fatal('unable to extract %r to %r' % (root_path,
builddir_path))
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/scripts.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/scripts.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/scripts.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/scripts.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/scripts.py Thu Oct
8 16:52:50 2015
<br>
> @@ -0,0 +1,60 @@
<br>
> +"""
<br>
> +Utilities for building the llvmlab multi-tool.
<br>
> +"""
<br>
> +
<br>
> +import os
<br>
> +import sys
<br>
> +
<br>
> +
<br>
> +class Tool(object):
<br>
> + """
<br>
> + This object defines a generic command line tool
instance, which dynamically
<br>
> + builds its commands from a module dictionary.
<br>
> +
<br>
> + Example usage::
<br>
> +
<br>
> + import scripts
<br>
> +
<br>
> + def action_foo(name, args):
<br>
> + "the foo command"
<br>
> +
<br>
> + ...
<br>
> +
<br>
> + tool = scripts.Tool(locals())
<br>
> + if __name__ == '__main__':
<br>
> + tool.main(sys.argv)
<br>
> +
<br>
> + Any function beginning with "action_" is considered
a tool command. It's
<br>
> + name is defined by the function name suffix.
Underscores in the function
<br>
> + name are converted to '-' in the command line
syntax. Actions ending ith
<br>
> + "-debug" are not listed in the help.
<br>
> + """
<br>
> +
<br>
> + def __init__(self, locals):
<br>
> + # Create the list of commands.
<br>
> + self.commands = dict((name[7:].replace('_', '-'),
f)
<br>
> + for name, f in locals.items()
<br>
> + if name.startswith('action_'))
<br>
> +
<br>
> + def usage(self, name):
<br>
> + print >>sys.stderr, "Usage: %s command
[options]" % (
<br>
> + os.path.basename(name))
<br>
> + print >>sys.stderr
<br>
> + print >>sys.stderr, "Available commands:"
<br>
> + cmds_width = max(map(len, self.commands))
<br>
> + for name, func in sorted(self.commands.items()):
<br>
> + if name.endswith("-debug"):
<br>
> + continue
<br>
> +
<br>
> + print >>sys.stderr, " %-*s - %s" %
(cmds_width, name,
<br>
> + func.__doc__)
<br>
> + sys.exit(1)
<br>
> +
<br>
> + def main(self, args):
<br>
> + if len(args) < 2 or args[1] not in
self.commands:
<br>
> + if len(args) >= 2:
<br>
> + print >>sys.stderr, "error: invalid command
%r\n" % args[1]
<br>
> + self.usage(args[0])
<br>
> +
<br>
> + cmd = args[1]
<br>
> + return self.commands[cmd]('%s %s' % (args[0], cmd),
args[2:])
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/shell.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/shell.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/shell.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/shell.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/shell.py Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,44 @@
<br>
> +"""
<br>
> +shell like utilities
<br>
> +"""
<br>
> +
<br>
> +import os
<br>
> +
<br>
> +
<br>
> +def execute(args):
<br>
> + import subprocess
<br>
> + """execute(command) - Run the given command (or
argv list) in a shell and
<br>
> + return the exit code."""
<br>
> + return subprocess.Popen(args).wait()
<br>
> +
<br>
> +
<br>
> +def capture(args, include_stderr=False):
<br>
> + import subprocess
<br>
> + """capture(command) - Run the given command (or
argv list) in a shell and
<br>
> + return the standard output."""
<br>
> + stderr = subprocess.PIPE
<br>
> + if include_stderr:
<br>
> + stderr = subprocess.STDOUT
<br>
> + p = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=stderr)
<br>
> + out, _ = p.communicate()
<br>
> + return p.wait(), out
<br>
> +
<br>
> +
<br>
> +def mkdir_p(path):
<br>
> + """mkdir_p(path) - Make the "path" directory, if it
does not exist; this
<br>
> + will also make directories for any missing parent
directories."""
<br>
> + import errno
<br>
> +
<br>
> + if not path or os.path.exists(path):
<br>
> + return
<br>
> +
<br>
> + parent = os.path.dirname(path)
<br>
> + if parent != path:
<br>
> + mkdir_p(parent)
<br>
> +
<br>
> + try:
<br>
> + os.mkdir(path)
<br>
> + except OSError as e:
<br>
> + # Ignore EEXIST, which may occur during a race
condition.
<br>
> + if e.errno != errno.EEXIST:
<br>
> + raise
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py
(added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/test_llvmlab.py Thu
Oct 8 16:52:50 2015
<br>
> @@ -0,0 +1,50 @@
<br>
> +# RUN: pythong test_llvmlab.py
<br>
> +import unittest
<br>
> +
<br>
> +import os
<br>
> +import shutil
<br>
> +import tempfile
<br>
> +from . import ci
<br>
> +
<br>
> +class TestLLVMLabCI(unittest.TestCase):
<br>
> +
<br>
> + def setUp(self):
<br>
> + self.workdir = tempfile.mkdtemp()
<br>
> + print self.workdir
<br>
> + os.chdir(self.workdir)
<br>
> +
<br>
> + def tearDown(self):
<br>
> + shutil.rmtree(self.workdir)
<br>
> +
<br>
> + def test_bisect(self):
<br>
> + ci.action_bisect("llvmlab", ["--min-rev", "219719",
<br>
> + "--max-rev", "219899",
<br>
> + "bash", "-c",
<br>
> + "%(path)s/bin/clang -v | grep b700"])
<br>
> +
<br>
> + def test_ls(self):
<br>
> + """Check that you can """
<br>
> + builds = ci.action_ls("llvmlab", [])
<br>
> + self.assertIn("clang-stage1-configure-RA_build",
builds)
<br>
> + compilers = ci.action_ls("llvmlab",
<br>
> + ["clang-stage1-configure-RA_build"])
<br>
> + compiler_revs = [x.revision for x in compilers]
<br>
> + self.assertIn(219899, compiler_revs)
<br>
> +
<br>
> + def test_fetch_noargs(self):
<br>
> + """ """
<br>
> + path = ci.action_fetch("llvmlab",
["clang-stage1-configure-RA_build"])
<br>
> + self.assertTrue(os.path.isdir(path), "Fetch did not
get a compiler?")
<br>
> +
<br>
> + def test_fetch_arg(self):
<br>
> + """ """
<br>
> + path = ci.action_fetch("llvmlab",
<br>
> + ["--update-link", "clang_link",
<br>
> + "clang-stage1-configure-RA_build",
<br>
> + "clang-r219899-t2014-10-15_20-42-53-b808"])
<br>
> + self.assertTrue(os.path.isdir(path), "Fetch did not
get a compiler?")
<br>
> +
<br>
> +
<br>
> +def run_tests():
<br>
> + suite =
unittest.TestLoader().loadTestsFromTestCase(TestLLVMLabCI)
<br>
> + unittest.TextTestRunner(verbosity=2).run(suite)
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/llvmlab/util.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/util.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/llvmlab/util.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/llvmlab/util.py (added)
<br>
> +++ zorg/trunk/llvmbisect/llvmlab/util.py Thu Oct 8
16:52:50 2015
<br>
> @@ -0,0 +1,286 @@
<br>
> +import ConfigParser
<br>
> +import datetime
<br>
> +import inspect
<br>
> +import os
<br>
> +import sys
<br>
> +import traceback
<br>
> +
<br>
> +__all__ = []
<br>
> +
<br>
> +def _write_message(kind, message):
<br>
> + # Get the file/line where this message was
generated.
<br>
> + f = inspect.currentframe()
<br>
> + # Step out of _write_message, and then out of
wrapper.
<br>
> + f = f.f_back.f_back
<br>
> + file,line,_,_,_ = inspect.getframeinfo(f)
<br>
> + location = '%s:%d' % (os.path.basename(file), line)
<br>
> +
<br>
> + print >>sys.stderr, '%s: %s: %s' % (location,
kind, message)
<br>
> +
<br>
> +note = lambda message: _write_message('note',
message)
<br>
> +warning = lambda message: _write_message('warning',
message)
<br>
> +error = lambda message: _write_message('error',
message)
<br>
> +fatal = lambda message: (_write_message('fatal
error', message), sys.exit(1))
<br>
> +
<br>
> +
<br>
> +def sorted(l, **kwargs):
<br>
> + l = list(l)
<br>
> + l.sort(**kwargs)
<br>
> + return l
<br>
> +
<br>
> +def list_split(list, item):
<br>
> + parts = []
<br>
> + while item in list:
<br>
> + index = list.index(item)
<br>
> + parts.append(list[:index])
<br>
> + list = list[index+1:]
<br>
> + parts.append(list)
<br>
> + return parts
<br>
> +
<br>
> +def pairs(l):
<br>
> + return zip(l, l[1:])
<br>
> +
<br>
> +###
<br>
> +
<br>
> +class EnumVal(object):
<br>
> + def __init__(self, enum, name, value):
<br>
> + self.enum = enum
<br>
> + self.name = name
<br>
> + self.value = value
<br>
> +
<br>
> + def __repr__(self):
<br>
> + return '%s.%s' % (self.enum._name, self.name)
<br>
> +
<br>
> +class Enum(object):
<br>
> + def __init__(self, name, **kwargs):
<br>
> + self._name = name
<br>
> + self.__items = dict((name, EnumVal(self, name,
value))
<br>
> + for name,value in kwargs.items())
<br>
> + self.__reverse_map = dict((e.value,e.name)
<br>
> + for e in self.__items.values())
<br>
> + self.__dict__.update(self.__items)
<br>
> +
<br>
> + def get_value(self, name):
<br>
> + return self.__items.get(name)
<br>
> +
<br>
> + def get_name(self, value):
<br>
> + return self.__reverse_map.get(value)
<br>
> +
<br>
> + def get_by_value(self, value):
<br>
> + return
self.__items.get(self.__reverse_map.get(value))
<br>
> +
<br>
> + def contains(self, item):
<br>
> + if not isinstance(item, EnumVal):
<br>
> + return False
<br>
> + return item.enum == self
<br>
> +
<br>
> +class multidict:
<br>
> + def __init__(self, elts=()):
<br>
> + self.data = {}
<br>
> + for key,value in elts:
<br>
> + self[key] = value
<br>
> +
<br>
> + def __contains__(self, item):
<br>
> + return item in self.data
<br>
> + def __getitem__(self, item):
<br>
> + return self.data[item]
<br>
> + def __setitem__(self, key, value):
<br>
> + if key in self.data:
<br>
> + self.data[key].append(value)
<br>
> + else:
<br>
> + self.data[key] = [value]
<br>
> + def items(self):
<br>
> + return self.data.items()
<br>
> + def values(self):
<br>
> + return self.data.values()
<br>
> + def keys(self):
<br>
> + return self.data.keys()
<br>
> + def __len__(self):
<br>
> + return len(self.data)
<br>
> + def get(self, key, default=None):
<br>
> + return self.data.get(key, default)
<br>
> + def todict(self):
<br>
> + return self.data.copy()
<br>
> +
<br>
> +###
<br>
> +
<br>
> +class Preferences(object):
<br>
> + def __init__(self, path):
<br>
> + self.path = path
<br>
> + self.config_path = os.path.join(path, "config")
<br>
> + self.options = ConfigParser.RawConfigParser()
<br>
> +
<br>
> + # Load the config file, if present.
<br>
> + if os.path.exists(self.config_path):
<br>
> + self.options.read(self.config_path)
<br>
> +
<br>
> + def save(self):
<br>
> + file = open(self.config_path, "w")
<br>
> + try:
<br>
> + self.options.write(file)
<br>
> + finally:
<br>
> + file.close()
<br>
> +
<br>
> + def get(self, section, option, default = None):
<br>
> + if self.options.has_option(section, option):
<br>
> + return self.options.get(section, option)
<br>
> + else:
<br>
> + return default
<br>
> +
<br>
> + def getboolean(self, section, option, default =
None):
<br>
> + if self.options.has_option(section, option):
<br>
> + return self.options.getboolean(section, option)
<br>
> + else:
<br>
> + return default
<br>
> +
<br>
> + def setboolean(self, section, option, value):
<br>
> + return self.options.set(section, option,
str(value))
<br>
> +
<br>
> +_prefs = None
<br>
> +def get_prefs():
<br>
> + global _prefs
<br>
> + if _prefs is None:
<br>
> + _prefs =
Preferences(os.path.expanduser("~/.llvmlab"))
<br>
> +
<br>
> + # Allow dynamic override of only_use_cache option.
<br>
> + if os.environ.get("LLVMLAB_ONLY_USE_CACHE"):
<br>
> + _prefs.setboolean("ci", "only_use_cache", True)
<br>
> +
<br>
> + return _prefs
<br>
> +
<br>
> +###
<br>
> +
<br>
> +import threading
<br>
> +import Queue
<br>
> +
<br>
> +def detect_num_cpus():
<br>
> + """
<br>
> + Detects the number of CPUs on a system. Cribbed
from pp.
<br>
> + """
<br>
> + # Linux, Unix and MacOS:
<br>
> + if hasattr(os, "sysconf"):
<br>
> + if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
<br>
> + # Linux & Unix:
<br>
> + ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
<br>
> + if isinstance(ncpus, int) and ncpus > 0:
<br>
> + return ncpus
<br>
> + else: # OSX:
<br>
> + return int(os.popen2("sysctl -n
hw.ncpu")[1].read())
<br>
> + # Windows:
<br>
> + if os.environ.has_key("NUMBER_OF_PROCESSORS"):
<br>
> + ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
<br>
> + if ncpus > 0:
<br>
> + return ncpus
<br>
> + return 1 # Default
<br>
> +
<br>
> +def execute_task_on_threads(fn, iterable,
num_threads = None):
<br>
> + """execute_task_on_threads(fn, iterable) ->
iterable
<br>
> +
<br>
> + Given a task function to run on an iterable list of
work items, execute the
<br>
> + task on each item in the list using some number of
threads, and yield the
<br>
> + results of the task function.
<br>
> +
<br>
> + If a task function throws an exception, the
exception will be
<br>
> + printed but not returned to the caller. Clients
which wish to
<br>
> + control exceptions should handle them inside the
task function.
<br>
> + """
<br>
> + def push_work():
<br>
> + for item in iterable:
<br>
> + work_queue.put(item)
<br>
> +
<br>
> + # Push sentinels to cause workers to terminate.
<br>
> + for i in range(num_threads):
<br>
> + work_queue.put(_sentinel)
<br>
> + def do_work():
<br>
> + while True:
<br>
> + # Read a work item.
<br>
> + item = work_queue.get()
<br>
> +
<br>
> + # If we hit a sentinel, propogate it to the output
queue and
<br>
> + # terminate.
<br>
> + if item is _sentinel:
<br>
> + output_queue.put(_sentinel)
<br>
> + break
<br>
> +
<br>
> + # Otherwise, execute the task and push to the
output queue.
<br>
> + try:
<br>
> + output = (None, fn(item))
<br>
> + except Exception, e:
<br>
> + output = ('error', sys.exc_info())
<br>
> +
<br>
> + output_queue.put(output)
<br>
> +
<br>
> + # Compute the number of threads to use.
<br>
> + if num_threads is None:
<br>
> + num_threads = detect_num_cpus()
<br>
> +
<br>
> + # Create two queues, one for feeding items to the
works and another for
<br>
> + # consuming the output.
<br>
> + work_queue = Queue.Queue()
<br>
> + output_queue = Queue.Queue()
<br>
> +
<br>
> + # Create our unique sentinel object.
<br>
> + _sentinel = []
<br>
> +
<br>
> + # Create and run thread to push items onto the work
queue.
<br>
> + threading.Thread(target=push_work).start()
<br>
> +
<br>
> + # Create and run the worker threads.
<br>
> + for i in range(num_threads):
<br>
> + t = threading.Thread(target=do_work)
<br>
> + t.daemon = True
<br>
> + t.start()
<br>
> +
<br>
> + # Read items from the output queue until all
threads are finished.
<br>
> + finished = 0
<br>
> + while finished != num_threads:
<br>
> + item = output_queue.get()
<br>
> +
<br>
> + # Check for termination marker.
<br>
> + if item is _sentinel:
<br>
> + finished += 1
<br>
> + continue
<br>
> +
<br>
> + # Check for exceptions.
<br>
> + if item[0] == 'error':
<br>
> + _,(t,v,tb) = item
<br>
> + traceback.print_exception(t, v, tb)
<br>
> + continue
<br>
> +
<br>
> + assert item[0] is None
<br>
> + yield item[1]
<br>
> +
<br>
> +def timestamp():
<br>
> + return
datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
<br>
> +
<br>
> +###
<br>
> +
<br>
> +import collections
<br>
> +
<br>
> +class orderedset(object):
<br>
> + def __init__(self, items=None):
<br>
> + self.base = collections.OrderedDict()
<br>
> + if items is not None:
<br>
> + self.update(items)
<br>
> +
<br>
> + def update(self, items):
<br>
> + for item in items:
<br>
> + self.add(item)
<br>
> +
<br>
> + def add(self, item):
<br>
> + self.base[item] = None
<br>
> +
<br>
> + def remove(self, item):
<br>
> + del self.base[item]
<br>
> +
<br>
> + def __nonzero__(self):
<br>
> + return bool(self.base)
<br>
> +
<br>
> + def __len__(self):
<br>
> + return len(self.base)
<br>
> +
<br>
> + def __iter__(self):
<br>
> + return iter(self.base)
<br>
> +
<br>
> + def __contains__(self, item):
<br>
> + return item in self.base
<br>
>
<br>
> Added: zorg/trunk/llvmbisect/setup.py
<br>
> URL:
<a class="moz-txt-link-freetext" href="http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/setup.py?rev=249757&view=auto">http://llvm.org/viewvc/llvm-project/zorg/trunk/llvmbisect/setup.py?rev=249757&view=auto</a><br>
>
==============================================================================<br>
> --- zorg/trunk/llvmbisect/setup.py (added)
<br>
> +++ zorg/trunk/llvmbisect/setup.py Thu Oct 8 16:52:50
2015
<br>
> @@ -0,0 +1,41 @@
<br>
> +import os
<br>
> +
<br>
> +from setuptools import setup, find_packages
<br>
> +
<br>
> +# setuptools expects to be invoked from within the
directory of setup.py, but it
<br>
> +# is nice to allow:
<br>
> +# python path/to/setup.py install
<br>
> +# to work (for scripts, etc.)
<br>
> +os.chdir(os.path.dirname(os.path.abspath(__file__)))
<br>
> +
<br>
> +setup(
<br>
> + name = "llvmbisect",
<br>
> + version = "1.0",
<br>
> +
<br>
> + author = "Daniel Dunbar and Chris Matthews",
<br>
> + author_email = <a class="moz-txt-link-rfc2396E" href="mailto:chris.matthews@apple.com">"chris.matthews@apple.com"</a>,
<br>
> + url = '<a class="moz-txt-link-freetext" href="http://lab.llvm.org">http://lab.llvm.org</a>',
<br>
> + license = 'BSD',
<br>
> +
<br>
> + description = "Compiler bisection service.",
<br>
> + keywords = 'testing compiler performance
development llvm',
<br>
> +
<br>
> + classifiers=[
<br>
> + 'Development Status :: 4 - Beta',
<br>
> + 'Environment :: Console',
<br>
> + 'Intended Audience :: Developers',
<br>
> + ('License :: OSI Approved :: '
<br>
> + 'University of Illinois/NCSA Open Source License'),
<br>
> + 'Natural Language :: English',
<br>
> + 'Operating System :: OS Independent',
<br>
> + 'Progamming Language :: Python',
<br>
> + 'Topic :: Software Development :: Quality
Assurance',
<br>
> + 'Topic :: Software Development :: Testing',
<br>
> + ],
<br>
> +
<br>
> + packages = find_packages(),
<br>
> +
<br>
> + scripts = ['bin/llvmlab'],
<br>
> +
<br>
> + install_requires=['requests'],
<br>
> +)
<br>
>
<br>
>
<br>
> _______________________________________________
<br>
> llvm-commits mailing list
<br>
> <a class="moz-txt-link-abbreviated" href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>
<br>
>
<a class="moz-txt-link-freetext" href="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a>
<br>
<br>
<br>
</div>
</div>
</span></blockquote>
</blockquote>
<p><br>
</p>
</body>
</html>