<html><head><style>body{font-family:Helvetica,Arial;font-size:13px}</style></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;">The tool has an open door policy - anyone with a bot can upload their builds if they would like.  So you just need to find a volunteer!</div> <div id="bloop_sign_1474330247242289152" class="bloop_sign"><br></div><p class="airmail_on">On September 19, 2016 at 5:10:11 PM, Vassil Vassilev (<a href="mailto:v.g.vassilev@gmail.com">v.g.vassilev@gmail.com</a>) wrote:</p> <blockquote type="cite" class="clean_bq"><span><div bgcolor="#FFFFFF" text="#000000"><div></div><div>



<title></title>


<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">

<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.

<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">
<div>
<div><span>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></span></div>
</div>
</blockquote>
</blockquote>
<p><br></p>


</div></div></span></blockquote></body></html>